【问题标题】:How can I parse dates with a suffix "th", "st" or "nd" on the day of the month?如何解析带有后缀“th”、“st”或“nd”的日期?
【发布时间】:2013-01-29 12:47:55
【问题描述】:

我在使用 DateTime.Parse 时遇到问题

我正在处理各种格式的日期,其中一些格式为January 11thFebruary 22nd 等等。

DateTime.Parse 在尝试解析此类日期时抛出异常。

我想知道 DateTime 中是否有我缺少的内置功能,例如我可以设置的标志,它会使 Parse 以更可接受的方式运行。

我知道这可以用一个相对简单的正则表达式来解决,而且我已经有一个模糊匹配我写的日期的类,但是我想知道是否有内置的方法来执行这种提取因为从长远来看,它可能比重新发明轮子更容易维护。

【问题讨论】:

  • 您从哪里获得这些日期(数据库、用户输入...)?
  • 我认为你将不得不使用 Regex
  • @NDraskovic 互联网上大约 100 个站点

标签: c# datetime


【解决方案1】:

.Net 框架中没有内置任何东西来解析January 11th or February 22nd 等格式的日期。您必须删除后缀字符,然后才能使用DateTime.TryParseExact

对于后缀为stth 的日期,您可以使用string.Replace 删除该部分,然后使用DateTime.TryParseExact。像。

string str = "1st February 2013";
DateTime dtObject;
string replacedStr =  str.Substring(0,4)
                         .Replace("nd","")
                         .Replace("th","")
                         .Replace("rd","")
                         .Replace("st","")
                         + str.Substring(4);


if (DateTime.TryParseExact(replacedStr, 
                            "dd MMMMM yyyy", 
                            CultureInfo.InstalledUICulture, 
                            DateTimeStyles.None, 
                            out dtObject))
{
 //valid date
}

对于多种格式,您可以在字符串数组中指定格式,稍后您可以使用它。它返回一个bool 值,指示解析是否成功。

来自 MSDN 的示例:

string[] formats= {"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"};
string[] dateStrings = {"5/1/2009 6:32 PM", "05/01/2009 6:32:05 PM", 
                        "5/1/2009 6:32:00", "05/01/2009 06:32", 
                        "05/01/2009 06:32:00 PM", "05/01/2009 06:32:00"}; 
DateTime dateValue;

foreach (string dateString in dateStrings)
{
   if (DateTime.TryParseExact(dateString, formats, 
                              new CultureInfo("en-US"), 
                              DateTimeStyles.None, 
                              out dateValue))
      Console.WriteLine("Converted '{0}' to {1}.", dateString, dateValue);
   else
      Console.WriteLine("Unable to convert '{0}' to a date.", dateString);

【讨论】:

  • +1 谢谢,您能否详细说明这将如何解决我的问题? (例如,1 月 22 日或 2 月 11 日或 3 月 3 日?我真的希望有一个更模糊的解决方案(无需明确指定格式)
  • @Habib 对,但如果我删除这些字符串,我可以使用 DateTime.Parse 并完全避免 TryParseExact。我希望有一个更宽松的 Parse 版本,2010 年 1 月 11 日对我来说似乎是一个非常简单的日期
  • @Ramhound 有没有办法将正则表达式作为格式或类似的东西传递?
  • @Habib 感谢您的时间和精力。这很有帮助,但它不能解决我的问题,对不起。如果您编辑您的问题以反映情况(没有内置方法来解析这些日期),我会接受它。
  • “2013 年 8 月 22 日”将不起作用,因为 8 月的“st”被删除。最后一个 sub str 假定长度为 4,但确实需要匹配长度(在本例中为 5)。我认为您需要使用正则表达式来进行字符串操作,然后再将其传递给 DateTime.Parse ,如@ma1169 的答案
【解决方案2】:

这是一个非常古老的问题,但是对于仍在研究复杂自然语言日期解析的任何人,我建议使用 nChronic,这是令人惊叹的(基于 ruby​​ 的)慢性日期解析器的 .NET 端口。

它的来源在这里: nChronic Github

它也在 Nuget 中作为慢性:Chronic in Nuget

使用这个库的一些非常简单的示例代码如下:

using Chronic;
var parser = new Chronic.Parser ();
Span ParseObj;
DateTime ParsedDateTime;
ParseObj = parser.Parse ("January 11th");
ParsedDateTime = ParseObj.Start;

以下是它可以处理的一些示例:

简单

  • 星期四
  • 十一月
  • 夏天
  • 星期五
  • 13:00
  • 周一 2:35
  • 下午 4 点
  • 10 到 8
  • 10 点 2 分
  • 2点半
  • 早上六点
  • 星期五下午 1 点
  • 晚上 7 点
  • 昨天
  • 今天
  • 明天
  • 上周
  • 下周
  • 这个星期二
  • 下个月
  • 去年冬天
  • 今天早上
  • 昨晚
  • 这一秒
  • 昨天 4:00
  • 上周五 20:00
  • 上周星期二
  • 明天下午 6:45
  • 下午
  • 昨天
  • 上周的星期四

复杂

  • 3 年前
  • 一年前
  • 5 个月前
  • 7 小时前
  • 7 天后
  • 1 周后
  • 3 小时内
  • 1 年前的明天
  • 3 个月前的星期六下午 5:00
  • 明天中午前7小时
  • 11 月的第三个星期三
  • 明年第三个月
  • 今年 9 月的第 3 个星期四
  • 上周第四天
  • 2010 年 6 月 14 日晚上十一点
  • 97 年 5 月 7 日凌晨三点

具体日期

  • 1 月 5 日
  • 6 月 22 日
  • 2017 年 5 月 5 日
  • 二月二十一日
  • 12 月 25 日
  • 5 月 27 日
  • 2006 年 10 月
  • 10 月 6 日
  • 2010 年 1 月 3 日
  • 2004 年 2 月 14 日
  • 2004 年 2 月 14 日
  • 2000 年 1 月 3 日
  • 85 年 4 月 17 日
  • 1979 年 5 月 27 日
  • 27/5/1979
  • 05/06
  • 1979-05-27
  • 星期五
  • 5
  • 4:00
  • 17:00
  • 0800

具体时间(以上很多时间都有附加时间)

  • 1 月 5 日晚上 7 点
  • 6 月 22 日上午 8 点
  • 1979-05-27 05:00:00
  • 03/01/2012 07:25:09.234567

【讨论】:

    【解决方案3】:

    我有类似的问题,这里有更好的方法

      stringdate="August 19th 2000" 
      string pattern = @"\b(\d+)(?:st|nd|rd|th)\b";
      Regex rgx = new Regex(pattern);
      DateTime.Parse(String.Format("{0:MMMM,  d, yyyy}", rgx.Replace(stringdate, "$1"))  
      **result**    {19/08/2000 00:00:00}   System.DateTime
    

    来自Microsoftregex to remove ordinalsHow can I visualize the way various DateTime formats will display?

    编辑

    如果没有指定年份:

        stringdate= rgx.Replace(stringdate, "$1");
        DateTime datetime;
        if (!DateTime.TryParseExact(stringdate, "MMMM dd yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None, out datetime))
        {
         // assuming no gap exist
         datetime = DateTime.Parse(stringdate += " "+DateTime.Now.Year);
        }
    

    现在如果输入的字符串文本是"June 11th",它将是11/6/2021

    DateTime Documentation 有更多处理日期的函数和方法。

    如果您根本不想要年份,则可以添加以下行:

    datetime.ToString("MM/dd");
    

    现在输出将是“11/6”

    【讨论】:

    • 这很好,例如,它不会在 8 月得到误报。
    • String.Format 部分的意义何在?正则表达式将删除序数,但 String.Format 期待一个日期并传递一个字符串,所以除非我遗漏了什么,否则它永远不会改变任何东西?当我希望它是 -06-11 时,“June 11th”也被解析为 2011-06-01
    • 您好@BrianHeward,“2011 年 6 月 11 日”的解析在 2011 年 11 月 6 日在我的机器上按预期工作!请仔细检查您自己的代码,至于 String.Format(),它与正则表达式无关,它与重载解析函数之一有关,它是为了展示如何设置自己的格式以防有人需要使用自定义日期时显式设置解析格式。因此,如果您使用标准格式,则可以跳过此选项
    • 我传入的字符串不是“2011 年 6 月 11 日”。我正在尝试解析“6 月 11 日”,输入中未指定年份,所以我希望它被解析为当前年份。相反,第 11 天(月份部分的日期)被解析为年份,而日期被假定为 1。
    • 我通过总是添加年份来解决这个问题,如果失败,则解析它而不添加年份(因为“2021 年 6 月 11 日 2021 年”不会解析。)
    猜你喜欢
    • 2014-02-25
    • 2017-01-07
    • 2011-04-08
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 2015-10-11
    • 1970-01-01
    相关资源
    最近更新 更多