Как вы указали, это обсуждалось ранее. Здесь и здесь - два хороших сообщения для обзора.
Кроме того, документация на DateTime.Subtract
имеет следующее:
Метод Subtract(DateTime)
не учитывает значение свойства Kind
двух значений DateTime
при выполнении вычитания. Перед вычитанием DateTime
объектов убедитесь, что объекты представляют время в одном часовом поясе. В противном случае результат будет включать разницу между часовыми поясами.
Примечание
Метод DateTimeOffset.Subtract(DateTimeOffset)
учитывает разницу между часовыми поясами, когда выполняя вычитание.
За пределами просто «представлять время в одном часовом поясе», имейте в виду, что даже если объекты находятся в одном часовом поясе, вычитание значений DateTime
все равно не будет рассмотрим DST или другие переходы между двумя объектами.
Ключевым моментом является то, что для определения прошедшего времени вы должны вычитать абсолютных точек во времени . Они лучше всего представлены DateTimeOffset
in. NET.
Если у вас уже есть значения DateTimeOffset
, вы можете просто вычесть их. Тем не менее, вы все равно можете работать со значениями DateTime
до тех пор, пока вы сначала правильно их преобразуете в DateTimeOffset
.
В качестве альтернативы вы можете преобразовать все в UT C - но вам придется go - DateTimeOffset
или аналогичный код, чтобы сделать это правильно в любом случае.
В вашем случае вы можете изменить свой код на следующее:
public static TimeSpan Subtract(this DateTime minuend, TimeZoneInfo minuendTimeZone,
DateTime subtrahend, TimeZoneInfo subtrahendTimeZone)
{
return minuend.ToDateTimeOffset(minuendTimeZone) -
subtrahend.ToDateTimeOffset(subtrahendTimeZone);
}
Вам также понадобится ToDateTimeOffset
метод расширения (, который я также использовал в других ответах ).
public static DateTimeOffset ToDateTimeOffset(this DateTime dt, TimeZoneInfo tz)
{
if (dt.Kind != DateTimeKind.Unspecified)
{
// Handle UTC or Local kinds (regular and hidden 4th kind)
DateTimeOffset dto = new DateTimeOffset(dt.ToUniversalTime(), TimeSpan.Zero);
return TimeZoneInfo.ConvertTime(dto, tz);
}
if (tz.IsAmbiguousTime(dt))
{
// Prefer the daylight offset, because it comes first sequentially (1:30 ET becomes 1:30 EDT)
TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
TimeSpan offset = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
return new DateTimeOffset(dt, offset);
}
if (tz.IsInvalidTime(dt))
{
// Advance by the gap, and return with the daylight offset (2:30 ET becomes 3:30 EDT)
TimeSpan[] offsets = { tz.GetUtcOffset(dt.AddDays(-1)), tz.GetUtcOffset(dt.AddDays(1)) };
TimeSpan gap = offsets[1] - offsets[0];
return new DateTimeOffset(dt.Add(gap), offsets[1]);
}
// Simple case
return new DateTimeOffset(dt, tz.GetUtcOffset(dt));
}