Определить смещение времени с учетом Олсона TZID и локального DateTime?

Мне нужно определить смещение по времени, данное Olson TZID события и DateTime события.

Я полагаю, что могу сделать это с помощью Noda Time, но я здесь новичок и мне нужна помощь - пример реальной последовательности вызовов API для выполнения этой задачи.

Еще несколько подробностей о том, что мы используем и делаем.

  1. У нас работает веб-сайт на базе ASP.NET + SQL Server. Пользователи нашего сайта вводят и сохраняют события, которые происходят в разных местах. Для каждого события Lat / Long и DateTime со смещением времени (этого события) входят в число обязательных полей.
  2. Существуют различные сценарии ввода данных, иногда сначала вводится широта / долгота, иногда DateTime. Система позволяет определить широту / долготу по адресу или точке карты. Мы хотим, чтобы смещение времени соответствовало широте / долготе в большинстве случаев, потому что мы обычно ожидаем, что DateTime будет введен как локальный для этого события.
  3. Мы используем поле DateTimeOffset SQL Server для хранения данных.
  4. Мы не хотим зависеть от сторонних веб-сервисов для определения смещения, но предпочитаем, чтобы наша система делала это.

Я могу свободно загружать и использовать любые необходимые инструменты или данные. Я уже загрузил шейп-файлы с http://efele.net/maps/tz/ и использовал Shape2SQL http://goo.gl/u7AUy для преобразования их в таблицу SQL Server с TZID и GEOM.
Я знаю, как получить TZID из Lat / Long, запрашивая эту таблицу.
Опять же, мне нужно определить смещение по времени от TZID и DateTime.

1 ответ

Спасибо за Джона Скита и Мэтта Джонсона за то, что они предоставили ответ в Google Group Noda Time http://goo.gl/I9unm0. Джон объяснил, как получить значение Microsoft BCL DateTimeOffset, используя значения Olson TZID и NodaTime LocalDateTime. Мэтт указал на пример определения, является ли DST активным, учитывая ZonedDateTime: каков эквивалент System.TimeZoneInfo.IsDaylightSavingTime в NodaTime?

Я собираюсь написать сообщение в блоге, обобщающее мой опыт получения Olson TZID из Lat/Long, парсинга строки DateTime в LocalDateTime и определения DateTimeOffset. Я покажу, как работают методы GetTmzIdByLocation(широта, долгота) и GetRoughDateTimeOffsetByLocation(широта, долгота) и почему мне нужны оба (первый метод не работает для местоположений в океане). Как только я напишу этот пост, я добавлю сюда комментарий.

Обратите внимание, что синтаксический анализ строки DateTime в приведенном ниже коде пока не оптимален; как объяснил Мэтт в посте группы Google (ссылка выше), лучше использовать инструменты Noda Time, чем BCL. Смотрите связанный вопрос на http://goo.gl/ZRZ7XP

Мой текущий код:

public object GetDateTimeOffset(string latitude, string longitude, string dateTime)
{
    var tzFound = false;
    var isDST = false;

    var tmpDateTime = new DateTimeOffset(DateTime.Now).DateTime;
    if (!String.IsNullOrEmpty(dateTime))
    {
        try
        {
            // Note: Looks stupid? I need to throw away TimeZone Offset specified in dateTime string (if any).
            // Funny thing is that calling DateTime.Parse(dateTime) would automatically modify DateTime for its value in a system timezone.
            tmpDateTime = DateTimeOffset.Parse(dateTime).DateTime;
        }

        catch (Exception) { }
    }

    try
    {
        var tmzID = GetTmzIdByLocation(latitude, longitude);

        DateTimeOffset result;
        if (String.IsNullOrEmpty(tmzID) || tmzID.ToLower() == "uninhabited")   // TimeZone is unknown, it's probably an ocean, so we would just return time offest based on Lat/Long. 
        {
            var offset = GetRoughDateTimeOffsetByLocation(latitude, longitude);
            result = new DateTimeOffset(tmpDateTime, TimeSpan.FromMinutes(offset * 60));  // This only works correctly if tmpDateTime.Kind = Unspecified, see http://goo.gl/at3Vba
        }                                                                      // A known TimeZone is found, we can adjust for DST using Noda Time calls below.
        else
        {
            tzFound = true;
            // This was provided by Jon Skeet
            var localDateTime = LocalDateTime.FromDateTime(tmpDateTime); // See Noda Time docs at http://goo.gl/XseiSa
            var dateTimeZone = DateTimeZoneProviders.Tzdb[tmzID];
            var zonedDateTime = localDateTime.InZoneLeniently(dateTimeZone);  // See Noda Time docs at http://goo.gl/AqE8Qo
            result = zonedDateTime.ToDateTimeOffset(); // BCL DateTimeOffset
            isDST = zonedDateTime.IsDaylightSavingsTime();
        }

        return new { result = result.ToString(IncidentDateFormat), tzFound, isDST };
    }
    catch (Exception ex)
    {
        IMAPLog.LogEvent(System.Reflection.MethodBase.GetCurrentMethod().Name, "", ex);
        throw new CustomHttpException("Unable to get timezone offset.");
    }
}

Метод расширения (предоставленный Мэттом Джонсоном) для определения, активен ли DST. Что такое эквивалент System.TimeZoneInfo.IsDaylightSavingTime в NodaTime?

public static class NodaTimeUtil
{
    // An extension method by Matt Johnson - on Stack Overflow at http://goo.gl/ymy7Wb
    public static bool IsDaylightSavingsTime(this ZonedDateTime zonedDateTime)
    {
        var instant = zonedDateTime.ToInstant();
        var zoneInterval = zonedDateTime.Zone.GetZoneInterval(instant);
        return zoneInterval.Savings != Offset.Zero;
    }
}
Другие вопросы по тегам