2014-04-02 15 views
3

我有以下代碼,它的工作原理。如何Convert.ToDateTime()解析給定的字符串,當給定的文化不知道格式

string testDateStr = "2009.7.28 05:23:15"; 
DateTime testDateObj = Convert.ToDateTime(testDateStr, CultureInfo.GetCultureInfo("fr-FR")); 

我檢查了我的文化的有效格式:

string[] validFormats = testDateObj.GetDateTimeFormats(CultureInfo.GetCultureInfo("fr-FR")); 

,沒有他們的「2009年7月28日5點23分15秒」格式相匹配。我想知道如何在不引發格式異常的情況下對其進行解析,以及在調用Convert.ToDateTime()時完成了哪種隱藏解析。

更新: 我在LakshmiNarayanan的回答後嘗試了以下內容。

foreach(var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) 
{ 
    foreach(var format in testDateObj.GetDateTimeFormats(culture)) 
    { 
     if (format == testDateStr) 
     { 
      Console.WriteLine(culture.DisplayName); 
     } 
    } 
} 

有其實際包含格式我的字符串是文化,但它仍然無法解釋爲什麼當我們要求它使用一個特定的文化,而這種文化不知道轉換不拋出異常格式化字符串是英寸

回答

3

Convert.ToDateTime的方法在內部使用DateTime.Parse方法,它是基於內部複雜Lex方法。有一些規則適用於傳遞的字符串。它被分成令牌,每個令牌都被分析。分析非常複雜,我只會展示幾條規則。

如果令牌由數字組成並且長度爲3〜8,那麼這個令牌將是年份,這就是爲什麼可以解析01.2014.01字符串,這將產生01 Jan 2014結果。請注意,您也可以解析字符串,如01 2014 0101\n2014\n01,以獲得相同的結果。您可以使用空格或,.符號分隔令牌。

如果令牌是月份的名稱,那麼它將是月份(該表或令牌構建在內部DateTimeFormatInfo.CreateTokenHashTable方法中)。所以,你在哪裏找到FebFebruary並不重要。您可以平等解析2014 1 Jan2014.Jan.1,2014...,Jan..,..1或甚至5Jan2014字符串(最後一個不使用任何分隔符,但會檢查數字結束的位置,因此它已成功分割爲5,Jan2014令牌)。

如果我們有不明確的字符串01/04,那麼來自文化的信息被用來解決日/月的順序。訂單摘自DateTimeFormatInfo.MonthDayPattern。例如,對於en-US它是MMMM dd,並且對於en-GB它是dd MMMM。內部System.DateTimeParse類有private static bool GetMonthDayOrder(string pattern, DateTimeFormatInfo dtfi, out int order)方法,用於提取訂單。如果order變量的值爲6,則它是MM/dd,如果它的值爲7,則它是dd/MM。請注意,它不會嘗試做一些關於01/31的啓發式,只考慮從文化中提取的訂單。下面是測試代碼:

CultureInfo ci = CultureInfo.GetCultureInfo("en-US"); 

DateTimeFormatInfo dtfi = ci.DateTimeFormat; 
Assembly coreAssembly = Assembly.ReflectionOnlyLoad("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 
Type dateTimeParseType = coreAssembly.GetType("System.DateTimeParse"); 
MethodInfo getMonthDayOrderMethodInfo = dateTimeParseType.GetMethod("GetMonthDayOrder", BindingFlags.Static | BindingFlags.NonPublic); 
object[] parameters = new object[] { dtfi.MonthDayPattern, dtfi, null }; 
getMonthDayOrderMethodInfo.Invoke(null, parameters); 
int order = (int)parameters[2]; 
switch (order) 
{ 
    case -1: 
     Console.WriteLine("Cannot extract information"); 
     break; 
    case 6: 
     Console.WriteLine("MM/dd"); 
     break; 
    case 7: 
     Console.WriteLine("dd/MM"); 
     break; 
} 

和很多對AM/PM模式,星期,時間後綴其他檢查(例如韓國語的後綴認爲,這意味着每小時),等等。

下面的代碼將產生有關特定文化的令牌信息:

DateTimeFormatInfo dti = CultureInfo.InvariantCulture.DateTimeFormat; 
dynamic hashes = dti.GetType().GetMethod("CreateTokenHashTable", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(dti, null); 
var tokens = Enumerable.Repeat(new { str = "", type = "", value = "" }, 0).ToList(); 
foreach (dynamic hash in hashes) 
    if (hash != null) 
    { 
     Type hashType = hash.GetType(); 
     tokens.Add(new { str = (string)hashType.GetField("tokenString", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(hash).ToString(), 
         type = (string)hashType.GetField("tokenType", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(hash).ToString(), 
         value = (string)hashType.GetField("tokenValue", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(hash).ToString() }); 
    } 
foreach (var token in tokens.Distinct().OrderBy(t => t.type).ThenBy(t => t.value)) 
    Console.WriteLine("{0,10} {1} {2}", token.str, token.type, token.value); 

對於InvariantCulture輸出爲:

 AM 1027 0 
     PM 1284 1 
    Sunday DayOfWeekToken 0 
     Sun DayOfWeekToken 0 
    Monday DayOfWeekToken 1 
     Mon DayOfWeekToken 1 
    Tuesday DayOfWeekToken 2 
     Tue DayOfWeekToken 2 
Wednesday DayOfWeekToken 3 
     Wed DayOfWeekToken 3 
     Thu DayOfWeekToken 4 
    Thursday DayOfWeekToken 4 
    Friday DayOfWeekToken 5 
     Fri DayOfWeekToken 5 
     Sat DayOfWeekToken 6 
    Saturday DayOfWeekToken 6 
     AD EraToken 1 
     A.D. EraToken 1 
     , IgnorableSymbol 0 
     . IgnorableSymbol 0 
    January MonthToken 1 
     Jan MonthToken 1 
    October MonthToken 10 
     Oct MonthToken 10 
    November MonthToken 11 
     Nov MonthToken 11 
    December MonthToken 12 
     Dec MonthToken 12 
    February MonthToken 2 
     Feb MonthToken 2 
    March MonthToken 3 
     Mar MonthToken 3 
     Apr MonthToken 4 
    April MonthToken 4 
     May MonthToken 5 
     June MonthToken 6 
     Jun MonthToken 6 
     Jul MonthToken 7 
     July MonthToken 7 
     Aug MonthToken 8 
    August MonthToken 8 
September MonthToken 9 
     Sep MonthToken 9 
     /SEP_Date 0 
     - SEP_DateOrOffset 0 
     日 SEP_DaySuff 0 
     일 SEP_DaySuff 0 
     時 SEP_HourSuff 0 
     時 SEP_HourSuff 0 
     T SEP_LocalTimeMark 0 
     分 SEP_MinuteSuff 0 
     月 SEP_MonthSuff 0 
     월 SEP_MonthSuff 0 
     秒 SEP_SecondSuff 0 
     : SEP_Time 0 
     년 SEP_YearSuff 0 
     年 SEP_YearSuff 0 
     GMT TimeZoneToken 0 
     Z TimeZoneToken 0 

對於fr-FR培養(注意July被包括在列表中,以及作爲來自InvariantCulture的其他標記)

 AM 1027 0 
     PM 1284 1 
     h DateWordToken 0 
    dimanche DayOfWeekToken 0 
     Sun DayOfWeekToken 0 
     dim. DayOfWeekToken 0 
    Sunday DayOfWeekToken 0 
    lundi DayOfWeekToken 1 
    Monday DayOfWeekToken 1 
     lun. DayOfWeekToken 1 
     Mon DayOfWeekToken 1 
    Tuesday DayOfWeekToken 2 
     Tue DayOfWeekToken 2 
    mardi DayOfWeekToken 2 
     mar. DayOfWeekToken 2 
    mercredi DayOfWeekToken 3 
Wednesday DayOfWeekToken 3 
     mer. DayOfWeekToken 3 
     Wed DayOfWeekToken 3 
    jeudi DayOfWeekToken 4 
    Thursday DayOfWeekToken 4 
     Thu DayOfWeekToken 4 
     jeu. DayOfWeekToken 4 
     ven. DayOfWeekToken 5 
    vendredi DayOfWeekToken 5 
    Friday DayOfWeekToken 5 
     Fri DayOfWeekToken 5 
    samedi DayOfWeekToken 6 
     sam. DayOfWeekToken 6 
     Sat DayOfWeekToken 6 
    Saturday DayOfWeekToken 6 
ap. J.-C. EraToken 1 
     , IgnorableSymbol 0 
     . IgnorableSymbol 0 
    January MonthToken 1 
    janv. MonthToken 1 
    janvier MonthToken 1 
     Jan MonthToken 1 
     oct. MonthToken 10 
     Oct MonthToken 10 
    octobre MonthToken 10 
    October MonthToken 10 
     nov. MonthToken 11 
     Nov MonthToken 11 
    novembre MonthToken 11 
    November MonthToken 11 
     déc. MonthToken 12 
    December MonthToken 12 
     Dec MonthToken 12 
    décembre MonthToken 12 
    févr. MonthToken 2 
    février MonthToken 2 
    February MonthToken 2 
     Feb MonthToken 2 
     mars MonthToken 3 
    March MonthToken 3 
     Mar MonthToken 3 
     Apr MonthToken 4 
     avr. MonthToken 4 
    avril MonthToken 4 
    April MonthToken 4 
     mai MonthToken 5 
     May MonthToken 5 
     June MonthToken 6 
     juin MonthToken 6 
     Jun MonthToken 6 
     July MonthToken 7 
    juil. MonthToken 7 
    juillet MonthToken 7 
     Jul MonthToken 7 
     Aug MonthToken 8 
     août MonthToken 8 
    August MonthToken 8 
    sept. MonthToken 9 
     Sep MonthToken 9 
septembre MonthToken 9 
September MonthToken 9 
     /SEP_Date 0 
     - SEP_DateOrOffset 0 
     日 SEP_DaySuff 0 
     일 SEP_DaySuff 0 
     時 SEP_HourSuff 0 
     時 SEP_HourSuff 0 
     T SEP_LocalTimeMark 0 
     分 SEP_MinuteSuff 0 
     月 SEP_MonthSuff 0 
     월 SEP_MonthSuff 0 
     秒 SEP_SecondSuff 0 
     : SEP_Time 0 
     년 SEP_YearSuff 0 
     年 SEP_YearSuff 0 
     GMT TimeZoneToken 0 
     Z TimeZoneToken 0 
+0

這很有道理。所以基本上你是說我們傳遞的文化實際上沒有任何價值,除了使匹配邏輯更快? –

+0

@ΕГИІИО我們傳遞的文化是月份名稱,月份縮寫,日期名稱,各種滿足等的來源。格式字符串不被使用。 –

+0

然後我猜DateSeparator/TimeSeparator也不是它的一部分。 –

0

可能途徑

dateString = "05/01/1996"; 
    ConvertToDateTime(dateString); 
    dateString = "Tue Apr 28, 2009"; 
    ConvertToDateTime(dateString); 
    dateString = "Wed Apr 28, 2009"; 
    ConvertToDateTime(dateString); 
    dateString = "06 July 2008 7:32:47 AM"; 
    ConvertToDateTime(dateString); 
    dateString = "17:32:47.003"; 
    ConvertToDateTime(dateString); 
    // Convert a string returned by DateTime.ToString("R"). 
    dateString = "Sat, 10 May 2008 14:32:17 GMT"; 
    ConvertToDateTime(dateString); 
    // Convert a string returned by DateTime.ToString("o"). 
    dateString = "2009-05-01T07:54:59.9843750-04:00"; 
    ConvertToDateTime(dateString); 


int year=2009; 
int month=7; 
int day=28; 
int hr=5; 
int min=23; 
int s=15; 

DateTime testDateObj = Convert.ToDateTime(year, month, day, hr, min, s); 

或者乾脆

DateTime testDateObj = Convert.ToDateTime(2009, 7, 28, 5, 23, 15); 
+0

我認爲你沒有正確理解我的問題。我想要一個解釋。 –

0

嘗試使用類DateTimeFormatInfo有信息abount日期時間格式我你的文化。

+0

當你不指定文化時,我想知道它是如何工作的。 –

+0

它需要標準的文化設置 – Dallas

+0

但我的標準文化(本地)沒有列出這個特定的格式。因此,這個問題。 –

1

Datetime.GetDateTimeFormats()方法沒有以格式「2009.7.28 05:23:15」列出日期,這可能是因爲默認cultureInfo。

但是,如果您檢查IFormatProvider方法的重載方法GetDateTimeFormats(IFormatProvider),您可以看到對於文化「fr-FR」,該方法能夠使用「點」分隔符成功解析日期。例如28.07.09 5:23:15

因此,這個工作原理的邏輯假設是,DateTime.Parse()在所有可能的文化中運行字符串,如果沒有提供任何特定的文化,沒有任何文化的字符串匹配時會返回一個異常。

編輯:

通過MSDN挖掘時,Convert.ToDateTime(stringTime)正在與所述的DateTimeFormatInfo解析當前培養。

如果值不爲空,返回值是使用在被當前區域性初始化的 DateTimeFormatInfo對象格式化信息調用上值 DateTime.Parse方法的結果。 值參數必須包含日期和時間 的表示,其格式爲DateTimeFormatInfo主題中所述的格式之一。

因此,當沒有設置特定文化時,DateTimeFormatInfo對象引用默認構造函數。參照MSDN,

此構造創建表示 的不變培養的日期和時間信息的DateTimeFormatInfo對象。若要爲特定文化創建DateTimeFormatInfo對象,請爲該文化創建一個CultureInfo 對象,並檢索由其CultureInfo.DateTimeFormat屬性返回的DateTimeFormatInfo對象 。

因此,不定義的文化是沒有定義文化時的默認文化。所以Convert.ToDateTime的默認字符串方法是指DateTimeFormatInfo的默認對象,它指的是不變的文化。這意味着Convert.ToDateTime必須解析所有文化中的所有驗證。

因此,我們假設所有文化變體的驗證都被檢查是正確的。

希望它有幫助。榮譽,一個非常有趣的觀察。

+0

當您不指定GetDateTimeFormats()的文化時,它會選擇默認文化。當您使用Convert.ToDateTime()而沒有指定文化時相同。所以我不確定他們爲什麼不相關。但你有嘗試所有可能的格式的有效假設,然後再次,這是可怕的:) –

+0

我試圖找到給定的格式是否匹配解析通過所有可能的文化。令人驚訝的是,10種文化確實包含了它,這樣的解釋,但我遇到了另一種。我已經更新了我的問題。 –

+0

我剛剛用觀察更新了答案。如果沒有提到特定文化,matchin邏輯會更快,因爲我認爲它必須檢查多種文化。我的猜測是,當文化被定義時,它應該會更快。但我不認爲這個檢查會影響性能。 – LakshmiNarayanan

0

更新: - 嘗試這樣的:

string testDateStr = "2009.7.28 05:23:15"; 
    string testDateObj = Convert.ToDateTime(testDateStr).Date.ToString("d"); 

    string[] validFormats = (Convert.ToDateTime(testDateObj)).GetDateTimeFormats(); 
    foreach(string s in validFormats) 
    { 
     lblresult.Text += s; 
    } 
+0

你試過這個嗎?我看到明顯的編譯錯誤。 –

+0

對不起,我很快就寫了上一篇文章,請嘗試更新代碼。 –

+0

它仍然沒有解釋如何完成第2行的轉換。 –

相關問題