Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using RFC-1123 date format with C#, parsing issues for dates on Tuesdays

Tags:

c#

rfc1123

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?

like image 311
andrew.w.lane Avatar asked Dec 17 '22 11:12

andrew.w.lane


1 Answers

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()

like image 80
antlersoft Avatar answered Dec 19 '22 01:12

antlersoft