The best way to save DateTime in string and the parse it back

Viewed 79

The code below seems to be ok but it fails when it comes to save/parse Thai calendar, e.g. 08/31/2555 will fail and will be parsed as 31/8/3108. Why it so? If I specify "Thai Culture" then it will fail with US/EU time.

DateTime time = DateTime.Now.AddYears(-10);
String timeString = time.ToString("MM/dd/yyyy HH:mm:ss");

DateTime newTime = DateTime.ParseExact(timeString, "MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture));
4 Answers

Are you sure you get 3108 and not 3098?

Your string will be in the local calendar (the Thai calendar is 543 years ahead so is 2022 - 10 + 543 = 2555) - then you're reading that date in using the invariant culture (which uses the Gregorian calendar).

2555 in the Gregorian calendar is then converted to the Thai calendar when it's being printed, and should result in a second conversion: 2555 + 543 = 3098.

Your code is equivalent to:

const string desiredFormat = "MM/dd/yyyy HH:mm:ss";

// Set the current thread to the Thai culture.
// This line is only here to demonstrate the problem and can be removed.
Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");

// {31/8/2555 15:32:04} - original
var time = DateTime.Now.AddYears(-10);

// "08/31/2555 15:32:04" (Thai calendar string)
var timeStringLocal = time.ToString(desiredFormat);

// {31/8/3098 15:32:04} - incorrect
// 2555 is treated as a Gregorian date, then converted to 3098 in the Thai calendar when displayed
var newTimeFromLocal = DateTime.ParseExact(timeStringLocal, desiredFormat, CultureInfo.InvariantCulture);

What I'd instead suggest that you do is to have the string for parsing use the invariant (Gregorian) calendar instead, which will avoid any such issues.

const string desiredFormat = "MM/dd/yyyy HH:mm:ss";

// Set the current thread to the Thai culture.
// This line is only here to demonstrate the problem and can be removed.
Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");

// {31/8/2555 15:32:04} - original
var time = DateTime.Now.AddYears(-10);

// "08/31/2012 15:32:04" - (invariant - Gregorian - calendar string)
var timeStringInvariant = time.ToString(desiredFormat, CultureInfo.InvariantCulture);

// {31/8/2555 15:32:04} - correct
var newTimeFromInvariant = DateTime.ParseExact(timeStringInvariant, desiredFormat, CultureInfo.InvariantCulture);

If you have full control over both saving and parsing, you could use a specific culture for both.

E.g. using "en-US" culture:

CultureInfo DATETIME_CULTURE = CultureInfo.GetCultureInfo("en-US");
const string DATETIME_FORMAT = "yyyy/MM/dd_HH:mm:ss";

DateTime time = DateTime.Now.AddYears(-10);
string timeString = time.ToString(DATETIME_FORMAT, DATETIME_CULTURE);
DateTime newTime = DateTime.ParseExact(timeString, DATETIME_FORMAT, DATETIME_CULTURE, DateTimeStyles.None);

Now time should be identical to newTime.

Use a culture-invariant format O:

var th = new CultureInfo("th-TH");
var us = new CultureInfo("en-US");
var str = DateTime.Now.AddYears(-10).ToString("O");
Console.WriteLine(str);
var dt_th = DateTime.Parse(str, th);
var dt_us = DateTime.Parse(str, us);
Console.WriteLine(dt_th);
Console.WriteLine(dt_us);
Console.WriteLine(dt_th == dt_us);
2012-08-31T14:24:14.4710048+00:00
8/31/2012 2:24:14 PM
8/31/2012 2:24:14 PM
True
Related