4

We ran into a bug in our product caused by the fact that for certain cultures, if you set the current thread's culture to that culture and then output a string based on a DateTime in RFC1123 format and then try to convert it back to a DateTime, it fails. The most head-scratching failures are from a lot of cultures that have days of the week that translate it to a word that begins with mar, like martedi in Italian. I'm assuming that the parsing routines see "mar" and think it means March in English and everything fails, but that's just speculation. The best way I know of to describe exactly what I'm talking about is to provide code:

using System;
using System.Globalization;
using System.Threading;

namespace RFC1123Test
{
    class Program
    {
    static void Main(string[] args)
    {
        //list taken from http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo(v=vs.71).aspx
        var culturesToTest = new string[] { String.Empty /*invariant culture*/, "af", "af-ZA", 
        "sq", "sq-AL", "ar", "ar-DZ", "ar-BH", "ar-EG", "ar-IQ", "ar-JO", "ar-KW", 
        "ar-LB", "ar-LY", "ar-MA", "ar-OM", "ar-QA", "ar-SA", "ar-SY", "ar-TN", 
        "ar-AE", "ar-YE", "hy", "hy-AM", "az", "az-AZ-Cyrl", "az-AZ-Latn", "eu", "eu-ES", 
        "be", "be-BY", "bg", "bg-BG", "ca", "ca-ES", "zh-HK", "zh-MO", "zh-CN", "zh-CHS", 
        "zh-SG", "zh-TW", "zh-CHT", "hr", "hr-HR", "cs", "cs-CZ", "da", "da-DK", "div", 
        "div-MV", "nl", "nl-BE", "nl-NL", "en", "en-AU", "en-BZ", "en-CA", "en-CB", 
        "en-IE", "en-JM", "en-NZ", "en-PH", "en-ZA", "en-TT", "en-GB", "en-US", "en-ZW", 
        "et", "et-EE", "fo", "fo-FO", "fa", "fa-IR", "fi", "fi-FI", "fr", "fr-BE", "fr-CA",
        "fr-FR", "fr-LU", "fr-MC", "fr-CH", "gl", "gl-ES", "ka", "ka-GE", "de", "de-AT", 
        "de-DE", "de-LI", "de-LU", "de-CH", "el", "el-GR", "gu", "gu-IN", "he", "he-IL", 
        "hi", "hi-IN", "hu", "hu-HU", "is", "is-IS", "id", "id-ID", "it", "it-IT", 
        "it-CH", "ja", "ja-JP", "kn", "kn-IN", "kk", "kk-KZ", "kok", "kok-IN", "ko",
        "ko-KR", "ky", "ky-KZ", "lv", "lv-LV", "lt", "lt-LT", "mk", "mk-MK", "ms", 
        "ms-BN", "ms-MY", "mr", "mr-IN", "mn", "mn-MN", "no", "nb-NO", "nn-NO", "pl",
        "pl-PL", "pt", "pt-BR", "pt-PT", "pa", "pa-IN", "ro", "ro-RO", "ru", "ru-RU", 
        "sa", "sa-IN", "sr-SP-Cyrl", "sr-SP-Latn", "sk", "sk-SK", "sl", "sl-SI", "es",
        "es-AR", "es-BO", "es-CL", "es-CO", "es-CR", "es-DO", "es-EC", "es-SV", "es-GT",
        "es-HN", "es-MX", "es-NI", "es-PA", "es-PY", "es-PE", "es-PR", "es-ES", "es-UY",
        "es-VE", "sw", "sw-KE", "sv", "sv-FI", "sv-SE", "syr", "syr-SY", "ta", "ta-IN", 
        "tt", "tt-RU", "te", "te-IN", "th", "th-TH", "tr", "tr-TR", "uk", "uk-UA", "ur", 
        "ur-PK", "uz", "uz-UZ-Cyrl", "uz-UZ-Latn", "vi", "vi-VN" };

        //2:30 PM local time
        var timeOfDayForTest = TimeSpan.FromHours(14).Add(TimeSpan.FromMinutes(30));

        CultureInfo cultureForTest = null;
        foreach (var culture in culturesToTest)
        {
        bool suitableForTest = true;
        try
        {
            cultureForTest = new CultureInfo(culture);
            Thread.CurrentThread.CurrentCulture = cultureForTest;
            Thread.CurrentThread.CurrentUICulture = cultureForTest;
        }
        catch
        {
            suitableForTest = false;
        }
        if (suitableForTest)
        {
            //be sure to do a date for every day of the week
            for (int i = 0; i < 7; i++)
            {
            runTest(cultureForTest, DateTime.Now.Date.Add(timeOfDayForTest).AddDays(i));
            }
        }
        }
    }

    private static void runTest(CultureInfo culture, DateTime dt)
    {
        //string representation of RFC1123 pattern
        string rfc1123Date = dt.ToUniversalTime().ToString(CultureInfo.CurrentCulture.DateTimeFormat.RFC1123Pattern);

        bool parseFails = false;
        DateTime parsedDt = DateTime.MinValue;
        try
        {
        parsedDt = Convert.ToDateTime(rfc1123Date);
        }
        catch
        {
        parseFails = true;
        }
        if (parseFails)
        {
        Console.WriteLine(String.Format("ERROR: {0} [{1}] parse failed for [{2}].",
            culture.Name, culture.NativeName, rfc1123Date));
        }
        else
        {
        if (parsedDt.Ticks == dt.Ticks)
        {
            //success!
            //Console.WriteLine(String.Format("SUCCESS: {0} [{1}] worked just fine for {2}.",
            //culture.Name, culture.NativeName, rfc1123Date));
        }
        else
        {
            Console.WriteLine(String.Format("ERROR: {0} [{1}] parsed successfully for {2} but parsed date does not match.",
            culture.Name, culture.NativeName, rfc1123Date));
        }
        }
    }
    }
}

The output is this:

ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Dins, 12 Jul 2011 18:30:00 GMT].
ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Woen, 13 Jul 2011 18:30:00 GMT].
ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Dond, 14 Jul 2011 18:30:00 GMT].
ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Vry, 15 Jul 2011 18:30:00 GMT].
ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Sat, 16 Jul 2011 18:30:00 GMT].
ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Son, 17 Jul 2011 18:30:00 GMT].
ERROR: af-ZA [Afrikaans (Suid Afrika)] parse failed for [Maan, 18 Jul 2011 18:30:00 GMT].
ERROR: sq-AL [shqipe (Shqip‰ria)] parse failed for [Mar, 12 Kor 2011 18:30:00 GMT].
ERROR: sq-AL [shqipe (Shqip‰ria)] parse failed for [Sht, 16 Kor 2011 18:30:00 GMT].
ERROR: gl-ES [galego (galego)] parse failed for [mar, 12 xull 2011 18:30:00 GMT].
ERROR: it-IT [italiano (Italia)] parse failed for [mar, 12 lug 2011 18:30:00 GMT].
ERROR: it-CH [italiano (Svizzera)] parse failed for [mar, 12 lug 2011 18:30:00 GMT].
ERROR: it-CH [italiano (Svizzera)] parse failed for [gio, 14 lug 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 12 7 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 13 7 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 14 7 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 15 7 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 16 7 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 17 7 2011 18:30:00 GMT].
ERROR: ja-JP [??? (??)] parse failed for [?, 18 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 12 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 13 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 14 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 15 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 16 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 17 7 2011 18:30:00 GMT].
ERROR: ko-KR [??? (????)] parse failed for [?, 18 7 2011 18:30:00 GMT].
ERROR: es-AR [Espa¤ol (Argentina)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-BO [Espa¤ol (Bolivia)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-CL [Espa¤ol (Chile)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-CO [Espa¤ol (Colombia)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-CR [Espa¤ol (Costa Rica)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-DO [Espa¤ol (Rep£blica Dominicana)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-EC [Espa¤ol (Ecuador)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-SV [Espa¤ol (El Salvador)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-GT [Espa¤ol (Guatemala)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-HN [Espa¤ol (Honduras)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-MX [Espa¤ol (M‚xico)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-NI [Espa¤ol (Nicaragua)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-PA [Espa¤ol (Panam )] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-PY [Espa¤ol (Paraguay)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-PE [Espa¤ol (Per£)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-PR [Espa¤ol (Puerto Rico)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-ES [espa¤ol (Espa¤a)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-UY [Espa¤ol (Uruguay)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].
ERROR: es-VE [Espa¤ol (Republica Bolivariana de Venezuela)] parse failed for [mar, 12 jul 2011 18:30:00 GMT].

So my question is, why doesn't this code work?

andrew.w.lane
  • 118
  • 3
  • 10

2 Answers2

6

The notes for RFC1123Pattern is that it is to be parsed using the invariant culture. The following should work:

string rfc1123Date = dt.ToUniversalTime().ToString("R");

...

parsedDt = Convert.ToDateTime(rfc1123Date);

Tail of your output:

SUCCESS: vi-VN [Tiê'ng Vi?t (Vi?t Nam)] worked just fine for Wed, 13 Jul 2011 18:30:00 GMT. SUCCESS: vi-VN [Tiê'ng Vi?t (Vi?t Nam)] worked just fine for Thu, 14 Jul 2011 18:30:00 GMT. SUCCESS: vi-VN [Tiê'ng Vi?t (Vi?t Nam)] worked just fine for Fri, 15 Jul 2011 18:30:00 GMT. SUCCESS: vi-VN [Tiê'ng Vi?t (Vi?t Nam)] worked just fine for Sat, 16 Jul 2011 18:30:00 GMT. SUCCESS: vi-VN [Tiê'ng Vi?t (Vi?t Nam)] worked just fine for Sun, 17 Jul 2011 18:30:00 GMT. SUCCESS: vi-VN [Tiê'ng Vi?t (Vi?t Nam)] worked just fine for Mon, 18 Jul 2011 18:30:00 GMT.

user7116
  • 63,008
  • 17
  • 141
  • 172
  • Interesting change. It makes all the strange Tuesday problems go away, but there are still parse failures for these cultures: af-ZA ar-SA th-TH – andrew.w.lane Jul 13 '11 at 11:08
6

Try using DateTime.ParseExact with the same format provider you used to generate the text date:

DateTime.ParseExact(rfc1123Date, CultureInfo.CurrentCulture.DateTimeFormat.RFC1123Pattern, CultureInfo.CurrentCulture).ToLocalTime()

andrew.w.lane
  • 118
  • 3
  • 10
antlersoft
  • 14,636
  • 4
  • 35
  • 55