c#daylight savings重复小时转换为UTC

ama*_*y11 15 c# time dst

我正在使用TimeZoneInfo在客户端wallclock'Eastern Time'和UTC之间进行转换.我的问题是在秋季DST更改期间发生的"重复"小时.

从UTC到Eastern的转换期间:
2010-11-07 06:00 UTC - >给出2010-11-07T 01:00:00-03:30
2010-11-07 07:00 UTC - >给出2010-11 -07T 01:00:00-03:30
我怎么知道哪个是第一个小时,哪个是第二个小时?DateTime.IsDaylightSavingTime()在两个小时都返回false,但是它不应该在第一个小时返回true吗?

同样,我如何存储2010-11-07 01:00:00 -03:30?如何将我的应用程序转换为UTC,因为它可能是2010-11-07 06:00或2010-11-07 07:00

对于那些需要代码的人,我骑自行车穿过一个带有UTC日期时间列的数据表,尝试使用'DupHr'列转换为东部第二个重复小时,但我总是在01:00时结束'DupHr'= 1.

    TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

    DateTime EasternTime;
    DateTime DuplicateHour = new DateTime(2010, 11, 7, 1, 0, 0);  // hard coded for this example
    TimeZoneInfo.AdjustmentRule[] rules =  est.GetAdjustmentRules();

    foreach (DataRow row in dt.Rows)
    {
        row["DupHr"] = 0;  // by default not duplicate hour
        EasternTime = TimeZoneInfo.ConvertTimeFromUtc((DateTime)row[UTCColumnName], est);
        if (!EasternTime.IsDaylightSavingTime())
        {
            if (EasternTime.Equals(DuplicateHour ))
            {
                row["DupHr"] = 1;   // This is the second duplicate hour !
            }
        } else

            EasternTime.Add(rules[1].DaylightDelta);  // Add DST offset from rule #1

        row[newESTColumnName] = EasternTime;        
    }
Run Code Online (Sandbox Code Playgroud)

谢谢!

重要的是要知道重复小时的"模棱两可".小时必须是唯一的(第一和第二).考虑一个收费站货币柜台应用程序,它必须全天候运行并累计每小时的收集.每小时收集的钱必须是可识别的.第一小时1:00至1:59与第二小时1:00至1:59小时不同.以下例程isSecondHour仅在通过时间为秋季DST变化的第二小时时才返回true .用户界面可以适当地显示该标志.

 // Get the DST rule for the year and zone  (rules may change from year to year as in 2004)
    public static TimeZoneInfo.AdjustmentRule GetDSTrule(int Year, TimeZoneInfo zone)
    {
        TimeZoneInfo.AdjustmentRule[] rules = zone.GetAdjustmentRules();

        foreach (TimeZoneInfo.AdjustmentRule rul in rules)
        {
            if (rul.DateStart < new DateTime(Year, 1, 1) && rul.DateEnd > new DateTime(Year, 1, 1))
            {
                return rul;
            }
        }
        return null;
    }

    // Determine if 'localtime' is in the second duplicate DST hour.
    public static Boolean isSecondHour(TimeZoneInfo localzone, DateTime localtime, DateTime UTCtime)
    {

        if (localzone.IsAmbiguousTime(localtime))
        {
            TimeZoneInfo.AdjustmentRule rul = GetDSTrule(localtime.Year, localzone);
            return UTCtime.Add(localzone.GetUtcOffset(localtime)) == localtime;
        }
        else
            return false;
    }


    static void Main(string[] args)
    {
        var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

        var times = new DateTime[] {
        new DateTime (2010, 11, 7, 3,  0, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 4,  0, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 5,  0, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 5,  30, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 6,  0, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 6,  30, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 7,  0, 0, DateTimeKind.Unspecified),
        new DateTime (2010, 11, 7, 8,  0, 0, DateTimeKind.Unspecified)

    };

    DateTime EasternTime;
    Console.WriteLine("UTC Time  |  Est Time   | IsDaylightSaving | IsAmbiguousTime | isSecondHour ");

    foreach (var utc in times)
    {
        // Get Eastern Time from UTC using standard convert routine.
        EasternTime = TimeZoneInfo.ConvertTimeFromUtc(utc, est);
        Console.WriteLine("{0:HH:mm}     |   {1:HH:mm}     | {2,11}      |      {3,5}      |      {4,5}", utc,EasternTime, est.IsDaylightSavingTime(EasternTime), est.IsAmbiguousTime(EasternTime),isSecondHour(est,EasternTime, utc));
     }
Run Code Online (Sandbox Code Playgroud)

结果

UTC Time  |  Est Time   | IsDaylightSaving | IsAmbiguousTime | isSecondHour
03:00     |   23:00     |        True      |      False      |      False
04:00     |   00:00     |        True      |      False      |      False
05:00     |   01:00     |       False      |       True      |      False
05:30     |   01:30     |       False      |       True      |      False
06:00     |   01:00     |       False      |       True      |       True
06:30     |   01:30     |       False      |       True      |       True
07:00     |   02:00     |       False      |      False      |      False
08:00     |   03:00     |       False      |      False      |      False
Run Code Online (Sandbox Code Playgroud)

Chr*_*ers 11

摘要

你无法知道,因为你没有存储偏移,你丢失了一条重要的信息,即时间最初所在的时区,正如你所指出的那样,可能是东部标准时间或东部夏令时.

检测模糊的时间

TimeZoneInfo提供方法IsAmbiguousTime来检查是否可能出现这种情况.

您检测到这个模糊时间的问题是您正在尝试使用IsDaylightSavings哪个在模糊时间返回false,如此示例所示:

var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

var times = new DateTime[] {
    new DateTime (2010, 11, 7, 4,  0, 0, DateTimeKind.Utc),
    new DateTime (2010, 11, 7, 5,  0, 0, DateTimeKind.Utc),
    new DateTime (2010, 11, 7, 5, 30, 0, DateTimeKind.Utc),
    new DateTime (2010, 11, 7, 6,  0, 0, DateTimeKind.Utc),
};

Console.WriteLine("     Time  | IsDaylightSaving | IsAmbiguousTime");
foreach (var t in times) {
    var time = TimeZoneInfo.ConvertTimeFromUtc(t, est);
    Console.WriteLine ("    {0:HH:mm}  | {1,11}      |      {2,5}", time, est.IsDaylightSavingTime(time), est.IsAmbiguousTime(time));
}
Run Code Online (Sandbox Code Playgroud)

结果:

 Time  | IsDaylightSaving | IsAmbiguousTime
00:00  |        True      |      False
01:00  |       False      |       True
01:30  |       False      |       True
01:00  |       False      |       True
Run Code Online (Sandbox Code Playgroud)

所以你想要使用est.IsAmbiguousTime(EasternTime).然后没有必要,DuplicateHour因为这将覆盖当天模糊的全部时间范围.DateTimeOffset由于显式存储偏移量而不会遇到此问题.

将EST转换为UTC并存储在数据库中

对于从EST到UTC的初始转换,数据库中的现有数据将要存储偏移量以供将来使用.对于非模糊时间,可以从时区检索.但是,正如您所指出的那样,由于模糊不清,这些信息将无法使用.对于这些时间,您将不得不假设使用哪个偏移量并将数据库中的时间标记为可疑,以便UI在显示这些时间时做出相应的反应.

根据受影响的数据量,可能不值得更改UI并简单地忽略问题,特别是如果时间超过一小时对用户来说真的不重要(因为在用户的屏幕在那个时区它仍将显示为凌晨1点).如果你以后改变主意,数据库仍然会记录时间是可疑的.

从UTC转换为EST并检测模糊时间

首先,使用DateTimeOffset,因为这可以区分美国东部时间凌晨1点和美国东部标准时间凌晨1点之间的区别.此时TimeZoneInfo.IsAmbiguousTime(DateTimeOffset)可用于突出显示屏幕上的重复次数,并且TimeZoneInfo.IsDaylightSavings(DateTimeOffset)还将正确返回true或false.

var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

var times = new DateTimeOffset[] {
    new DateTimeOffset (2010, 11, 7, 4, 00, 0, TimeSpan.Zero),
    new DateTimeOffset (2010, 11, 7, 5, 00, 0, TimeSpan.Zero),
    new DateTimeOffset (2010, 11, 7, 5, 30, 0, TimeSpan.Zero),
    new DateTimeOffset (2010, 11, 7, 6, 00, 0, TimeSpan.Zero),
};

Console.WriteLine("     Time  | IsDaylightSaving | IsAmbiguousTime");
foreach (var t in times) {
    var time = TimeZoneInfo.ConvertTime (t, est);
    Console.WriteLine ("    {0:HH:mm}  | {1,11}      |      {2,5}", time, est.IsDaylightSavingTime(time), est.IsAmbiguousTime(time));
}
Run Code Online (Sandbox Code Playgroud)

结果:

 Time  | IsDaylightSaving | IsAmbiguousTime
00:00  |        True      |      False
01:00  |        True      |       True
01:30  |        True      |       True
01:00  |       False      |       True
Run Code Online (Sandbox Code Playgroud)

未来的考虑因素

用户界面问题

当向用户显示时,本地时间是否模糊(重复小时)无关紧要.您只需将UTC时间转换为其时区并将其格式化为字符串即可.您可能需要检查IsAmbiguousTime以向用户显示提示他们为什么可能两次看到"1am"的提示.按日期对信息进行排序应使用UTC完成.从UTC到本地时间永远不应该是模糊的,因为每个时间点只在UTC中存在一次,没有重复的小时数.

因此,现在唯一的问题是,如果用户输入时间并且您需要解释他们的意思时间,因为用户不太可能输入偏移量或甚至不关心此类详细信息.遗憾的是,没有简单的方法可以解决这个问题,并且没有尝试向用户介绍偏移,他们会犯错并输入错误的时间.例如,他们可能会在午夜4点过后凌晨4点进入,忘记那个晚上还有1小时/更少.或者,当时钟在凌晨3点前进时,它们可以在凌晨3点进入,在那一天是一个根本不存在的时间.

幸运的是,时钟变化的时间旨在最大限度地减少用户输入问题,因为大多数人都在睡觉.所以系统可以采取最好的猜测,并接受有时一小时的外出.如果确实很重要,那么您可以检查当天是否有夏令时并显示带有警告/提示的不同UI.

存储和转移

如果将时间存储在MSSQL服务器中,则应首选datetimeoffset,因为这可以处理存储时间和偏移量.使用此类型时,MSSQL服务器可以正确处理具有不同偏移的时间.

对于不支持此类型的数据库,您可以将时间以UTC格式存储在数据库中,并将该时间的偏移量存储在单独的列中.这样您就可以准确地知道记录的当地时间.

当与外部系统交换时,理想地将时间作为本地格式yyyy-MM-dd HH:mm:sszzzz(例如2010-11-07 01:00:00-03:30)传输,以便可以保留时间和偏移.否则UTC通常是最好的选择,但理想情况下应该以"Z"或"+00:00"为后缀,以使其显而易见.

在内存中,DateTimeOffset类是更好的选择,因为它可以表示与DateTime相比的任何仲裁偏移,而DateTime只能表示UTC或系统的本地时间.


请注意,TimeZoneInfo夏令时的准确性取决于所应用的操作系统版本,Service Pack和Windows更新.

此外,重要的是如何应用日光节约.如果操作系统使用"自动调整夏令时"来应用它们,则可以正确调整偏移.如果管理员已禁用此功能并通过添加/减去一小时来手动调整时间,则操作系统将不会意识到这一点,并且将使用错误的偏移量进行操作.有关此OS设置的其他说明,请参阅TimeZoneInfo.Local.