22

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()); } 
1
  • The other benefit to this approach is that the short time in the database tops out at 2067, so setting the twodigityearmax to 2067 works nicely. Commented Nov 19, 2009 at 20:47

6 Answers 6

26

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Yeah, in this case they do want all dates in the future, but it is nice to know the best place to put more complicated code should the need arise.
Though the answer is pretty old, I wonder about the need to use 3rd party libraries for the +/-50 rule. If the +/-50 rule is needed, why not just setting TwoDigitYearMax to DateTime.Today.Year + 50?
12

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...

Comments

4

You're looking for the Calendar.TwoDigitYearMax Property.

Jon Skeet has posted something on this you will likely find useful.

Comments

0

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; } 

1 Comment

Largest downside to this is that the date parsing is static, given that the intent is to read user input, a more flexible approach to date parsing is useful.
0

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?

Comments

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

1 Comment

This is a late answer to a question with an already established answer. In the future, please try to be extra careful with formatting and provide more context as to why this answer is better than the widely accepted answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.