2012-07-26 74 views
10

解析部分日期值時,我需要分析這可能是任何合理的格式的日期字符串,假設一年。例如:坐落在.NET

  • 2012-12-25
  • 25 december 2012
  • 25 dec
  • 17:35

一些這些字符串的包含不明確的日期,這會導致在幾個可能DateTime值(例如25 dec可以解釋爲2012-12-252011-12-251066-12-25,等等)。

DateTime.Parse當前處理這些不明確的值的方法是通過使用當前系統日期,以確定上下文。因此,如果當前的日期是26日2012年7月字符串25 dec被認爲是當年和被解析爲2012-12-25

是它在某種程度上可以改變此行爲,並設置當前日期背景下自己?

+1

的,這是接近:http://stackoverflow.com/questions/2003088/using-datetime-tryparseexact-without-瞭解這一年的 – 2012-07-26 14:29:44

+0

更多的信息也在這裏;不是很重複http://stackoverflow.com/questions/2024273/convert-a-two-digit-year-to-a-four-digit-year – 2017-02-08 18:28:30

回答

2

我能想到的唯一的事情是後期處理的日期。之後你有字符串,並且在DateTime對象中有一年。如果字符串不包含年份,則自己設置年份。

if(! string.contains(DateTime.Year.toString()) { 
    // Set the year yourself 
} 
+2

如果字符串是「25 dec 12」,那該怎麼辦?那麼這一年將被設置爲2012,但該字符串不包含「2012」 – 2012-07-26 14:34:42

+2

DateTime.Year將當前年份返回爲4位數,因此對於任何輸入日期爲2位數的年份,這將失敗。只檢查兩位數在某些情況下,月份/日期不明確 – 2012-07-26 14:36:22

+1

這不是一個糟糕的解決方法,我擔心的是當前年份可能在日期字符串的其他地方合法存在,例如「2008-11-01T19:35:00.2012000Z」 – Wheelie 2012-07-26 14:38:55

1

你可以嘗試處理IFormatProvider的東西,但這可能需要一段時間。作爲一個快速的解決方案,我可以提出一個擴展方法:

public static class MyDateTimeStringExtensions 
{ 
    public static DateTime ToDateTimeWithYear(this string source, int year) 
    { 
     var dateTime = DateTime.Parse(source); 

     return dateTime.AddYears(year - dateTime.Year); 
    } 
} 
.... 
"2/2".ToDateTimeWithYear(2001) // returns 2/2/2001 12:00:00 AM 
1

如果你希望得到各種格式的「不完整的」日期/時間信息,你可以嘗試解析文本具體不同格式的最低詳述most-詳細。例如:

var text = "June 15"; 
DateTime datetime; 
if(DateTime.TryParseExact(text, "m", CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out datetime)) 
{ 
    // was just month, day, replace year with specific value: 
    datetime = new DateTime(1966, datetime.Month, datetime.Day); 
} 
else 
{ 
    // wasn't just month, day, try parsing a "whole" date/time: 
    datetime = DateTime.Parse(text); 
} 

...此代碼試圖解析月/日的格式在目前的文化(如果你有一個特定的,獨立的電流的文化,你可以用一種文化取代「CultureInfo.CurrentCulture」那就是你想要的格式)。如果失敗了,它會假設文本更加詳細,並繼續像往常一樣解析它。

如果您的日期/時間不是本地的,請不要使用DateTimeStyles.AssumeLocal。我總是建議以任何方式存儲的日期/時間數據(如序列化到文本),您始終使用通用;因爲當數據序列化時你不知道文化在起作用。 Universal是在公平競爭環境中獲取日期/時間數據的唯一可靠方法。在這種情況下使用DateTimeStyles.AssumeUnivesal

1

我有一個非常類似的問題。 DateTime.Parse或DateTime.TryParse將假定一天中的時間爲00:00:00,此時該字符串不包含任何時間信息。正如年份假設一樣,沒有辦法指定一個不同的時間作爲默認值。這是一個真正的問題,因爲設置這些默認值的時間是,因此解析方法會經歷所有的詳細步驟。否則,你必須非常痛苦地重新發明輪子,以確定該字符串是否包含將覆蓋默認值的信息。

我看了一下日期時間的源代碼。TryParse,並且可以預料的是,微軟已經竭盡全力去擴展DateTime類。所以我編寫了一些使用反射的代碼來利用它可以從DateTime的源代碼中獲得的東西。這有一些顯著的缺點:

  • 的使用反射代碼是尷尬
  • 的使用反射代碼調用,如果.NET Framework是升級這可能會改變內部成員
  • 反射,使用代碼將運行比一些不需要使用反射的假設替代方法慢

在我的情況下,我認爲沒有什麼比從頭開始重新創建DateTime.TryParse更尷尬了。我有單元測試,表明內部成員是否已經改變。我認爲對我而言,表現的懲罰是微不足道的。

我的代碼如下。此代碼用於覆蓋默認的小時/分鐘/秒,但我認爲可以輕鬆修改或擴展以覆蓋默認年份或其他內容。該代碼忠實地模仿內部System.DateTimeParse.TryParse之一的重載的內部代碼(它確實是DateTime.TryParse的實際工作),儘管我不得不使用尷尬的反射來做到這一點。唯一與System.DateTimeParse.TryParse有效不同的是它指定了一個默認的小時/分鐘/秒而不是將它們全部設置爲零。

以供參考,這是類DateTimeParse的,我模仿

 internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result) { 
     result = DateTime.MinValue; 
     DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result. 
     resultData.Init(); 
     if (TryParse(s, dtfi, styles, ref resultData)) { 
      result = resultData.parsedDate; 
      return true; 
     } 
     return false; 
    } 

的方法在這裏是我的代碼

public static class TimeExtensions 
{ 
    private static Assembly _sysAssembly; 
    private static Type _dateTimeParseType, _dateTimeResultType; 
    private static MethodInfo _tryParseMethod, _dateTimeResultInitMethod; 
    private static FieldInfo _dateTimeResultParsedDateField, 
       _dateTimeResultHourField, _dateTimeResultMinuteField, _dateTimeResultSecondField; 
    /// <summary> 
    /// This private method initializes the private fields that store reflection information 
    /// that is used in this class. The method is designed so that it only needs to be called 
    /// one time. 
    /// </summary> 
    private static void InitializeReflection() 
    { 
     // Get a reference to the Assembly containing the 'System' namespace 
     _sysAssembly = typeof(DateTime).Assembly; 
     // Get non-public types of 'System' namespace 
     _dateTimeParseType = _sysAssembly.GetType("System.DateTimeParse"); 
     _dateTimeResultType = _sysAssembly.GetType("System.DateTimeResult"); 
     // Array of types for matching the proper overload of method System.DateTimeParse.TryParse 
     Type[] argTypes = new Type[] 
     { 
      typeof(String), 
      typeof(DateTimeFormatInfo), 
      typeof(DateTimeStyles), 
      _dateTimeResultType.MakeByRefType() 
     }; 
     _tryParseMethod = _dateTimeParseType.GetMethod("TryParse", 
       BindingFlags.Static | BindingFlags.NonPublic, null, argTypes, null); 
     _dateTimeResultInitMethod = _dateTimeResultType.GetMethod("Init", 
       BindingFlags.Instance | BindingFlags.NonPublic); 
     _dateTimeResultParsedDateField = _dateTimeResultType.GetField("parsedDate", 
       BindingFlags.Instance | BindingFlags.NonPublic); 
     _dateTimeResultHourField = _dateTimeResultType.GetField("Hour", 
       BindingFlags.Instance | BindingFlags.NonPublic); 
     _dateTimeResultMinuteField = _dateTimeResultType.GetField("Minute", 
       BindingFlags.Instance | BindingFlags.NonPublic); 
     _dateTimeResultSecondField = _dateTimeResultType.GetField("Second", 
       BindingFlags.Instance | BindingFlags.NonPublic); 
    } 
    /// <summary> 
    /// This method converts the given string representation of a date and time to its DateTime 
    /// equivalent and returns true if the conversion succeeded or false if no conversion could be 
    /// done. The method is a close imitation of the System.DateTime.TryParse method, with the 
    /// exception that this method takes a parameter that allows the caller to specify what the time 
    /// value should be when the given string contains no time-of-day information. In contrast, 
    /// the method System.DateTime.TryParse will always apply a value of midnight (beginning of day) 
    /// when the given string contains no time-of-day information. 
    /// </summary> 
    /// <param name="s">the string that is to be converted to a DateTime</param> 
    /// <param name="result">the DateTime equivalent of the given string</param> 
    /// <param name="defaultTime">a DateTime object whose Hour, Minute, and Second values are used 
    /// as the default in the 'result' parameter. If the 's' parameter contains time-of-day 
    /// information, then it overrides the value of 'defaultTime'</param> 
    public static Boolean TryParse(String s, out DateTime result, DateTime defaultTime) 
    { 
     // Value of the result if no conversion can be done 
     result = DateTime.MinValue; 
     // Create the buffer that stores the parsed result 
     if (_sysAssembly == null) InitializeReflection(); 
     dynamic resultData = Activator.CreateInstance(_dateTimeResultType); 
     _dateTimeResultInitMethod.Invoke(resultData, new Object[] { }); 
     // Override the default time values of the buffer, using this method's parameter 
     _dateTimeResultHourField.SetValue(resultData, defaultTime.Hour); 
     _dateTimeResultMinuteField.SetValue(resultData, defaultTime.Minute); 
     _dateTimeResultSecondField.SetValue(resultData, defaultTime.Second); 
     // Create array parameters that can be passed (using reflection) to 
     // the non-public method DateTimeParse.TryParse, which does the real work 
     Object[] tryParseParams = new Object[] 
     { 
      s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, resultData 
     }; 
     // Call non-public method DateTimeParse.TryParse 
     Boolean success = (Boolean)_tryParseMethod.Invoke(null, tryParseParams); 
     if (success) 
     { 
      // Because the DateTimeResult object was passed as a 'ref' parameter, we need to 
      // pull its new value out of the array of method parameters 
      result = _dateTimeResultParsedDateField.GetValue((dynamic)tryParseParams[3]); 
      return true; 
     } 
     return false; 
    } 
} 

- 編輯 - 後來我意識到,我需要爲方法DateTime.TryParseExact做同樣的事情。但是,上述方法對TryParseExact不起作用,這導致我擔心該方法比我想象的更脆弱。好吧。令人高興的是,我能夠想到的TryParseExact一個非常不同的方法不使用任何反射

 public static Boolean TryParseExact(String s, String format, IFormatProvider provider, 
          DateTimeStyles style, out DateTime result, DateTime defaultTime) 
    { 
     // Determine whether the format requires that the time-of-day is in the string to be converted. 
     // We do this by creating two strings from the format, which have the same date but different 
     // time of day. If the two strings are equal, then clearly the format contains no time-of-day 
     // information. 
     Boolean willApplyDefaultTime = false; 
     DateTime testDate1 = new DateTime(2000, 1, 1, 2, 15, 15); 
     DateTime testDate2 = new DateTime(2000, 1, 1, 17, 47, 29); 
     String testString1 = testDate1.ToString(format); 
     String testString2 = testDate2.ToString(format); 
     if (testString1 == testString2) 
      willApplyDefaultTime = true; 

     // Let method DateTime.TryParseExact do all the hard work 
     Boolean success = DateTime.TryParseExact(s, format, provider, style, out result); 

     if (success && willApplyDefaultTime) 
     { 
      DateTime rawResult = result; 
      // If the format contains no time-of-day information, then apply the default from 
      // this method's parameter value. 
      result = new DateTime(rawResult.Year, rawResult.Month, rawResult.Day, 
          defaultTime.Hour, defaultTime.Minute, defaultTime.Second); 
     } 
     return success; 
    }