【问题标题】:Parse string to DateTime in C#在 C# 中将字符串解析为 DateTime
【发布时间】:2011-07-18 23:41:01
【问题描述】:

我有 日期和时间,格式如下:

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

如何将其解析为System.DateTime

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

【问题讨论】:

  • 那你为什么不使用 DateTime.Parse?
  • 我是反对者之一。这是因为您最初的问题 (stackoverflow.com/revisions/…) 表示您想使用 DateTime.Parse() 但您没有说明为什么不能使用它。这使它看起来像是一个无意义的问题,特别是因为一个简单的检查就可以清楚地表明 cacois 是正确的:您的字符串“2011-03-21 13:26”对于 DateTime.Parse() 来说不是问题。最后,您在原始问题中没有提及 ParseExact() 。您等到 Mitch 的回答之后将其添加到编辑中。
  • 我只是喜欢那些在 cmets 中没有给出任何理由而拒绝投票的问题。

标签: c# .net string parsing datetime


【解决方案1】:

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

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

或者更简单地说,如果您想隐式使用当前文化的日期模式,您可以像这样使用它:

 DateTime? dt = dtStr.ToDate();

在这种情况下,不需要指定特定的模式。

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

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

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

这甚至允许使用优雅的快捷方式,例如“Elvis”运算符?.,例如:

int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;

在这里您还可以使用year.HasValue 来检查转换是否成功,如果不成功则year 将包含null,否则日期的年份部分。转换失败不抛出异常。


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

Try it in .NetFiddle

public static class Extensions
{
  /// Extension method parsing a date string to a DateTime? <para/>
  /// <summary>
  /// </summary>
  /// <param name="dateTimeStr">The date string to parse</param>
  /// <param name="dateFmt">dateFmt is optional and allows to pass 
  /// a parsing pattern array or one or more patterns passed 
  /// as string parameters</param>
  /// <returns>Parsed DateTime or null</returns>
  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();
    }
    var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
                   style, out var dt) ? dt : null as DateTime?;
    return result;
  }
}

关于代码的一些信息

您可能想知道,为什么我使用InvariantCulture 调用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)。

旧的 C# 风格需要以下方式(我从上面的代码中删除了它):

  // DateTime? result = null;
  // DateTime dt;
  // if (DateTime.TryParseExact(dateTimeStr, dateFmt,
  //    CultureInfo.InvariantCulture, style, out dt)) result = dt;

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


使用示例

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 允许指定严格的DateTimeStyles,这样您就可以准确地知道是否传递了正确的日期/时间字符串。


更多使用示例

重载函数允许您传递一个有效格式数组,用于解析/转换日期,如here 所示(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 standard 格式 "yyyy-MM-dd HH:mm:ss" 作为后备。这样,扩展功能可以轻松“链接”不同的后备格式。

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

var strDateArray = new[] { "15-01-2019", "15.01.2021" };
var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
var dtRange = strDateArray.Select(s => s.ToDate(patterns));
dtRange.Dump(); 

这将通过使用模式即时转换数组中的日期并将它们转储到控制台。


关于 TryParseExact 的一些背景知识

最后,这里有一些关于背景的cmets(即我这样写的原因):

我更喜欢 TryParseExact 在这个扩展方法中,因为你避免异常处理 - 你可以read in Eric Lippert's article about exceptions 为什么你应该使用 TryParse 而不是 Parse,我引用他该主题:2)

这个不幸的设计决定1) [注释:to 让 Parse 方法抛出异常] 这当然令人烦恼 框架团队此后不久实施了 TryParse,它做了正确的事情。

确实如此,但 TryParseTryParseExact 仍然难以使用:它们强制您使用未初始化的变量作为 out 参数,该参数不能为空,并且在您转换时您需要评估布尔返回值 - 您必须立即使用 ifstatement,或者必须将返回值存储在附加的布尔变量中,以便稍后进行检查。而且你不能只使用目标变量而不知道转换是否成功。

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

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

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


1) 这里的意思是异常处理(即try { ... } catch(Exception ex) { ...} 块) - 当您使用 Parse 时这是必要的,因为它如果解析无效字符串,将引发异常 - 在这种情况下不仅没有必要,而且令人讨厌,并使您的代码复杂化。 TryParse 避免了这一切,正如我提供的代码示例所示。


2) Eric Lippert 是著名的StackOverflow fellow,曾在 Microsoft 担任 C# 编译器团队的首席开发人员几年。

【讨论】:

  • 如果我需要解析格式为“dd/MM/yyyy”的日期,然后还要解析格式为“MM/dd/yyyy”的日期怎么办?例如,我有一个日期“07/09/2021”,我需要能够正确解析它。 B-)
  • 在这种情况下,您的日期不明确,并且没有任何解析函数能够预测您的意思:2021 年 9 月 7 日或 7 月 9 日?在这种情况下,唯一的方法是让用户选择他/她想要使用的文化,然后应用正确的模式。或者您使用用户必须指定月份的模式,例如“September”或“July”,即“dd/MMMM/yyyy”。 @詹姆斯
【解决方案2】:

试试下面的代码

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

【讨论】:

  • 您好,欢迎您,请在回答问题时提供解释。不建议只发布代码
  • 虽然 GetMonthName 是一个有用的方法,但在 .NET 中不应该这样做——框架为此目的提供了所有日期字符串格式选项。您甚至可以提供自己的格式。如果 DateTime 或 DateTimeOffset 对象可用,则应将此方法视为不必要的 hack。
【解决方案3】:

您也可以使用 XmlConvert.ToDateString

var dateStr = "2011-03-21 13:26";
var parsedDate = XmlConvert.ToDateTime(dateStr, "yyyy-MM-dd hh:mm");

最好指定日期种类,代码为:

var anotherParsedDate = DateTime.ParseExact(dateStr, "yyyy-MM-dd hh:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);

更多关于不同解析选项的细节http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

【讨论】:

    【解决方案4】:

    简单直接的答案 -->

    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,我也是这样做的
    【解决方案5】:

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

    string s = "2011-03-21 13:26";
    
    DateTime dt = 
        DateTime.ParseExact(s, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
    

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

    在构造格式字符串时一定要检查Custom Date and Time Format Strings,尤其要注意字母的个数和大小写(即“MM”和“mm”的意思很不一样)。

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

    【讨论】:

    • 更正 - 它总是更安全 ;) 如果您正在调用带有异常的方法,请始终尽可能先检查异常条件。
    • 我想说总是传递你的文化更安全。我宁愿有一个例外,也不愿将“01-02-2013”​​误解为 1 月 2 日或 2 月 1 日。
    • @Carra:ISO8601 格式的日期(即 yyyy-mm-dd' 总是以正确的方式解释。这就是我们使用 ISO8601 格式日期的原因...
    • 精确解析很有用。有时,我宁愿我的应用程序崩溃和我的电脑着火,而不是产生不正确的输出。取决于应用程序。
    • ParseExact 很棒,因为它很灵活,但它有一个缺点:请注意,如果变量 @ 的日期格式存在语法错误,ParseExact 和 Parse 方法会抛出异常987654326@。因此,最好使用 TryParseExcact。 我已经在下面的回答中指出了原因。
    【解决方案6】:
    var dateStr = @"2011-03-21 13:26";
    var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);
    

    查看link 了解其他格式字符串!

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

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

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

        它会为你抛出 FormatException 吗?

        【讨论】:

          猜你喜欢
          • 2011-11-26
          • 2013-04-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-30
          • 2013-11-21
          • 2020-09-27
          相关资源
          最近更新 更多