Получение дневного времени для начала и конца в NodaTime

Как я могу получить начальную и конечную даты для летнего времени с использованием Noda Time? Функция ниже выполняет эту задачу, но она ужасно громоздка и просит более простого решения.

/// <summary>
/// Gets the start and end of daylight savings time in a given time zone
/// </summary>
/// <param name="tz">The time zone in question</param>
/// <returns>A tuple indicating the start and end of DST</returns>
/// <remarks>Assumes this zone has daylight savings time</remarks>
private Tuple<LocalDateTime, LocalDateTime> GetZoneStartAndEnd(DateTimeZone tz)
{
    int thisYear = TimeUtils.SystemLocalDateTime.Year; // Get the year of the current LocalDateTime

    // Get January 1, midnight, of this year and next year.
    var yearStart = new LocalDateTime(thisYear, 1, 1, 0, 0).InZoneLeniently(tz).ToInstant();
    var yearEnd = new LocalDateTime(thisYear + 1, 1, 1, 0, 0).InZoneLeniently(tz).ToInstant();

    // Get the intervals that we experience in this year
    var intervals = tz.GetZoneIntervals(yearStart, yearEnd).ToArray();

    // Assuming we are in a US-like daylight savings scheme,
    // we should see three intervals:
    // 1. The interval that January 1st sits in
    // 2. At some point, daylight savings will start.
    // 3. At some point, daylight savings will stop.
    if (intervals.Length == 1)
        throw new Exception("This time zone does not use daylight savings time");
    if (intervals.Length != 3)
        throw new Exception("The daylight savings scheme in this time zone is unexpected.");

    return new Tuple<LocalDateTime,LocalDateTime>(intervals[1].IsoLocalStart, intervals[1].IsoLocalEnd);
}

Ответ 1

Там нет ни одной встроенной функции, о которой я знаю, но все данные там, поэтому вы можете создать свой собственный.

Вы на правильном пути с тем, что вы показали, но есть несколько вещей, которые следует учитывать:

  • Обычно людей интересуют конечные точки интервалов. Возвращая начало и остановку только среднего интервала, вы, вероятно, получаете значения, отличные от ожидаемых. Например, если вы используете один из часовых поясов США, например "America/Los_Angeles", ваша функция возвращает переходы как 3/9/2014 3:00:00 AM и 11/2/2014 2:00:00 AM, где вы, вероятно, ожидаете 2:00 AM для обоих.

  • Часовые пояса к югу от экватора, которые используют DST, начнут его к концу года и закончат его к началу следующего года. Поэтому иногда элементы в кортеже могут быть отменены с того, что вы ожидаете от них.

  • Существует очень много часовых поясов, которые не используют летнее время, поэтому бросать исключение - не лучшая идея.

  • Есть как минимум два часовых пояса, которые в настоящее время имеют четыре перехода за один год ("Africa/Casablanca" и "Africa/Cairo") - с "перерывом" в свои периоды летнего времени для Рамадана. И иногда есть переходы, не связанные с DST, например, когда Самоа изменила стандартное отклонение в 2011 году, что дало ему три перехода за один год.

Учитывая все это, было бы лучше вернуть список точек одного перехода, а не набор пар переходов.

Кроме того, это незначительно, но лучше было бы не привязывать метод к системным часам вообще. Год может быть легко передан параметром. Затем вы можете использовать этот метод для не текущих лет, если это необходимо.

public IEnumerable<LocalDateTime> GetDaylightSavingTransitions(DateTimeZone timeZone, int year)
{
    var yearStart = new LocalDateTime(year, 1, 1, 0, 0).InZoneLeniently(timeZone).ToInstant();
    var yearEnd = new LocalDateTime(year + 1, 1, 1, 0, 0).InZoneLeniently(timeZone).ToInstant();
    var intervals = timeZone.GetZoneIntervals(yearStart, yearEnd);

    return intervals.Select(x => x.IsoLocalEnd).Where(x => x.Year == year);
}

Также обратите внимание на конец, важно фильтровать только значения, которые находятся в текущем году, потому что интервалы могут очень распространяться на следующий год или продолжаться бесконечно.

Ответ 2

Этот фрагмент кода также поможет вам проверить время в летнее время или нет

public static bool IsDaylightSavingsTime(this DateTimeOffset dateTimeOffset)
        {
            var timezone = "Europe/London"; //https://nodatime.org/TimeZones
            ZonedDateTime timeInZone = dateTimeOffset.DateTime.InZone(timezone);
            var instant = timeInZone.ToInstant();
            var zoneInterval = timeInZone.Zone.GetZoneInterval(instant);
            return zoneInterval.Savings != Offset.Zero;
        }

как его использовать

var testDate = DateTimeOffset.Now;
var isDst = testDate.IsDaylightSavingsTime();

Зависит от вашей ситуации, вы можете немного ее изменить