使用已知但未给定的时区解析DateTime

Phi*_*ier 6 c# timezone datetime parsing windows-phone-7

我在解析日期和时间时遇到了一个问题:

我正在尝试解析从德国网站提取的日期时间字符串.它的格式为'day.month.year 24hours:minutes',如:

01.01.2011 17:00
Run Code Online (Sandbox Code Playgroud)

它总是在德国时区.但问题出现了:

  • '01 .01.2011 17:00'应解析为UTC的'01 .01.2011 16:00'的DateTime结构(此处,时区为CET,没有夏令时)
  • 而01 .06.2011 17:00'应该解释为与01 .01.2011 15:00'一个DateTime结构在UTC(这里,时区是CEST,与夏令时)

我不知道如何实现这一目标.如果我将本地时钟设置为德国时区,并使用DateTime.ParseExact和解析标志DateTimeStyles.AssumeLocal并DateTimeStyles.AdjustToUniversal正确解析它.但是,我希望任何客户端独立于本地时钟和时区解析它.另外,我不想自己做时区偏移,因为它取决于日期(夏天:-2 /冬季:-1).

一旦我有UTC的日期时间,很容易将其转换为任何本地时区.

Jon*_*eet 6

听起来您知道应该用哪个时区来解析它。假设.NET 3.5(因此TimeZoneInfo)你应该逻辑上:

  • 将其解析为“本地”时间(不特定于时区)
  • 将本地时间转换为 UTC 时间

不幸的DateTime是,这有点棘手。编辑:我以为你想使用DateTimeStyles.AssumeUniversal- 但最终会返回一个local DateTime,令人烦恼。基本上你希望在正确的时间 结束,DateTime以便你可以使用:

parsed = DateTime.SpecifyKind(parsed, DateTimeKind.Unspecified);
Run Code Online (Sandbox Code Playgroud)

然后您可以通过以下方式获取 UTC 值:

DateTime utc = TimeZoneInfo.ConvertTimeToUtc(parsed, germanTimeZone);
Run Code Online (Sandbox Code Playgroud)

请注意,您确实首先需要一个“未指定”的日期时间,以便您可以将其转换为任意时区的 UTC。您还应该记住,由于 DST 更改,当地时间可能不明确(出现两次)或不可能(根本不出现)。

是的,当它完成后,在野田时间里这会容易得多:)


Phi*_*ier 1

在看到无法在 WP7/Silverlight 框架的帮助下完成该任务后,我编写了一个完成这项工作的小助手:

public static class DateTimeHelper
{
    /// <summary>
    /// Tries to parse the given datetime string that is not annotated with a timezone 
    /// information but known to be in the CET/CEST zone and returns a DateTime struct
    /// in UTC (so it can be converted to the devices local time). If it could not be 
    /// parsed, result contains the current date/time in UTC.
    /// </summary>
    public static bool TryParseCetCest(string s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result)
    {
        // Parse datetime, knowing it is in CET/CEST timezone. Parse as universal as we fix it afterwards
        if (!DateTime.TryParseExact(s, format, provider, style, out result))
        {
            result = DateTime.UtcNow;
            return false;
        }
        result = DateTime.SpecifyKind(result, DateTimeKind.Utc);

        // The boundaries of the daylight saving time period in CET and CEST (_not_ in UTC!)
        // Both DateTime structs are of kind 'Utc', to be able to compare them with the parsing result
        DateTime DstStart = LastSundayOf(result.Year, 3).AddHours(2);
        DateTime DstEnd = LastSundayOf(result.Year, 10).AddHours(3);

        // Are we inside the daylight saving time period?
        if (DstStart.CompareTo(result) <= 0 && result.CompareTo(DstEnd) < 0)
            result = result.AddHours(-2); // CEST = UTC+2h
        else
            result = result.AddHours(-1); // CET = UTC+1h

        return true;
    }

    /// <summary>
    /// Returns the last sunday of the given month and year in UTC
    /// </summary>
    private static DateTime LastSundayOf(int year, int month)
    {
        DateTime firstOfNextMonth = new DateTime(year, month + 1, 1, 0, 0, 0, DateTimeKind.Utc);
        return firstOfNextMonth.AddDays(firstOfNextMonth.DayOfWeek == DayOfWeek.Sunday ? -7 :
                                                    (-1 * (int)firstOfNextMonth.DayOfWeek));
    }
}
Run Code Online (Sandbox Code Playgroud)

诀窍是在没有DateTimeStyles.AssumeUniversal 标志的情况下解析它(这使得TryParseExact假设日期是 UTC 并返回转换/调整为本地的日期),将其重新指定为 UTC,然后手动将其调整为实际的 UTC 等效值。

它遵循 DST 规则(可在此处找到) 。我在夏令时开始/结束之前/之后对所有 4 个边界情况进行了测试。这再次表明了测试的重要性:我必须将<运算符更改DstStart.CompareTo(result) < 0为<=才能使其产生正确的结果。

我有一种感觉,我在这里重新发明轮子(我讨厌这样做),但不想使用专用的库来完成这个简单的工作。我看过 Noda Time,这是一个很棒的项目,但我认为这没有必要。

我希望通过这个小帮手可以节省一些人的时间。它故意不是对所有时区通用(如果您需要它,请使用像 Noda Time 这样的库),但对于这些只有一个固定的单一时区的情况,就像我的情况一样。