Заменить TimeZone - PullRequest
0 голосов
/ 10 мая 2018

Мы пытаемся создать базовую функцию составления календаря событий, которая позволяет пользователю создавать событие и указывать время начала для данного месяца, дня, года, часа и минуты, а также часовой пояс (System.TimeZoneInfo.Id). Система CMS генерирует результирующее System.DateTime на основе местоположения нашего сервера, скажем, TimeZoneInfo.Id Стандартное время горы . CMS не предоставляет опцию с их компонентом выбора даты для указания часового пояса. Однако у нас есть контроль над точностью даты и времени в SQL, по умолчанию установлен на 7.

DateTime форматируется как yyyyMMddTHHmmssZ для целей заполнения времени начала / окончания в .ics / ical. С этим форматом 25 мая 2018 г. в 19:00 (20180508T192840Z) всегда будет выглядеть как стандартное время горы (MST) сервера, а не 25 мая 2018 г. в 7:00 вечера в выбранное восточное стандартное время (EST).

Как я могу "заменить" часовой пояс для DateTime, который генерируется без изменения года / месяца / дня / часа / минуты, на DateTime, DateTimeOffset, TimeZoneInfo, NodaTime, или даже string функции для форматирования в yyyyMMddTHHmmssZ?

Следующее:

TimeZoneInfo destinationTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var converted = TimeZoneInfo.ConvertTime(dateTime1, destinationTimeZone);

или

LocalDateTime fromLocal = LocalDateTime.FromDateTime(dateTime1);
DateTimeZone fromZone = DateTimeZoneProviders.Tzdb["America/Denver"];
ZonedDateTime fromZoned = fromLocal.InZoneLeniently(fromZone);

DateTimeZone toZone = DateTimeZoneProviders.Tzdb["America/Chicago"];
ZonedDateTime toZoned = fromZoned.WithZone(toZone);
LocalDateTime toLocal = toZoned.LocalDateTime;
var result = toLocal.ToDateTimeUnspecified();

Создает новый DateTime с часами, скорректированными с CST на EST, который не будет работать, поскольку цель состоит в том, чтобы получить DateTime с исходным значением часа, но с TimeZoneInfo.Id Восточное стандартное время .

DateTime конструктор, по-видимому, не имеет конструктора, определяющего TimeZoneInfo, только DateTimeKind.

Как это можно сделать с некоторыми, например DateTime, созданными из DateTime.Now?

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Несколько вещей:

  • Ваш спецификатор формата включает в себя Z в конце.Это обрабатывается форматированием строки .Net как символьный литерал , поскольку он не является допустимым спецификатором форматирования даты и времени .Обратите внимание, что токены форматирования чувствительны к регистру.Как литерал, он просто копируется в вывод - так же, как T.Таким образом, эта генерируемая строка всегда будет интерпретироваться как UTC всем, что ее анализирует, поскольку именно это означает Z в стандарте ISO 8601.В конечном счете, это является основной причиной проблемы, с которой вы сталкиваетесь.

    Если вы хотели, чтобы оно отражало неоднозначное местное время (потому что часовой пояс находится где-то в вашем .ics?), То пропустите Zполностью.Однако, если вы намеревались включить смещение часового пояса, вы можете использовать спецификатор K для значений DateTime или, возможно, спецификатор zzz в сочетании со значениями DateTimeOffset - в зависимости от ваших конкретных потребностей.

  • Как отмечали другие, DateTime не знает о часовом поясе, но также отмечает, что ни один из них не является DateTimeOffset в том смысле, что он отслеживает только смещение от UTC, а не определенный часовой пояс.Например, он может отслеживать -07:00, но он не может сказать вам, что находится в Mountain Time.Вот почему Noda Time имеет тип ZonedDateTime..Net не имеет такого встроенного типа сам по себе.

  • В вашем коде, не то, что в вызове TimeZoneInfo.ConvertTime, .Kind dateTime1 переменная будет учтена.Если это DateTimeKind.Utc, то результат будет детерминированным.Но если это DateTimeKind.Unspecified или DateTimeKind.Local, то он будет обрабатываться так, как если бы он соответствовал часовому поясу локального компьютера - который является часовым поясом server в вашем случае.

  • Обратите внимание, что гораздо лучше написать код таким же образом, который ведет себя одинаково независимо от того, установлен часовой пояс сервера.Обычно это означает избегать DateTimeKind.Local, таких как DateTime.Now, TimeZoneInfo.Local и других.Вместо этого используйте DateTime.UtcNow для получения текущего DateTime.В качестве альтернативы вы можете использовать либо DateTimeOffset.Now или DateTimeOffset.UtcNow, либо один из методов в реализациях Noda Time IClock.

В конце дня, хотя есть несколько возможныхРешения вашей проблемы, самый простой способ создать текущее время в виде строки в определенном часовом поясе будет:

TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime utcNow = DateTime.UtcNow;
DateTime converted = TimeZoneInfo.ConvertTime(utcNow, destinationTimeZone);
string s = converted.ToString("yyyyMMddTHHmmss");

ИЛИ вам может потребоваться:

TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTimeOffset utcNow = DateTimeOffset.UtcNow;
DateTimeOffset converted = TimeZoneInfo.ConvertTime(utcNow, destinationTimeZone);
string s = converted.ToString("yyyyMMddTHHmmsszzz").Replace(":","");

Обратите внимание на удалениеот : до Replace в конце - потому что в формате ISO 8601 basic смещение должно быть как -0500, а не -05:00.К сожалению, нет спецификатора формата, чтобы получить это напрямую.(Только в формате ISO 8601 расширенный используется двоеточие).

0 голосов
/ 10 мая 2018
Тип

DateTime не знает о часовом поясе, все, что он знает о зонах - это DateTimeKind, который может быть Local, Utc или Unspecified.Информация о зоне, включенная в строковое представление, будет основана на значении Kind и часовом поясе сервера.

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

var dateTime = DateTime.Now; /*your date time here*/
var destinationTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var zonedDateTime = new DateTimeOffset(DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified), destinationTimeZone.BaseUtcOffset);
var dateTimeStr = zonedDateTime.ToString("o"/*your format goes here*/);
...