Это одна из тех проблем, которая кажется обманчиво простой, но решение на самом деле гораздо более сложное, чем вы думаете.
Сложности возникают из-за природы местного времени, правила которого определяются часовые пояса . Многие часовые пояса имеют переходы , которые происходят либо регулярно (например, для перехода на летнее время ), либо нерегулярно (например, для изменений в стандартном времени).
Как таковыенужно учитывать:
- Может ли день быть короче или длиннее 24 часов?
- Например, в большинстве США начало летнего времени наступает в 2:00 утра, и в этот день в день 23 часа, потому что пропущен час с 2:00 до 2:59. В конце летнего времени, также в 2:00 утра в США, час с 1:00 до 1:59 повторяется, создавая 25 часов в этот день.
- Может лидень начинается или заканчивается в другое время, кроме полуночи?
Узнайте больше в Программисты лжи верят во время .
Рассмотрите возможность использования следующего подхода для преодоления этих реальных соображений.
Во-первых,определить некоторые вспомогательные функции для выполнения большей части работы. Они не относятся к конкретному моменту времени или конкретному часовому поясу.
static double GetFractionOfDay(DateTimeOffset dto, TimeZoneInfo tz)
{
// Get the start of the day, and the start of the next day
DateTimeOffset startOfDay = GetStartOfDay(dto, tz);
DateTimeOffset startOfNextDay = GetStartOfDay(startOfDay.AddDays(1), tz);
// Calculate the length of the day. It might not be 24 hours!
TimeSpan lengthOfDay = startOfNextDay - startOfDay;
// Now calculate the position within the day, and the fraction to return
TimeSpan durationSinceStartOfDay = dto - startOfDay;
return durationSinceStartOfDay / lengthOfDay;
}
static DateTimeOffset GetStartOfDay(DateTimeOffset dto, TimeZoneInfo tz)
{
// Make sure we're in the correct time zone
dto = TimeZoneInfo.ConvertTime(dto, tz);
// Start by assuming a local midnight exists
DateTime dt = dto.Date;
// Handle days without a local midnight (these do exist in several time zones)
if (tz.IsInvalidTime(dt))
{
// Advance by the transition gap. This is usually 1 hour, but best not to hard-code that.
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]);
}
// Handle days with more than one midnight (it's possible, even if unlikely)
if (tz.IsAmbiguousTime(dt))
{
// There's more than one. Prefer the first one, since we want the beginning of the day.
TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
TimeSpan offset = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
return new DateTimeOffset(dt, offset);
}
// Clean case, just one local midnight and it does exist
return new DateTimeOffset(dt, tz.GetUtcOffset(dt));
}
Определив их, вы теперь можете получить ответ на свой вопрос относительно «сейчас» в местном часовом поясе.
double dayFraction = GetFractionOfDay(DateTimeOffset.Now, TimeZoneInfo.Local);
Однако - Хотя это правильный ответ «какая это часть дня», имейте в виду, что может быть более важно выровнятьс тем, что ожидает API получения, даже если не совсем правильно. Другими словами, если 12:00
должно всегда быть 0.5
, даже если это не совсем в середине дня, тогда используйте elmer007's подход.