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


165

我的日期和时间的格式像这样:

"2011-03-21 13:26" //year-month-day hour:minute

我如何解析为System.DateTime

我想使用类似的功能DateTime.Parse()DateTime.ParseExact()如果可能),以便能够手动指定日期的格式。


19
那么,为什么不使用DateTime.Parse?
奥斯丁·萨洛宁

8
我是低俗的人之一。这是因为您最初的问题(stackoverflow.com/revisions/…)指出您想要使用DateTime.Parse(),但未说明为什么无法使用它。这使它看起来像一个胡说八道的问题,特别是因为简单的检查就可以清楚地知道cacois是正确的:您的字符串“ 2011-03-21 13:26”对于DateTime.Parse()来说不是问题。最后,您在原始问题中没有提及ParseExact()。您一直等到Mitch回答后才将其添加到编辑中。
匿名

4
我只是喜欢那些拒绝投票的人,而无需在评论中给出任何理由。
Hooch 2015年

Answers:


271

DateTime.Parse()会尝试找出给定日期的格式,通常效果很好。如果可以保证日期始终为给定格式,则可以使用ParseExact()

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

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

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

构造格式字符串时,请确保检查“ 自定义日期和时间格式字符串”,尤其要注意字母数和大小写(即“ MM”和“ mm”的含义完全不同)。

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


5
更正-总是更安全;)如果您要调用带有异常的方法,请始终首先检查异常条件(如果可能)。
Gusdor

3
我想说,始终传承您的文化会更安全。我宁愿有一个例外,而不是将“ 01-02-2013”​​误解为1月2日或2月1日。
卡拉

1
@Carra:始终以正确的方式解释ISO8601格式的日期(即yyyy-mm-dd'。这就是为什么我们使用ISO8601格式的日期...
Mitch Wheat

精确解析可能会很有用。有时,与产生错误的输出相反,我更希望应用程序崩溃并且计算机着火。取决于应用程序。
艾伦

ParseExact很好,因为它很灵活,但是有一个缺点:请注意,如果variable的日期格式中存在语法错误,则ParseExact和Parse方法会引发异常s。因此,最好使用TryParseExcact。我在下面的回答中指出了原因。
马特

47

正如我稍后解释的那样,我将始终支持TryParseTryParseExact方法。因为它们使用起来有点笨重,所以我写了一个扩展方法,使解析变得更加容易:

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

ParseParseExact等不同,它不会引发异常,并允许您通过检查

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;

在这里,您还可以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;
    }
}

有关代码的一些信息

您可能会奇怪,为什么我使用了InvariantCulturecall TryParseExact:这是强制函数始终以相同的方式处理格式模式(否则,例如,“。”在英语中可以解释为小数点分隔符,而在英语中它是组分隔符日期分隔符)德语)。回想一下,我们已经在几行之前查询了基于文化的格式字符串,所以在这里还可以。

更新:( .ToDate()不带参数)现在默认为线程当前区域性的所有常见日期/时间模式。
请注意,我们需要resultdt,因为TryParseExact不允许使用DateTime?,我们打算将其返回。在C#版本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;

或者,如果您更喜欢它:

 // 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?;

在这种情况下,您根本不需要两个声明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!");
}

如您所见,此示例只是查询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);

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

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

进阶范例

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

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

在这种情况下,.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(); 

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


有关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#编译器团队的首席开发人员工作了两年。


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

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



4

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

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

2

简单直接的答案->

using System;

namespace DemoApp.App

{
public class TestClassDate
{
    public static DateTime GetDate(string string_date)
    {
        DateTime dateValue;
        if (DateTime.TryParse(string_date, out dateValue))
            Console.WriteLine("Converted '{0}' to {1}.", string_date, dateValue);
        else
            Console.WriteLine("Unable to convert '{0}' to a date.", string_date);
        return dateValue;
    }
    public static void Main()
    {
        string inString = "05/01/2009 06:32:00";
        GetDate(inString);
    }
}
}

/**
 * Output:
 * Converted '05/01/2009 06:32:00' to 5/1/2009 6:32:00 AM.
 * */

尼斯@Shivam Bharadwaj,我也是这样做的
Muhammad Irfan


0

试试下面的代码

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;

嗨,欢迎光临,回答问题时请提供解释。不建议只发布代码
阿里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.