У меня есть глобальный временной интервал (от одной «временной метки» UTC до другого), и я хочу определить, какая часть интервала перекрывает данный день недели в данном часовом поясе.
Давайте рассмотрим пример: скажем, интервал 2018-05-11T02:00:00Z/2018-05-11T10:00:00Z
, а день недели Пятница .
Для Нью-Йорка (Америка / Нью-Йорк) интервал переводится в местный интервал времени даты 2018-05-10T22:00/2018-05-11T06:00
, где первые два часа интервала не перекрываются в пятницу. Таким образом, полученный интервал должен составлять 2018-05-11T04:00:00Z/2018-05-11T10:00:00Z
.
Если бы часовым поясом был Копенгаген (Европа / Копенгаген), исходный интервал остался бы неизменным, поскольку все они перекрываются в пятницу в этом часовом поясе.
Если интервал был достаточно длинным, вы могли бы легко иметь несколько совпадений с днем недели. Конечно, также возможно отсутствие совпадений.
Мне трудно найти лучший и самый надежный способ решить эту проблему. Моя лучшая идея - взять день недели и перевести его в глобальное время из заданного часового пояса, а затем проверить на совпадения. Однако день недели не привязан к конкретной дате, а это значит, что мне на самом деле нечего переводить, и поэтому сначала нужно выяснить, какими потенциально перекрывающимися датами будет день недели.
Если я переведу интервал в местное время, возьму совпадение с днем недели (намного проще, потому что у меня теперь есть фактические даты для работы), а затем переведу их обратно, я получу правильный ответ в большинстве случаев. Однако такие вещи, как переходы DST, могут легко испортить ситуацию при переводе назад, а откат может привести к тому, что локальный временной интервал даты станет «недействительным», то есть начало будет после конца, открыв еще одну банку с червями.
Я пытаюсь решить проблему в C # с помощью NodaTime, но думаю, что проблема носит общий характер.
Ниже приведена пара тестов, запрошенных @jskeet:
using FluentAssertions;
using NodaTime;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
public class Tests
{
public static TheoryData<Interval, IsoDayOfWeek, DateTimeZone, IEnumerable<Interval>> OverlapsDayOfWeekExamples = new TheoryData<Interval, IsoDayOfWeek, DateTimeZone, IEnumerable<Interval>>
{
{ // No overlap in given time zone
new Interval(Instant.FromUtc(2018, 05, 11, 00, 00), Instant.FromUtc(2018, 05, 11, 04, 00)),
IsoDayOfWeek.Friday,
DateTimeZoneProviders.Tzdb["America/New_York"],
Enumerable.Empty<Interval>()
},
{ // Cut short because interval begins Thursday
new Interval(Instant.FromUtc(2018, 05, 11, 02, 00), Instant.FromUtc(2018, 05, 11, 10, 00)),
IsoDayOfWeek.Friday,
DateTimeZoneProviders.Tzdb["America/New_York"],
new [] { new Interval(Instant.FromUtc(2018, 05, 11, 04, 00), Instant.FromUtc(2018, 05, 11, 10, 00)) }
},
{ // Remains unchanged because everything overlaps in given time zone
new Interval(Instant.FromUtc(2018, 05, 11, 02, 00), Instant.FromUtc(2018, 05, 11, 10, 00)),
IsoDayOfWeek.Friday,
DateTimeZoneProviders.Tzdb["Europe/Copenhagen"],
new [] { new Interval(Instant.FromUtc(2018, 05, 11, 02, 00), Instant.FromUtc(2018, 05, 11, 10, 00)) }
},
{ // Cut short because interval begins Saturday and day starts at 01:00 (Spring forward)
new Interval(Instant.FromUtc(2018, 11, 04, 02, 15), Instant.FromUtc(2018, 11, 04, 06, 30)),
IsoDayOfWeek.Sunday,
DateTimeZoneProviders.Tzdb["America/Sao_Paulo"],
new [] { new Interval(Instant.FromUtc(2018, 11, 04, 03, 00), Instant.FromUtc(2018, 11, 04, 06, 30)) }
},
{ // Cut short because interval begins Saturday and day starts later (Fall back)
new Interval(Instant.FromUtc(2018, 02, 18, 01, 00), Instant.FromUtc(2018, 02, 18, 07, 30)),
IsoDayOfWeek.Sunday,
DateTimeZoneProviders.Tzdb["America/Sao_Paulo"],
new [] { new Interval(Instant.FromUtc(2018, 02, 18, 03, 00), Instant.FromUtc(2018, 02, 18, 07, 30)) }
},
{ // Overlaps multiple times (middle overlap is during DST transition)
new Interval(Instant.FromUtc(2018, 10, 28, 16, 15), Instant.FromUtc(2018, 11, 11, 12, 30)),
IsoDayOfWeek.Sunday,
DateTimeZoneProviders.Tzdb["America/New_York"],
new []
{
new Interval(Instant.FromUtc(2018, 10, 28, 16, 15), Instant.FromUtc(2018, 10, 29, 04, 00)),
new Interval(Instant.FromUtc(2018, 11, 04, 04, 00), Instant.FromUtc(2018, 11, 05, 05, 00)),
new Interval(Instant.FromUtc(2018, 11, 11, 05, 00), Instant.FromUtc(2018, 11, 11, 12, 30)),
}
},
{ // Results in an invalid date time interval
new Interval(Instant.FromUtc(2018, 10, 28, 00, 45), Instant.FromUtc(2018, 10, 28, 01, 15)),
IsoDayOfWeek.Sunday,
DateTimeZoneProviders.Tzdb["Europe/Copenhagen"],
new [] { new Interval(Instant.FromUtc(2018, 10, 28, 00, 45), Instant.FromUtc(2018, 10, 28, 01, 15)) }
},
};
[Theory]
[MemberData(nameof(OverlapsDayOfWeekExamples))]
public void OverlapsDayOfWeekTest531804504(Interval interval, IsoDayOfWeek dayOfWeek, DateTimeZone timeZone, IEnumerable<Interval> expected)
{
OverlapsDayOfWeek(interval, dayOfWeek, timeZone).Should().BeEquivalentTo(expected);
}
public IEnumerable<Interval> OverlapsDayOfWeek(Interval interval, IsoDayOfWeek dayOfWeek, DateTimeZone timeZone)
{
throw new NotImplementedException();
}
}