【问题标题】:String to UTC Time conversion字符串到 UTC 时间的转换
【发布时间】:2014-11-24 04:10:39
【问题描述】:

我收到了包含这样日期的邮件存档。

Wed, 17 Dec 1997 13:36:23 +2
Mon, 16 Jun 1997 15:41:52 EST
Tue, 15 Jul 1997 14:37:00 EDT
Tue, 5 Aug 1997 08:37:56 PST
Tue, 5 Aug 1997 15:46:16 PDT
Thu, 5 Mar 1998 08:44:19 MET
Mon, 8 Nov 1999 17:49:25 GMT
Thu, 24 Feb 94 20:06:06 MST
Mon, 19 Dec 2005 14:17:06 CST
Thu, 14 Sep 95 19:15 CDT
Sat, 22 Feb 1997 05:16:55 UT
Mon, 8 Jul 1996 15:48:54 GMT-5
Mon, 25 Nov 1996 17:10:28 WET
Mon, 6 Jan 1997 23:43:48 UT
Fri, 13 Jun 1997 16:44:03 -0400

询问是将这个时间转换为 UTC。这就是我试图做到这一点的方式。

static void Main(string[] args)
{
    var possibleValues = new string[] 
    {
        "Mon, 29 Sep 2014 08:33:35 +0200"
        , "Fri, 29 Jun 2001 07:53:01 -0700"
        ,"Fri, 26 Sep 2014 15:57:04 +0000"
        ,"Wed, 17 Dec 1997 13:36:23 +2"
        , "Fri, 13 Jun 1997 16:44:03 -0400"

        , "Mon, 16 Jun 1997 15:41:52 EST"
        , "Tue, 15 Jul 1997 14:37:00 EDT"
        , "Tue, 5 Aug 1997 08:37:56 PST"
        , "Tue, 5 Aug 1997 15:46:16 PDT"
        , "Thu, 5 Mar 1998 08:44:19 MET"
        , "Mon, 8 Nov 1999 17:49:25 GMT"
        , "Thu, 24 Feb 94 20:06:06 MST"
        , "Mon, 19 Dec 2005 14:17:06 CST"
        , "Thu, 14 Sep 95 19:15:00 CDT"
        , "Sat, 22 Feb 1997 05:16:55 UT"
        , "Mon, 8 Jul 1996 15:48:54 GMT-5"
        , "Mon, 25 Nov 1996 17:10:28 WET"
        , "Mon, 6 Jan 1997 23:43:48 UT"

    };

    foreach (var item in possibleValues)
    {
        var dateParts = item.Split(' ');
        var lastItem = dateParts[dateParts.Length - 1];
        if (lastItem.StartsWith("+") || lastItem.StartsWith("-"))
        {
            try
            {
                DateTimeOffset offset = DateTimeOffset.Parse(item, CultureInfo.InvariantCulture);
                Debug.WriteLine("Input: {0}, UTC Time: {1}", item, offset.UtcDateTime);
            }
            catch (Exception exc)
            {
                Debug.WriteLine("Failed - {0}, Error Message: {1}", item, exc.Message);
            }
        }
        else
        {
            //Sometimes year is a two digit number and sometimes it is 4 digit number.
            string dateFormat = string.Format("ddd, {0} MMM {1} {2}:mm:ss {3}", new string('d', dateParts[1].Length), new string('y', dateParts[3].Length), int.Parse(dateParts[4].Substring(0, 2)) > 12 ? "HH" : "hh", lastItem);     
            try
            {
                DateTimeOffset offset = DateTimeOffset.ParseExact(item, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None);
                Debug.WriteLine("Input: {0}, UTC Time: {1}", item, offset.UtcDateTime);
            }
            catch (Exception exc)
            {
                Debug.WriteLine("Failed - {0}, DateFormat Tried: {1}, Error Message: {2}", item, dateFormat, exc.Message);
            }
        }
    }
}

我无法弄清楚如何处理所有情况。我也愿意使用野田时间。

我已经通过 SO 和 Google 的许多链接找到了这个答案,但无法从这些链接中实现任何答案。如果您知道类似的问题,请告诉我。

我已经浏览了下面的链接。

Convert.ToDateTime Method
Converting between types
daylight-saving-time-and-time-zone-best-practices
SO Tags timezone
Coding Best Practices Using DateTime in the .NET Framework
conversion-of-a-utc-date-time-string-in-c-sharp

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • @JohnSaunders,谢谢,我会记住这一点。
  • 这些字符串似乎大多符合 RFC 822/1123,但时区缩写“WET”和“MET”除外。此外,“GMT-5”和“+2”形式的偏移量不符合规范,因为该格式需要 +0100 之类的值。
  • @MattJohnson 根据www.worldtimezone.com/wtz-names/wtz-met,MET 是中欧时间 (UTC+01)。
  • @ndd 您可以查找非标准时区缩写并将它们转换为标准时区缩写。但是,请注意“CST”的问题,正如 Jon Skeet 在 20 分钟后在 Jon Skeet and Tony the Pony(Vimeo 视频)中所解释的那样。您可以通过检查电子邮件是否来自 .com 或 .au 地址来部分解决该问题.

标签: c# datetime timezone


【解决方案1】:

这些日期似乎大多符合经RFC 1123 §5.2.14 修订的RFC 822 §5.1

但是,指定的几个时区不符合规定。

  • “WET”通常为 +0000
  • “MET”很少见,但在here 显示为+0100。
  • “GMT-5”应写为“-0500”
  • “+2”应该写成“+0200”

该格式仅提供以下定义:

  • “UT”/“GMT”=+0100
  • “EDT”=-0400
  • “EST”/“CDT”=-0500
  • “CST”/“MDT”=-0600
  • “MST”/“PDT”=-0700
  • "PST" = -0800

请注意,在正常情况下,任何时区缩写可能会不明确。例如,“CST”有 5 种不同的含义,如您所见in this list。只有在这种特定格式中,缩写才有特定的上下文。换句话说,虽然“CST”是中国标准时间的有效缩写,但您永远不会在 RFC822/1123 格式的值中使用 CST。相反,您将使用“+0800”。

现在在 .NET 中,RFC822/1123 格式被 "R" standard format specifier 覆盖。通常,您可以使用 "R" 说明符调用 DateTimeOffset.ParseExactDateTime.ParseExact。但是,您将无法在此处使用它,因为它无法识别除“GMT”以外的任何时区缩写,也不适用于偏移量或两位数年份。

但是,非精确解析器(DateTimeOffset.ParseDateTime.Parse)似乎确实可以识别大部分重要位,我们可以利用这一点。您必须进行一些预处理以分配可以识别的时区偏移量。

private static readonly Dictionary<string,string> TZMap = new Dictionary<string, string>
{
    // Defined by RFC822, but not known to .NET
    {"UT", "+0000"},
    {"EST", "-0500"},
    {"EDT", "-0400"},
    {"CST", "-0600"},
    {"CDT", "-0500"},
    {"MST", "-0700"},
    {"MDT", "-0600"},
    {"PST", "-0800"},
    {"PDT", "-0700"},

    // Extraneous, as found in your data
    {"WET", "+0000"},
    {"MET", "+0100"}
};

public static DateTimeOffset Parse(string s)
{
    // Get the time zone part of the string
    var tz = s.Substring(s.LastIndexOf(' ') + 1);

    // Replace time zones defined in the map
    if (TZMap.ContainsKey(tz))
    {
        s = s.Substring(0, s.Length - tz.Length) + TZMap[tz];
    }

    // Replace time zone offsets with leading characters
    if (tz.StartsWith("GMT+") || tz.StartsWith("GMT-") || tz.StartsWith("UTC+") || tz.StartsWith("UTC-"))
    {
        s = s.Substring(0, s.Length - tz.Length) + tz.Substring(3);
    }
        
    DateTimeOffset dto;
    if (DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out dto))
    {
        return dto;
    }

    throw new ArgumentException("Could not parse value: " + s);
}

这会传递您提供的所有示例值,但是您可能会发现需要添加到地图中的更多无关值。在确定所有边缘情况之前,可能需要多次遍历您的数据。

当然,既然你在这里得到一个DateTimeOffset,如果你想要UTC值,你可以使用.UtcDateTime.ToUniversalTime()

【讨论】:

  • @mattjohson 在运行上面的代码后我注意到我得到了不同的结果,我可能有错误,但我在发布问题之前仔细检查了我的答案,所以你能帮忙验证答案吗? i.stack.imgur.com/2o6E1.png
  • 这两个输入值在我看来是一样的。它们应该产生相同的输出。我在这里给出的方法应该给出一个带有 8:33:35 和 +02:00 偏移量的DateTimeOffset。致电.UtcDateTime 将给出 6:33:35。我不知道你是怎么得到 12:33:35 的。
  • 哦,我现在明白了,您正在将您的原始代码与我的答案进行比较。您的原始代码有缺陷,因为您在 DateTime 实例上调用 .ToUniversalTime()。这将假定该值位于您正在运行的计算机的本地时区。你不想那样做。您可以在 DateTimeOffset 实例上调用它,但不能在 DateTime 上调用。错误在于offset.DateTime.ToUniversalTime()。它应该只是offset.ToUniversalTime()
  • 好的,你知道如何进一步填充字典吗?我正在解析 1993 年的档案,所以如果我必须手动执行此操作,那会杀了我。
  • 另外,(nitpick)我看到您将代码编辑为.ToUniversalTime().DateTime - 这也与调用.UtcDateTime 相同。
猜你喜欢
  • 1970-01-01
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-24
  • 2016-12-12
  • 1970-01-01
相关资源
最近更新 更多