views:

292

answers:

4

I am getting a String.FormatException trying to convert/parse a string when the culture is other than non-US. The odd thing is that the string was generated by applying the very same format and culture as those being used to parse it back into a string. In the code below, all of these versions will fail:

const string culture = "ja-JP";
const string format = "dd MMM yyyy"; //error in orignal post included {0:}
CultureInfo info = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = info;
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(culture);

//string toParse = String.Format(info, format, DateTime.Now); //error in original post
string toParse = DateTime.Now.ToString(format);
System.Diagnostics.Debug.WriteLine(string.Format("Culture format = {0}, Date = {1}", culture, toParse));
try
{
    DateTime output = DateTime.ParseExact(toParse, format, CultureInfo.InvariantCulture);
    //DateTime output = DateTime.ParseExact(toParse, format, info);
    //DateTime output = DateTime.ParseExact(toParse, format, info, DateTimeStyles.None);
    //DateTime output = Convert.ToDateTime(toParse, info);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine(ex.Message);
}

Note that the string for en-US is "25 Feb 2010". The string for ja-JP is "25 2 2010".

Any idea how to get "25 2 2010" back into a date?

Thanks in advance.

Edit1: I should note that the Japanese culture is hard-coded here only as an example. I really need this to work with whatever culture is set by the user. What I need is a solution where the date time format works no matter what the user's culture. I think the single M does it.

Edit 2: M doesn't work for English. Anyone know a format string that works for all cultures?

A: 

Try using a single M when parsing the date. That is what is used in the example for the MonthDayPattern for Japanese culture.

const string format = "{0:dd M yyyy}";
tvanfosson
That's really not the problem. The OP is trying to use the composite format string as the format for `DateTime.ParseExact`, which is only expecting the date time format.
João Angelo
+2  A: 

If you change:

DateTime output = DateTime.ParseExact(
    toParse, format, CultureInfo.InvariantCulture);

to

DateTime output = DateTime.ParseExact(toParse, "dd MMM yyyy", info);

the date is correctly parsed.

Note that in your example you are using a culture (ja-JP) to convert to string but another culture to convert from string. Another problem is that String.Format accepts a composite format string ("My string to format - {0:dd MMM yyyy}"), but DateTime.ParseExact is expecting only the date time format.

João Angelo
If you want to use `DateTime.ParseExact` with `CultureInfo.InvariantCulture` then yes, you are right. If you use the JP culture however, it is not needed.
João Angelo
Yes, good catch on the {0:} thing - that wasn't the ultimate problem, but it was an error in my test script (now fixed). I did also try using the ja-JP culture during parsing - there are a number of tries in there. Only went to Invariant when that wasn't working.
sydneyos
A: 
string text = "25 2 2009";
DateTime date = DateTime.ParseExact(text, "d M yyyy", CultureInfo.InvariantCulture);
šljaker
A: 

The format pattern you pass to DateTime.ParseExact has to be just the date pattern, without the placeholder. And for JP culture, you need to use just one M since the dates are represented by numbers even when MMM is specified when converting to a string.

        const string culture = "ja-JP";
        const string FROM_STRING_FORMAT = "dd M yyyy";
        const string TO_STRING_FORMAT = "{0:" + FROM_STRING_FORMAT + "}";
        CultureInfo info = new CultureInfo(culture);
        Thread.CurrentThread.CurrentCulture = info;
        Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(culture);

        string toParse = String.Format(info, TO_STRING_FORMAT, DateTime.Now);
        Console.WriteLine(string.Format("Culture format = {0}, Date = {1}", culture, toParse));
        try
        {
            DateTime output = DateTime.ParseExact(toParse, FROM_STRING_FORMAT, CultureInfo.InvariantCulture);
            Console.WriteLine(output);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
Sam
This looks like the answer - does this work for all cultures?
sydneyos
No, this doesn't work for **English**. Japanese is a bit unusual where even when you specified `MMM` you get a result that is only `M`. So the string format `MMM` is not round-trip compliant when using `ParseExact` with Japanese. The best recommendation I can give you is use the `ParseExact` overload that accepts multiple formats, like this: `DateTime.ParseExact(toParse, new [] { "d M yyyy", "d MMM yyyy"}, CultureInfo.InvariantCulture, DateTimeStyles.None);`
Sam
João Angelo is right in his post. The real problem with your original is that you're formatting to-string with a specific culture, and then trying to parse it using invariant culture. The format strings don't match. If you use the same culture info in both format and parse, it works.
Sam