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; 
    }