Ответ 1
Заметим:
// These are just plain unspecified DateTimes
DateTime dtOneAm = new DateTime(2017, 03, 12, 01, 00, 00);
DateTime dtFourAm = new DateTime(2017, 03, 12, 04, 00, 00);
// The difference is not going to do anything other than 4-1=3
TimeSpan difference1 = dtFourAm - dtOneAm;
// ... but we have a time zone to consider!
TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
// Use that time zone to get DateTimeOffset values.
// The GetUtcOffset method has what we need.
DateTimeOffset dtoOneAmEastern = new DateTimeOffset(dtOneAm, eastern.GetUtcOffset(dtOneAm));
DateTimeOffset dtoFourAmEastern = new DateTimeOffset(dtFourAm, eastern.GetUtcOffset(dtFourAm));
// Subtracting these will take the offset into account!
// It essentially does this: [4-(-4)]-[1-(-5)] = 8-6 = 2
TimeSpan difference2 = dtoFourAmEastern - dtoOneAmEastern;
// Let see the results
Console.WriteLine("dtOneAm: {0:o} (Kind: {1})", dtOneAm, dtOneAm.Kind);
Console.WriteLine("dtFourAm: {0:o} (Kind: {1})", dtFourAm, dtOneAm.Kind);
Console.WriteLine("difference1: {0}", difference1);
Console.WriteLine("dtoOneAmEastern: {0:o})", dtoOneAmEastern);
Console.WriteLine("dtoFourAmEastern: {0:o})", dtoFourAmEastern);
Console.WriteLine("difference2: {0}", difference2);
Результаты:
dtOneAm: 2017-03-12T01:00:00.0000000 (Kind: Unspecified)
dtFourAm: 2017-03-12T04:00:00.0000000 (Kind: Unspecified)
difference1: 03:00:00
dtoOneAmEastern: 2017-03-12T01:00:00.0000000-05:00)
dtoFourAmEastern: 2017-03-12T04:00:00.0000000-04:00)
difference2: 02:00:00
Обратите внимание, что DateTime
несет DateTimeKind
в свойстве Kind
, который по умолчанию равен Unspecified
. Он не принадлежит к определенному часовому поясу. DateTimeOffset
не имеет вида, он имеет Offset
, который сообщает вам, насколько далеко локальное время смещено от UTC. Ни один из них не дает вам часового пояса. Это то, что делает объект TimeZoneInfo
. См. "Часовой пояс!= Смещение" в вики-указатель часового пояса.
Часть, которую, я думаю, вы, возможно, разочарованы, заключается в том, что по нескольким историческим причинам объект DateTime
никогда не понимает часовые пояса при выполнении математики, даже если у вас может быть DateTimeKind.Local
. Он мог бы быть реализован для наблюдения за переходами локального часового пояса, но это не было сделано таким образом.
Вы также можете быть заинтересованы в Noda Time, который дает совсем другой API для даты и времени в .NET, в гораздо более разумным и целенаправленным способом.
using NodaTime;
...
// Start with just the local values.
// They are local to *somewhere*, who knows where? We didn't say.
LocalDateTime ldtOneAm = new LocalDateTime(2017, 3, 12, 1, 0, 0);
LocalDateTime ldtFourAm = new LocalDateTime(2017, 3, 12, 4, 0, 0);
// The following won't compile, because LocalDateTime does not reference
// a linear time scale!
// Duration difference = ldtFourAm - ldtOneAm;
// We can get the 3 hour period, but what does that really tell us?
Period period = Period.Between(ldtOneAm, ldtFourAm, PeriodUnits.Hours);
// But now lets introduce a time zone
DateTimeZone eastern = DateTimeZoneProviders.Tzdb["America/New_York"];
// And apply the zone to our local values.
// We'll choose to be lenient about DST gaps & overlaps.
ZonedDateTime zdtOneAmEastern = ldtOneAm.InZoneLeniently(eastern);
ZonedDateTime zdtFourAmEastern = ldtFourAm.InZoneLeniently(eastern);
// Now we can get the difference as an exact elapsed amount of time
Duration difference = zdtFourAmEastern - zdtOneAmEastern;
// Dump the output
Console.WriteLine("ldtOneAm: {0}", ldtOneAm);
Console.WriteLine("ldtFourAm: {0}", ldtFourAm);
Console.WriteLine("period: {0}", period);
Console.WriteLine("zdtOneAmEastern: {0}", zdtOneAmEastern);
Console.WriteLine("zdtFourAmEastern: {0}", zdtFourAmEastern);
Console.WriteLine("difference: {0}", difference);
ldtOneAm: 3/12/2017 1:00:00 AM
ldtFourAm: 3/12/2017 4:00:00 AM
period: PT3H
zdtOneAmEastern: 2017-03-12T01:00:00 America/New_York (-05)
zdtFourAmEastern: 2017-03-12T04:00:00 America/New_York (-04)
difference: 0:02:00:00
Мы можем видеть период в три часа, но это не значит, что истекшее время. Это просто означает, что два локальных значения находятся на расстоянии три часа в своем положении на часах. NodaTime понимает разницу между этими понятиями, в то время как встроенные типы .Net не поддерживают.
Некоторые последующие чтения для вас:
- Что не так с DateTime?
- Больше удовольствия с помощью DateTime
- Случай с DateTime.Now
- Пять простоя на летнее время Антипаттерны разработчиков .NET
О, и еще одна вещь. У вашего кода это...
DateTime oneAm = TimeZoneInfo.ConvertTime(new DateTime(2017, 03, 12, 01, 00, 00), easternStandardTime);
Поскольку созданный DateTime
не имеет определенного типа, вы просите конвертировать из локального часового пояса вашего компьютера в восточное время. Если вы не в восточное время, ваша переменная oneAm
может быть не равна 1 AM!