Элегантный способ конвертировать временную метку эпохи в восточное и обратно с помощью Nodatime

Я работаю над написанием управляемой оболочки для API реального времени Массачусетского залива (MBTA). У них есть API, который возвращает время сервера, которое является меткой времени unix (эпоха). Библиотека, в которой я его реализую, - PCL. Profile 78 Это означает, что я ограничил поддержку BCL TimeZone, поэтому я прибег к использованию Nodatime

Я пытаюсь преобразовать время, возвращаемое с сервера, в восточное время, которое America/New_York как DateTime объект и обратный путь. Мой текущий код очень грязный

public static class TimeUtils
{
    static readonly DateTimeZone mbtaTimeZone = DateTimeZoneProviders.Tzdb["America/New_York"];
    static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    public static DateTime GetMbtaDateTime (long unixTimestamp)
    {
        var mbtaEpochTime = epoch.AddSeconds (unixTimestamp);
        var instant = Instant.FromUtc (mbtaEpochTime.Year, mbtaEpochTime.Month,
            mbtaEpochTime.Day, mbtaEpochTime.Hour, mbtaEpochTime.Minute, mbtaEpochTime.Second);
        var nodaTime = instant.InZone (mbtaTimeZone);
        return nodaTime.ToDateTimeUnspecified ();
    }

    public static long MbtaDateTimeToUnixTimestamp (DateTime time)
    {
        TimeSpan secondsSinceEpochMbtaTz = time - epoch;
        var instant = Instant.FromUtc (time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second);
        var mbtaTzSpan = mbtaTimeZone.GetUtcOffset (instant).ToTimeSpan ();
        var epochDiff = secondsSinceEpochMbtaTz - mbtaTzSpan;
        return (long)epochDiff.TotalSeconds;
    }
}

Есть ли другой способ написать это просто. Я надеюсь, что Nodatime получит поддержку для преобразования времени эпохи в America/New_York DateTime и America/New_York DateTime для эпохи времени. Мой метод MbtaDateTimeToUnixTimestamp это жестокий взлом

1 ответ

Решение

Во-первых, как уже упоминалось в комментариях, было бы лучше использовать типы кода Noda во всем коде - прибегать только к DateTime когда ты действительно должен. Это должно привести к значительно более чистому коду.

Преобразование метки времени Unix в Instant это действительно легко:

Instant instant = Instant.FromSecondsSinceUnixEpoch(seconds);

Затем вы можете преобразовать в ZonedDateTime согласно вашему текущему коду... и используя ToDateTimeUnspecified хорошо, если вам действительно нужно использовать DateTime,

Напротив, ваш текущий код выглядит неработоспособным - вы предполагаете, что DateTime значение UTC, эффективно. Это будет противоречить вашему последующему использованию часового пояса. Я подозреваю, что вы хотите преобразовать входные данные в LocalDateTime, а затем примените часовой пояс. Например:

public static long MbtaDateTimeToUnixTimestamp(DateTime time)
{
    var local = LocalDateTime.FromDateTime(time);
    var zoned = local.InZoneStrictly(mbtaTimeZone);
    var instant = zoned.ToInstant();
    return instant.Ticks / NodaConstants.TicksPerSecond;
}

Обратите внимание InZoneStrictly вызов. Это вызовет исключение, если вы либо укажете местное время, которого не было, либо время, которое существовало дважды - в обоих случаях из-за переходов DST. Возможно, это не то, чего вы хотите - вам действительно нужно подумать о том, что вы хотите, чтобы произошло в этих случаях, или попытаться избежать их осуществимости. См. Раздел часовых поясов документации для более подробной информации и опций.

Другие вопросы по тегам