c#夏令时重复小时转换为UTC

c#夏令时重复小时转换为UTC,c#,time,dst,C#,Time,Dst,我使用TimeZoneInfo在客户端挂钟“东部时间”和UTC之间转换。我的问题是秋季DST更改期间发生的“重复”小时。 从UTC转换为东部时: 2010-11-07 06:00 UTC-->给出2010-11-07T01:00:00-03:30 2010-11-07 07:00 UTC-->给出2010-11-07T01:00:00-03:30 我怎么知道哪个是第一个小时,哪个是第二个小时?DateTime.IsDaylightSavingTime()在两个小时内都返回false,但在第一个

我使用TimeZoneInfo在客户端挂钟“东部时间”和UTC之间转换。我的问题是秋季DST更改期间发生的“重复”小时。 从UTC转换为东部时:
2010-11-07 06:00 UTC-->给出2010-11-07T01:00:00-03:30
2010-11-07 07:00 UTC-->给出2010-11-07T01: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:00

对于那些需要代码的人,我在一个包含UTC datetime列的数据表中循环,试图在第二个重复的小时内转换为包含“DupHr”列的Eastern,但我总是在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;        
    }
谢谢

解决方案 对于重复的工作时间,重要的是了解更多信息,而不是“模棱两可”。时间必须是唯一的(第一个和第二个)。考虑一个收费亭货币柜台申请,必须运行24x7,并累计每小时的集合。每小时收取的款项必须是可识别的。第一个小时1:00到1:59与第二个小时1:00到1:59不同。仅当经过的时间在秋季DST更改的第二个小时内时,下面的isSecondHour例程才会返回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));
     }
总结 你不知道,因为不存储偏移量,你丢失了一条重要的信息,时间最初所在的时区,正如你所指出的,可能是东部标准时间,也可能是东部夏时制

模糊时间的检测 TimeZoneInfo提供了一种方法来检查是否存在这种情况

检测此不明确时间的问题是您试图使用
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));
}
结果:

 Time  | IsDaylightSaving | IsAmbiguousTime
00:00  |        True      |      False
01:00  |       False      |       True
01:30  |       False      |       True
01:00  |       False      |       True
 Time  | IsDaylightSaving | IsAmbiguousTime
00:00  |        True      |      False
01:00  |        True      |       True
01:30  |        True      |       True
01:00  |       False      |       True
所以您希望使用
est.isambiguustime(EasternTime)
。那么就不需要重复时间,因为这将涵盖当天不明确的全部时间范围。 DateTimeOffset不存在此问题,因为它显式存储偏移量

将EST转换为UTC并存储在数据库中 对于从EST到UTC的初始转换,数据库中的现有数据需要存储偏移量以备将来使用。对于非模糊时间,可以从时区中检索。但是,正如您所确定的,对于不明确的时间,此信息将不可用。对于这些时间,您必须假设要使用哪个偏移量,并将DB中的时间标记为可疑时间,以便UI在显示这些时间时能够做出相应的反应

根据数据受影响的程度,可能不值得更改UI并忽略该问题,特别是如果时间超过一小时(因为在该时区的用户屏幕上,该时间仍然显示为凌晨1点)对用户来说并不重要的话。如果你以后改变主意,DB仍然会记录时间是可疑的

从UTC转换为EST并检测不明确时间 首先,使用DateTimeOffset,因为它可以区分东部时间凌晨1点和东部时间凌晨1点之间的差异。此时,
TimeZoneInfo.isambiguustime(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));
}
结果:

 Time  | IsDaylightSaving | IsAmbiguousTime
00:00  |        True      |      False
01:00  |       False      |       True
01:30  |       False      |       True
01:00  |       False      |       True
 Time  | IsDaylightSaving | IsAmbiguousTime
00:00  |        True      |      False
01:00  |        True      |       True
01:30  |        True      |       True
01:00  |       False      |       True
未来考虑 用户界面问题 当向用户显示时,本地时间是否不明确(重复小时)应该无关紧要。您可以简单地将UTC时间转换为其时区,并将其格式化为字符串。您可能希望选中IsAmbiguustime,以向用户显示提示,说明为什么他们可能会看到“凌晨1点”两次。应使用UTC按日期对信息进行排序。从UTC到本地时间绝不应该含糊不清,因为每个时间点在UTC中只存在一次,所以不存在重复的小时数

因此,现在唯一的问题是,如果用户正在输入时间,您需要解释它们的含义,因为用户不太可能输入偏移量,甚至不关心这些细节。可悲的是,没有简单的方法来处理这个问题,如果不尝试教你的用户偏移量,他们就会犯错误并进入错误的时间。他们可能进入凌晨4点,以为是午夜4小时后,忘记了那天晚上有1小时的额外/更少时间。或者,他们可以在一天的凌晨3点进入,时钟在凌晨3点向前移动,而在那一天,这是一个根本不存在的时间

幸运的是,时钟改变的目的是尽量减少用户输入的问题,因为大多数人都在睡觉。因此,系统可以做出最好的猜测,有时可以接受一个小时的出局时间。如果这真的很重要,那么您可以检查当天是否有夏令时,并显示一个带有警告/提示的不同UI

储存和转移 如果在MSSQL服务器中存储时间,则应首选,因为这样可以同时存储时间和偏移量。使用这种类型时,MSSQL服务器可以正确处理不同偏移量的比较时间

对于不支持这种类型的数据库,可以在数据库中以UTC为单位存储时间,并在单独的列中存储该时间的偏移量。这将允许您准确地知道它记录的当地时间

当与外部系统交换时,理想情况下,将时间以本地格式传输,格式为
yyyy-MM-dd HH:MM:sszzz
(例如
2010-11-07 01:00:00-03:30
),这样时间和偏移量都可以保留。否则,UTC通常是最佳选择,但理想情况下应加上后缀“Z”或“+00:00”,以使这一点显而易见

在内存中,DateTimeOffset类是更好的选择,因为它可以表示任意偏移量
       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)

    };
    // ------------------ UTC to Eastern  

    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));
     }

        // ------------------ Eastern  to UTC    
    DateTime testTime;
    Console.WriteLine("UTC Time  |  Est Time   | IsDaylightSaving | IsAmbiguousTime | isSecondHour ");
    EasternTime = new DateTime(2010, 11, 7, 1, 30, 0, DateTimeKind.Unspecified);

    // First Hour of DST
    testTime = Convert_to_UTC (est, EasternTime,false);
    Console.WriteLine("{0:HH:mm}     |   {1:HH:mm}     | {2,11}      |      {3,5}      |      {4,5}", testTime, EasternTime, est.IsDaylightSavingTime(EasternTime), est.IsAmbiguousTime(EasternTime), isSecondHour(est, EasternTime, testTime));

    // Second Hour of DST
    testTime = Convert_to_UTC(est, EasternTime, true);
    Console.WriteLine("{0:HH:mm}     |   {1:HH:mm}     | {2,11}      |      {3,5}      |      {4,5}", testTime, EasternTime, est.IsDaylightSavingTime(EasternTime), est.IsAmbiguousTime(EasternTime), isSecondHour(est, EasternTime, testTime));