Определить смещение времени с учетом Олсона TZID и локального DateTime?
Мне нужно определить смещение по времени, данное Olson TZID события и DateTime события.
Я полагаю, что могу сделать это с помощью Noda Time, но я здесь новичок и мне нужна помощь - пример реальной последовательности вызовов API для выполнения этой задачи.
Еще несколько подробностей о том, что мы используем и делаем.
- У нас работает веб-сайт на базе ASP.NET + SQL Server. Пользователи нашего сайта вводят и сохраняют события, которые происходят в разных местах. Для каждого события Lat / Long и DateTime со смещением времени (этого события) входят в число обязательных полей.
- Существуют различные сценарии ввода данных, иногда сначала вводится широта / долгота, иногда DateTime. Система позволяет определить широту / долготу по адресу или точке карты. Мы хотим, чтобы смещение времени соответствовало широте / долготе в большинстве случаев, потому что мы обычно ожидаем, что DateTime будет введен как локальный для этого события.
- Мы используем поле DateTimeOffset SQL Server для хранения данных.
- Мы не хотим зависеть от сторонних веб-сервисов для определения смещения, но предпочитаем, чтобы наша система делала это.
Я могу свободно загружать и использовать любые необходимые инструменты или данные. Я уже загрузил шейп-файлы с 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;
}
}