在C#中将字符串解析为DateTime

Hoo*_*och 157 .net c# string datetime parsing

我有一个字符串格式的日期和时间:

"2011-03-21 13:26" //year-month-day hour:minute
Run Code Online (Sandbox Code Playgroud)

我怎么解析它System.DateTime

我想使用类似DateTime.Parse()DateTime.ParseExact()尽可能的函数,以便能够手动指定日期的格式.

Mit*_*eat 254

DateTime.Parse()将尝试找出给定日期的格式,它通常做得很好.如果您可以保证日期始终采用给定格式,那么您可以使用ParseExact():

string s = "2011-03-21 13:26";

DateTime dt = 
    DateTime.ParseExact(s, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
Run Code Online (Sandbox Code Playgroud)

(但请注意,如果日期不是预期的格式,使用其中一种TryParse方法通常会更安全)

确保在构造格式字符串时检查 自定义日期和时间格式字符串,尤其要注意字母和大小写的数量(即"MM"和"mm"表示非常不同的东西).

C#格式字符串的另一个有用资源是C#中的字符串格式化

  • 更正 - 它总是更安全;)如果您正在调用具有异常的方法,请始终首先检查异常条件(如果可能). (5认同)
  • 我会说永远传递你的文化更安全.我宁愿有一个例外,而不是将"01-02-2013"​​误解为1月2日或2月1日. (3认同)

Mat*_*att 43

正如我稍后解释的那样,我总是喜欢TryParseTryParseExact方法.因为它们使用起来有点笨重,所以我编写了一个扩展方法,使解析更容易:

var    dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
Run Code Online (Sandbox Code Playgroud)

与之不同Parse,ParseExact它不会抛出异常,并允许您检查通过

if (dt.HasValue) { // continue processing } else { // do error handling }

转换是否成功(在这种情况下dt有一个你可以访问的值dt.Value)或不是(在这种情况下,它是null).

这甚至允许使用像"Elvis"-operator这样的优雅快捷方式?.,例如:

int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;
Run Code Online (Sandbox Code Playgroud)

在这里你也可以year.HasValue用来检查转换是否成功,如果它没有成功那么year将包含null,否则是日期的年份部分.如果转换失败,则不会抛出异常.


解决方案:  .ToDate()扩展方法

在.NetFiddle中试一试

public static class Extensions
{
    // Extension method parsing a date string to a DateTime?
    // dateFmt is optional and allows to pass a parsing pattern array
    // or one or more patterns passed as string parameters
    public static DateTime? ToDate(this string dateTimeStr, params string[] dateFmt)
    {
      // example: var dt = "2011-03-21 13:26".ToDate(new string[]{"yyyy-MM-dd HH:mm", 
      //                                                  "M/d/yyyy h:mm:ss tt"});
      // or simpler: 
      // var dt = "2011-03-21 13:26".ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
      const DateTimeStyles style = DateTimeStyles.AllowWhiteSpaces;
      if (dateFmt == null)
      {
        var dateInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat;
        dateFmt=dateInfo.GetAllDateTimePatterns();
      }
      // Commented out below because it can be done shorter as shown below.
      // For older C# versions (older than C#7) you need it like that:
      // DateTime? result = null;
      // DateTime dt;
      // if (DateTime.TryParseExact(dateTimeStr, dateFmt,
      //    CultureInfo.InvariantCulture, style, out dt)) result = dt;
      // In C#7 and above, we can simply write:
      var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
                   style, out var dt) ? dt : null as DateTime?;
      return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

有关代码的一些信息

您可能想知道,为什么我使用了InvariantCulture调用TryParseExact:这是强制函数始终以相同的方式处理格式模式(否则例如"."可以解释为英文的小数分隔符,而它是一个组分隔符日期分隔符德语).回想一下,我们已经在几行之前查询了基于文化的格式字符串,所以这里没问题.

更新:( .ToDate()不带参数)现在默认为线程当前文化的所有常见日期/时间模式.
请注意,我们需要resultdt一起,因为TryParseExact不允许使用DateTime?,我们打算返回.在C#Version 7中,您可以ToDate如下简化功能:

 // in C#7 only: "DateTime dt;" - no longer required, declare implicitly
 if (DateTime.TryParseExact(dateTimeStr, dateFmt,
     CultureInfo.InvariantCulture, style, out var dt)) result = dt;
Run Code Online (Sandbox Code Playgroud)

或者,如果你喜欢它甚至更短:

 // in C#7 only: Declaration of result as a "one-liner" ;-)
 var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
              style, out var dt) ? dt : null as DateTime?;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你不需要两个声明DateTime? result = null;,并DateTime dt;在所有的-你可以在一行代码做到这一点.(如果您愿意,也可以写,out DateTime dt而不是写out var dt.)

我通过使用params关键字进一步简化了代码:现在您不再需要第二个重载方法了.


用法示例

var dtStr="2011-03-21 13:26";    
var dt=dtStr.ToDate("yyyy-MM-dd HH:mm");
if (dt.HasValue)
{
    Console.WriteLine("Successful!");
    // ... dt.Value now contains the converted DateTime ...
}
else
{
    Console.WriteLine("Invalid date format!");
}
Run Code Online (Sandbox Code Playgroud)

如您所见,此示例仅查询dt.HasValue转换是否成功.作为额外的奖励,TryParseExact允许指定strict,DateTimeStyles因此您确切地知道是否已经传递了正确的日期/时间字符串.


更多用法示例

重载功能可以传递一个的有效格式阵列用于解析/转换的日期,如图这里以及(TryParseExact直接支持此),例如

string[] dateFmt = {"M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm tt", 
                     "MM/dd/yyyy hh:mm:ss", "M/d/yyyy h:mm:ss", 
                     "M/d/yyyy hh:mm tt", "M/d/yyyy hh tt", 
                     "M/d/yyyy h:mm", "M/d/yyyy h:mm", 
                     "MM/dd/yyyy hh:mm", "M/dd/yyyy hh:mm"};
var dtStr="5/1/2009 6:32 PM"; 
var dt=dtStr.ToDate(dateFmt);
Run Code Online (Sandbox Code Playgroud)

如果您只有几个模板模式,您还可以写:

var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
Run Code Online (Sandbox Code Playgroud)

高级示例

您可以使用??运算符默认为故障安全格式,例如

var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");
Run Code Online (Sandbox Code Playgroud)

在这种情况下,.ToDate()将使用常见的本地文化日期格式,如果所有这些都失败,它将尝试使用ISO标准格式"yyyy-MM-dd HH:mm:ss"作为后备.这样,扩展功能允许轻松地"链接"不同的后备格式.

你甚至可以在LINQ中使用扩展,试试这个(它在上面的.NetFiddle中):

var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump(); 
Run Code Online (Sandbox Code Playgroud)

它将使用模式动态转换数组中的日期并将其转储到控制台.


关于TryParseExact的一些背景知识

最后,这里有一些关于背景的评论(即我之所以这样写的原因):

我更倾向于在这个扩展方法中使用TryParseExact,因为你避免了异常处理 - 你可以在Eric Lippert的文章中读到关于为什么你应该使用TryParse而不是Parse的异常,我引用他关于那个主题:2)

这个 不幸的设计决定1) [注释:让Parse方法抛出一个异常]是如此令人烦恼,当然 框架团队很快就实现了TryParse, 这是正确的.

确实如此,但TryParseTryParseExact两者还是有很多不足,使用舒适:他们强迫你使用一个未初始化的变量作为out,绝不能可为空并且当你转换,你需要评估布尔返回值参数-要么你有if立即使用一个语句,或者你必须将返回值存储在一个额外的布尔变量中,以便以后能够进行检查.并且您不能仅在不知道转换是否成功的情况下使用目标变量.

在大多数情况下,您只是想知道转换是否成功(当然还有成功的价值),因此保留所有信息的可空目标变量将是可取的并且更加优雅 - 因为整个信息都是只存储在一个地方:这是一致的,易于使用,更不容易出错.

我编写的扩展方法正是这样做的(它还向您展示了如果您不打算使用它,每次都必须编写哪种代码).

我相信它的好处.ToDate(strDateFormat)是它看起来很简单和干净 - 就像原版本DateTime.Parse应该一样简单- 但能够检查转换是否成功,并且没有抛出异常.


1)这里的意思是异常处理(即try { ... } catch(Exception ex) { ...}块) - 当你使用Parse时它是必需的,因为如果解析了无效的字符串会抛出异常 - 在这种情况下不仅是不必要的,而且也很烦人,使您的代码复杂化.TryParse避免了所有这些,因为我提供的代码示例正在显示.


2) Eric Lippert是着名的StackOverflow研究员,并在Microsoft担任C#编译器团队的主要开发人员几年.


Rob*_*Rob 13

var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);
Run Code Online (Sandbox Code Playgroud)

查看此链接以获取其他格式字符串!


cac*_*ois 5

DateTime.Parse()应该适用于该字符串格式.参考:

http://msdn.microsoft.com/en-us/library/1k1skd40.aspx#Y1240

它为你抛出一个FormatException吗?


Zac*_*son 5

使用如下代码将人类可读字符串的值放入 .NET DateTime 中:

DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);
Run Code Online (Sandbox Code Playgroud)