DateTime.TryParse century control C# - c#

The result of the following snippet is "12/06/1930 12:00:00". How do I control the implied century so that "12 Jun 30" becomes 2030 instead?
string dateString = "12 Jun 30"; //from user input
DateTime result;
DateTime.TryParse(dateString, new System.Globalization.CultureInfo("en-GB"),System.Globalization.DateTimeStyles.None,out result);
Console.WriteLine(result.ToString());
Please set aside, for the moment, the fact that a correct solution is to specify the date correctly in the first place.
Note: The result is independant of the system datetime for the pc running the code.
Answer: Thanks Deeksy
for (int i = 0; i <= 9; i++)
{
string dateString = "12 Jun " + ((int)i * 10).ToString();
Console.WriteLine("Parsing " + dateString);
DateTime result;
System.Globalization.CultureInfo cultureInfo = new System.Globalization.CultureInfo("en-GB");
cultureInfo.Calendar.TwoDigitYearMax = 2099;
DateTime.TryParse(dateString, cultureInfo , System.Globalization.DateTimeStyles.None, out result);
Console.WriteLine(result.ToString());
}

It's tricky, because the way two digit years work with TryParse is based on the TwoDigitYearMax property of the Calendar property of the CultureInfo object that you are using. (CultureInfo->Calendar->TwoDigitYearMax)
In order to make two digit years have 20 prepended, you'll need to manually create a CultureInfo object which has a Calendar object with 2099 set as the TwoDigitYearMax property. Unfortunately, this means that any two digit date parsed will have 20 prepended (including 98, 99 etc.) which is probably not what you want.
I suspect that your best option is to use a 3rd party date parsing library instead of the standard tryparse that will use the +50/-50 year rule for 2 digit years. (that a 2 digit year should be translated into a range between 50 years before this year and 50 years greater than this year).
Alternatively, you could override the ToFourDigitYear method on the calendar object (it's virtual) and use that to implement the -50/+50 rule.

I'd write a re-usable function:
public static object ConvertCustomDate(string input)
{
//Create a new culture based on our current one but override the two
//digit year max.
CultureInfo ci = new CultureInfo(CultureInfo.CurrentCulture.LCID);
ci.Calendar.TwoDigitYearMax = 2099;
//Parse the date using our custom culture.
DateTime dt = DateTime.ParseExact(input, "MMM-yy", ci);
return new { Month=dt.ToString("MMMM"), Year=dt.ToString("yyyy") };
}
Here's my list of quasi-date strings
List<string> dates = new List<string>(new []{
"May-10",
"Jun-30",
"Jul-10",
"Apr-08",
"Mar-07"
});
Scan over it like so:
foreach(object obj in dates.Select(d => ConvertCustomDate(d)))
{
Console.WriteLine(obj);
}
Notice that it handles 30 as 2030 now instead of 1930...

You're looking for the Calendar.TwoDigitYearMax Property.
Jon Skeet has posted something on this you will likely find useful.

I had a similar problem and I solved it with a Regex. In your case it would look like that:
private static readonly Regex DateRegex = new Regex(
#"^[0-9][0-9] (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9][0-9]$",
RegexOptions.Compiled | RegexOptions.ExplicitCapture);
private static string Beautify(string date)
{
var match = DateRegex.Match(date);
if (match.Success)
{
// Maybe further checks for correct day
return date.Insert("dd-MMM-".Length, "20");
}
return date;
}

Congratulations, you have a Y2K bug.
The documentation for this behavior starts with the Custom Date and Time Format strings, which includes a description of the yy format specifier. Specifically, we have this excerpt:
In a parsing operation, a two-digit year that is parsed using the "yy" custom format specifier is interpreted based on the Calendar.TwoDigitYearMax property of the format provider's current calendar.
Follow that to the Calendar.TwoDigitYearMax documentation and we find this:
This property allows a 2-digit year to be properly translated to a 4-digit year. For example, if this property is set to 2029, the 100-year range is from 1930 to 2029. Therefore, a 2-digit value of 30 is interpreted as 1930, while a 2-digit value of 29 is interpreted as 2029.
The initial value of this property is derived from the settings in the regional and language options portion of Control Panel.
If you only have two digits for the year you're gonna need to guess at the tipping point between the current and previous or current and next centuries. Microsoft made their guess, but also chose to make it configurable, where different systems may have it configured different ways. This implies it's dangerous to rely on two-digit year values, as we've known since before 1999. Since 1999, no one sane uses two digits for the year anymore.
As a side note, it's been some time since this was first decided; it's probably past time for Microsoft to update that default guess (maybe 2079, or a new approach entirely, perhaps based on an offset from the current year). Unfortunately, it's a statistical certainty there are programs out there which rely on the default not changing, such that it's difficult for Microsoft to update this. It would cause what they call a "breaking change", and they are pretty good about avoiding doing that to people... though there is some discussion on changing this for .Net 8.
This situation is therefore likely to start coming up more often in the near future, and having found my way here because it indeed had come up in another place I felt it worthwhile to add a more-recent answer to this older question. There's nothing really new in this answer, except to confirm the situation hasn't (yet) changed. Maybe a Windows 11 release will have a new default?

result = year.ToString().Length == 1
|| year.ToString().Length == 2 ? "1"
: (Convert.ToInt32(year.ToString()
.Substring(0, (year.ToString().Length - 2))) + 1).ToString();

Related

Differences of datetime between windows and linux [duplicate]

The result of the following snippet is "12/06/1930 12:00:00". How do I control the implied century so that "12 Jun 30" becomes 2030 instead?
string dateString = "12 Jun 30"; //from user input
DateTime result;
DateTime.TryParse(dateString, new System.Globalization.CultureInfo("en-GB"),System.Globalization.DateTimeStyles.None,out result);
Console.WriteLine(result.ToString());
Please set aside, for the moment, the fact that a correct solution is to specify the date correctly in the first place.
Note: The result is independant of the system datetime for the pc running the code.
Answer: Thanks Deeksy
for (int i = 0; i <= 9; i++)
{
string dateString = "12 Jun " + ((int)i * 10).ToString();
Console.WriteLine("Parsing " + dateString);
DateTime result;
System.Globalization.CultureInfo cultureInfo = new System.Globalization.CultureInfo("en-GB");
cultureInfo.Calendar.TwoDigitYearMax = 2099;
DateTime.TryParse(dateString, cultureInfo , System.Globalization.DateTimeStyles.None, out result);
Console.WriteLine(result.ToString());
}
It's tricky, because the way two digit years work with TryParse is based on the TwoDigitYearMax property of the Calendar property of the CultureInfo object that you are using. (CultureInfo->Calendar->TwoDigitYearMax)
In order to make two digit years have 20 prepended, you'll need to manually create a CultureInfo object which has a Calendar object with 2099 set as the TwoDigitYearMax property. Unfortunately, this means that any two digit date parsed will have 20 prepended (including 98, 99 etc.) which is probably not what you want.
I suspect that your best option is to use a 3rd party date parsing library instead of the standard tryparse that will use the +50/-50 year rule for 2 digit years. (that a 2 digit year should be translated into a range between 50 years before this year and 50 years greater than this year).
Alternatively, you could override the ToFourDigitYear method on the calendar object (it's virtual) and use that to implement the -50/+50 rule.
I'd write a re-usable function:
public static object ConvertCustomDate(string input)
{
//Create a new culture based on our current one but override the two
//digit year max.
CultureInfo ci = new CultureInfo(CultureInfo.CurrentCulture.LCID);
ci.Calendar.TwoDigitYearMax = 2099;
//Parse the date using our custom culture.
DateTime dt = DateTime.ParseExact(input, "MMM-yy", ci);
return new { Month=dt.ToString("MMMM"), Year=dt.ToString("yyyy") };
}
Here's my list of quasi-date strings
List<string> dates = new List<string>(new []{
"May-10",
"Jun-30",
"Jul-10",
"Apr-08",
"Mar-07"
});
Scan over it like so:
foreach(object obj in dates.Select(d => ConvertCustomDate(d)))
{
Console.WriteLine(obj);
}
Notice that it handles 30 as 2030 now instead of 1930...
You're looking for the Calendar.TwoDigitYearMax Property.
Jon Skeet has posted something on this you will likely find useful.
I had a similar problem and I solved it with a Regex. In your case it would look like that:
private static readonly Regex DateRegex = new Regex(
#"^[0-9][0-9] (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9][0-9]$",
RegexOptions.Compiled | RegexOptions.ExplicitCapture);
private static string Beautify(string date)
{
var match = DateRegex.Match(date);
if (match.Success)
{
// Maybe further checks for correct day
return date.Insert("dd-MMM-".Length, "20");
}
return date;
}
Congratulations, you have a Y2K bug.
The documentation for this behavior starts with the Custom Date and Time Format strings, which includes a description of the yy format specifier. Specifically, we have this excerpt:
In a parsing operation, a two-digit year that is parsed using the "yy" custom format specifier is interpreted based on the Calendar.TwoDigitYearMax property of the format provider's current calendar.
Follow that to the Calendar.TwoDigitYearMax documentation and we find this:
This property allows a 2-digit year to be properly translated to a 4-digit year. For example, if this property is set to 2029, the 100-year range is from 1930 to 2029. Therefore, a 2-digit value of 30 is interpreted as 1930, while a 2-digit value of 29 is interpreted as 2029.
The initial value of this property is derived from the settings in the regional and language options portion of Control Panel.
If you only have two digits for the year you're gonna need to guess at the tipping point between the current and previous or current and next centuries. Microsoft made their guess, but also chose to make it configurable, where different systems may have it configured different ways. This implies it's dangerous to rely on two-digit year values, as we've known since before 1999. Since 1999, no one sane uses two digits for the year anymore.
As a side note, it's been some time since this was first decided; it's probably past time for Microsoft to update that default guess (maybe 2079, or a new approach entirely, perhaps based on an offset from the current year). Unfortunately, it's a statistical certainty there are programs out there which rely on the default not changing, such that it's difficult for Microsoft to update this. It would cause what they call a "breaking change", and they are pretty good about avoiding doing that to people... though there is some discussion on changing this for .Net 8.
This situation is therefore likely to start coming up more often in the near future, and having found my way here because it indeed had come up in another place I felt it worthwhile to add a more-recent answer to this older question. There's nothing really new in this answer, except to confirm the situation hasn't (yet) changed. Maybe a Windows 11 release will have a new default?
result = year.ToString().Length == 1
|| year.ToString().Length == 2 ? "1"
: (Convert.ToInt32(year.ToString()
.Substring(0, (year.ToString().Length - 2))) + 1).ToString();

Same code, different behaviors with different .NET framework versions [duplicate]

According to Wikipedia (and confirmed in an answer by Dario Solera), in Italy they format times using colons:
The 24-hour notation is used in writing with a colon as a separator.
Example: 14:05. The minutes are written with two digits; the hour
numbers can be written with or without leading zero.
However, running the following code seems to output dots:
using System.Globalization;
Thread.CurrentThread.CurrentCulture = new CultureInfo("it-IT");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("it-IT");
// Outputs "11.08"
Console.WriteLine(DateTime.Now.ToShortTimeString());
// Outputs "."
Console.WriteLine(new CultureInfo("it-IT").DateTimeFormat.TimeSeparator);
Is this a framework bug? What's the best way to "fix" it? TimeSeparator is settable - should we just overwrite it before assigning to Thread.CurrentThread.CurrentCulture etc.?
I can guarantee in Italy we use colons to separate hour and minute digits, and we use the 24-hour format. Wikipedia is correct (at least this time).
Your problem is likely that you're not setting the Thread's UI culture. Something like this should work:
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("it-IT");
This seems to be a .NET 3.5 issue. In .NET 4.0 the code you posted uses a colon as expected. Seems like a strange breaking change between the framework versions, but seems like upgrading to .NET 4 will solve the problem.
The hours/minutes separator (TimeSeparator) in Italy seems to be a ., not a :.
You are specifically formatting for the Italian culture, so it follows that this is what will be used.
In a DateTime format string, the : is a place holder for this separator - if the culture defines . or , or anything else as the separator, that's what will be substituted when formatting the DateTime with that culture.
Following from the conversation under Oded's answer, this is probably what you should be using:
var culture = CultureInfo.GetCultureInfo("it-IT");
var stringValue = new TimeSpan(100, 100, 100, 100, 100).ToString(null, culture);
var timespan = TimeSpan.Parse(stringValue, culture);
// Another example
var culture = CultureInfo.GetCultureInfo("it-IT");
var stringValue = DateTime.Now.ToString(null, culture);
var dateTime = DateTime.Parse(stringValue, culture);

How can I parse an Arabic Umm Al-Qura date string into a .NET DateTime object?

I have the following Arabic date in the Umm Al-Qura calendar that I want to parse into a .NET DateTime object:
الأربعاء‏، 17‏ ذو الحجة‏، 1436
This date is equivalent to September 30th 2015 in the Gregorian calendar.
I've been trying the following "standard" C# code to parse this date, but without success:
var cultureInfo = new CultureInfo("ar-SA");
cultureInfo.DateTimeFormat.Calendar = new UmAlQuraCalendar(); // the default one anyway
var dateFormat = "dddd، dd MMMM، yyyy"; //note the ، instead of ,
var dateString = "‏الأربعاء‏، 17‏ ذو الحجة‏، 1436";
DateTime date;
DateTime.TryParseExact(dateString, dateFormat, cultureInfo.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, out date);
No matter what I do, the result of TryParseExact is always false. How do I parse this string properly in .NET?
By the way, if I start from a DateTime object, I can create the exact date string above using ToString()'s overloads on DateTime without problems. I just can't do it the other way around apparently.
Your datestring is 30 characters long and contains four UNICODE 8207 U+200F RIGHT TO LEFT MARK characters, but your dateformat does not.
// This gives a string 26 characters long
var str = new DateTime(2015,9,30).ToString(dateFormat, cultureInfo.DateTimeFormat)
RIGHT TO LEFT MARK is not whitespace.
If it only contains RLM/LRM/ALM you should probably just strip them out. Same with the isolates LRI/RLI/FSI and PDI sets, and LRE/RLE sets. You may not want to do that with LRO though. LRO is often used with legacy data where the RTL characters are stored in the opposite order, i.e. in the left-to-right order. In these cases you may want to actually reverse the characters.
Parsing dates from random places is a hard problem. You need a layered solution, try first one method, then another in priority order until you succeed. There is no 100% solution though, because people can type what they like.
See here for more information: http://www.unicode.org/reports/tr9/
This is a Right-To-Left culture, which means that the year will be rendered first. For example, the following code:
var cultureInfo = new CultureInfo("ar-SA");
cultureInfo.DateTimeFormat.Calendar = new UmAlQuraCalendar();
Console.WriteLine(String.Format(cultureInfo,"{0:dddd، dd MMMM، yyyy}",DateTime.Now));
produces الأربعاء، 17 ذو الحجة، 1436. Parsing this string works without problem:
var dateString="الأربعاء، 17 ذو الحجة، 1436";
var result=DateTime.TryParseExact(dateString, dateFormat, cultureInfo.DateTimeFormat,
DateTimeStyles.AllowWhiteSpaces,out date);
Debug.Assert(result);
PS: I don't know how to write the format string to parse the original input, as changing the position of what looks like a comma to me, changes the actual characters rendered in the string.

Compare DateTime by Javascript, Jquery

I have a function which convert date to my formatdate and compare it to CurrentDate.
It worked well when I test it with date is a date in Octorber compare to today (16/10/2013)
But if date is a date in November, the value return is true which means date < currentdate. I dont know why, anyone can explain it to me ? Thanks so much.
function IsPast(date) {
var fullDate = new Date();
var twoDigitMonth = fullDate.getMonth() + 1 + "";
if (twoDigitMonth.length == 1) twoDigitMonth = "0" + twoDigitMonth;
var twoDigitDate = fullDate.getDate() + "";
if (twoDigitDate.length == 1) twoDigitDate = "0" + twoDigitDate;
var currentDate = twoDigitDate + "/" + twoDigitMonth + "/" + fullDate.getFullYear();
var startDate = $.fullCalendar.formatDate(date, 'dd/MM/yyyy');
if (startDate <= currentDate) {return true;}
return false;
}
If you are being passed a Date object (and that seems to be what you get), then you can simply do:
function isPast(date) {
return new Date() > date;
}
If date isn't a date object, convert it to one first.
Note that identifiers starting with a capital letter are, by convention, reserved for constructors.
Edit: When I first read your question, I saw that you were using day first dates and jumped to my conclusion without really taking in your code. I've taken a closer look, and it appears my initial conclusion is just a red herring. The real problem is that you are taking two dates, converting them both to strings, and then trying to compare them. That's just silly - it will only tell you which string comes first alphabetically. Even sillier, is that for one of the dates you manually construct the string, but for the other one, you use a date formatting function. Why wouldn't you just use the date formatting function for both dates?
RobG's answer is correct and should be accepted.
When I see the date "01/11/2013", I read it as January 11, 2013, whereas you read it as 1 November, 2013. See the predicament the JavaScript engine is in when it tries to parse that date?*
When parsing JavaScript dates, I like to use year first dates, as these are unambiguous as far as I know. Everybody interprets "2013/11/01" as 2013 November 1. Including every JavaScript Date implementation I've seen.
Change your date format to be unambiguous.
*Frankly, I'm surprised you are seeing that behavior. I would expect that your system would be configured to expect day first dates and that the JavaScript engine would use those settings to properly parse the date. But, I'm not that surprised.

Format .NET DateTime "Day" with no leading zero

For the following code, I would expect result to equal 2, because the MSDN states that 'd' "Represents the day of the month as a number from 1 through 31. A single-digit day is formatted without a leading zero.".
DateTime myDate = new DateTime( 2009, 6, 4 );
string result = myDate.ToString( "d" );
However, result is actually equal to '6/4/2009' - which is the short-date format (which is also 'd'). I could use 'dd', but that adds a leading zero, which I don't want.
To indicate that this is a custom format specifier (in contrast to a standard format specifier), it must be two characters long. This can be accomplished by adding a space (which will show up in the output), or by including a percent sign before the single letter, like this:
string result = myDate.ToString("%d");
See documentation
Rather than using string formatting strings, how about using the Day property
DateTime myDate = new DateTime(2009,6,4)
int result = myDate.Day;
Or if you really needed the result in string format
string result = myDate.Day.ToString();
If you are looking to get a specific date part out of a date object rather than a formatted representation of the date, I prefer to use the properties (Day, Month, Year, DayOfWeek, etc.) It makes reading the code a bit easier (particularly if someone else is reading/maintaining it that doesn't have the various formatting codes memorized)

Categories

Resources