Как получить DateTimeOffset с информацией о местном времени

У меня есть эти входные строки:

var timeStr = "03:22";
var dateStr = "2018/01/12";
var format = "yyyy/MM/dd";
var timeZone = "Asia/Tehran";

Это информация о времени, которое у меня есть, timeStr находится в часовом поясе Asia/Tehran, а не в UTC.

Используя NodaTime, как я могу получить объект DateTimeOffset, содержащий эту информацию, содержащую правильное смещение?


person mehrandvd    schedule 29.05.2018    source источник


Ответы (2)


Давайте преобразуем всю вашу информацию в соответствующие типы Noda Time:

// Input values (as per the question)
var timeStr = "03:22";
var dateStr = "2018/01/12";
var format = "yyyy/MM/dd";
var timeZone = "Asia/Tehran";

// The patterns we'll use to parse input values
LocalTimePattern timePattern = LocalTimePattern.CreateWithInvariantCulture("HH:mm");
LocalDatePattern datePattern = LocalDatePattern.CreateWithInvariantCulture(format);

// The local parts, parsed with the patterns and then combined.
// Note that this will throw an exception if the values can't be parsed -
// use the ParseResult<T> return from Parse to check for success before
// using Value if you want to avoid throwing.
LocalTime localTime = timePattern.Parse(timeStr).Value;
LocalDate localDate = datePattern.Parse(dateStr).Value;
LocalDateTime localDateTime = localDate + localTime;

// Now get the time zone by ID
DateTimeZone zone = DateTimeZoneProviders.Tzdb[timeZone];

// Work out the zoned date/time being represented by the local date/time. See below for the "leniently" part.
ZonedDateTime zonedDateTime = localDateTime.InZoneLeniently(zone);
// The Noda Time type you want would be OffsetDateTime
OffsetDateTime offsetDateTime = zonedDateTime.ToOffsetDateTime();
// If you really want the BCL type...
DateTimeOffset dateTimeOffset = zonedDateTime.ToDateTimeOffset();

Обратите внимание на «InZoneLeniently», который обрабатывает неоднозначные или пропущенные локальные значения даты/времени следующим образом:

неоднозначные значения отображаются на более раннюю из альтернатив, а «пропущенные» значения сдвигаются вперед на время «промежутка».

Это может или может быть то, что вы хотите. Также есть InZoneStrictly, который вызовет исключение, если нет ни одного момента времени, представленного данной локальной датой/временем, или вы можете вызвать InZone и передать свой собственный ZoneLocalMappingResolver.

person Jon Skeet    schedule 30.05.2018

Если вы ищете объект Nodatime DateTimeOffset, вы можете сделать это так:

        var timeStr = "03:22";
        var dateStr = "2018/01/12";
        // DateTime.Parse can deal with this format without specs            
        // var format = "yyyy/MM/dd";
        var timeZone = "Asia/Tehran";


        var date = DateTime.Parse(timeStr + " " + dateStr);
        var zone = DateTimeZoneProviders.Tzdb[timeZone];
        var timespanOffset = zone.GetUtcOffset(SystemClock.Instance.Now).ToTimeSpan();
        var result = new DateTimeOffset(date, timespanOffset);

        Console.Write(result.ToUniversalTime());

Результат: 1/11/2018 10:52:00 PM +00:00, что соответствует тегеранскому времени GMT +4,5.

person pijemcolu    schedule 29.05.2018
comment
Спасибо. Формат может измениться в будущем. Вот почему я использовал для этого переменную. - person mehrandvd; 29.05.2018
comment
Почему сейчас переходят на GetUtcOffset? Разве это не должно быть date, чтобы учитывать смещение для этой даты? - person mehrandvd; 29.05.2018
comment
Это деталь реализации библиотеки, похоже, что datetimezoneprovider не может определить ее смещение, не зная текущего времени UTC. - person pijemcolu; 29.05.2018
comment
Я не думаю, что это требует текущего времени. Требуется дата, которую мы ищем для ее смещения. Для данного часового пояса в разные дни года существует разное смещение из-за перехода на летнее время. - person mehrandvd; 29.05.2018
comment
В моем примере var zone = DateTimeZoneProviders.Tzdb["Asia/Tehran"] содержит информацию о часовом поясе. если вы распечатаете, как он создается сейчас с SystemClock.Instance.Now timespanOffset.ToString() возвращает 04:30:00. Это правильное смещение от utc. - person pijemcolu; 29.05.2018
comment
zone.GetUtcOffset(Instant instant) требует момента в качестве параметра. Если вы думаете об этом, это означает, что вы могли бы получить смещения из разных часовых поясов, если бы вы перешли в другой момент часового пояса, например, gmt+1. Я думаю, это основная причина такой реализации. - person pijemcolu; 29.05.2018
comment
Это ошибочный подход ИМО. Это может дать неправильный результат, если я правильно понял требование ОП, которое заключается в том, чтобы получить смещение для указанной локальной даты/времени, а не смещение времени прямо сейчас. См. мой ответ о том, как сделать все это в Noda Time, указав, как вы хотите обрабатывать неоднозначные/пропущенные локальные значения. - person Jon Skeet; 30.05.2018
comment
Неправильный подход из-за того, что SystemClock.Instance.Now зависит от системы/культуры? Мне просто повезло получить правильное смещение 4:30 из-за того, что моя машина имеет правильное время UTC? @JonSkeet Мне также было любопытно, почему DateTimeZoneProviders.Tzdb[timeZone] не предоставляет свойство смещения utc по умолчанию? - person pijemcolu; 30.05.2018
comment
@pijemcolu: неправильный подход из-за того, что теперь смещение, вполне возможно, отличается от смещения на дату/время, указанные timeStr и dateStr. Не существует такого понятия, как смещение UTC по умолчанию для часового пояса — даже если вы игнорируете переход на летнее время, стандартное смещение UTC может меняться в течение истории. - person Jon Skeet; 30.05.2018