Преобразование данных времени в местное время в C # - PullRequest
0 голосов
/ 26 апреля 2018

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

  • День недели (число от 0 до 6)
  • Время (количество миллисекунд с полуночи по местному времени пользователя)
  • Смещение UTC (количество часов отличается от UTC)
  • DST Observed (логическое указание, если DST наблюдается в этом часовом поясе)

Эти данные представляют часы работы. Так что есть время на каждый день. Я хочу отобразить это время в местном времени пользователей, исходя из предположения, что каждый день в будущем

int dayOffset = availability.Day - (int)now.DayOfWeek
if (dayOffset < 0)
    dayOffset += 7;

Я действительно изо всех сил пытаюсь разобраться с часовыми поясами и работать, когда один часовой пояс может наблюдать летнее время, в то время как другой, возможно, наблюдает летнее время, но еще не наблюдал. Моя главная проблема на данный момент - я думаю мне нужно создать DateTimeOffset объект для нелокального времени, но я не уверен, как это сделать, так как не знаю, находится ли DST в эффект или нет. Я надеюсь, что я проясняю себя. Это действительно потрясающий опыт работы с датами и временем!

Ответы [ 4 ]

0 голосов
/ 26 апреля 2018

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

Однако, учитывая, что вы не ссылаетесь на абсолютное время на конкретную дату, а вместо этого ссылаетесь на время в бесконечном количестве дней в конкретном часовом поясе; сохранение времени в формате UTC больше не имеет смысла, поскольку время в формате UTC (даже если мы отбрасываем дату) будет различным в зависимости от даты из-за перехода на летнее время.

Лучший способ сохранить время довольно близко к тому, что вы уже сделали.

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

Чтобы решить эту проблему, просто сохраните идентификатор часового пояса вместо смещения UTC и логическое значение DST.

Теперь мы можем сконструировать объект DateTime и преобразовать его в любой часовой пояс, используя класс TimeZoneInfo :

int dayOffset = availability.Day - (int)DateTime.Today.DayOfWeek;
if (dayOffset < 0)
{
    dayOffset += 7;
}

var openingHourStart = DateTime
                       .SpecifyKind(DateTime.Today, DateTimeKind.Unspecified)
                       .AddDays(dayOffset)
                       .AddMilliseconds(availability.Time);

var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(availability.TimeZoneId);
var userTimeZone = TimeZoneInfo.Local;

var convertedOpeningHourStart = TimeZoneInfo.ConvertTime(openingHourStart,
                                                         sourceTimeZone,
                                                         userTimeZone);
0 голосов
/ 26 апреля 2018

Попробуйте Quartz.NET .

Он реализует оценку CronExpressions и даже запускает события в заданный момент времени. Он может оценить в следующий раз, когда произойдет событие. Это может помочь вам рассчитать время открытия.

Кроме того, взгляните на веб-сайт cronmaker : там вы сможете понять весь потенциал CronExpressions.

CronExpressionDescriptor - это библиотека DotNET для преобразования CronExpressions в удобочитаемые строки.

Другая библиотека, которую я еще не пробовал - это [HangFire]. (https://www.hangfire.io/)

В этом сообщении на форуме вы можете найти обсуждение того, как HangFire реализует оценку RecurringJobs в местном часовом поясе с помощью летнего времени, что, как я считаю, является решением для того, что вы ищете.

0 голосов
/ 26 апреля 2018

Комментарий к другому ответу немного прояснил проблему.

Итак, прежде всего, храните только UTC в вашей базе данных. На самом деле.

Теперь, поскольку вас не интересуют фактические даты, так как вы храните рабочие графики, которые повторяются еженедельно, дата становится релевантной только тогда, когда вы хотите представить ваше время - и когда вы помещаете их в ваша база данных.

Итак, давайте сначала посмотрим, как вы правильно вводите время в свою базу данных. Я предполагаю, что пользователь будет вводить время в своем собственном регионе.

Убедитесь, что вы сначала создали (локализованный) DateTime, состоящий из текущей даты и заданного времени (от пользователя), и преобразовали его в UTC DateTime (вы можете сохранить текущая дата, это не имеет значения):

var utcDateTime = DateTime.Now.Date
                 .AddHours(userHours)
                 .AddMinutes(userMinutes)
                 .ToUniversalTime();

Теперь, когда вы представляете это время пользователю, просто идите другим путем:

var userDateTime = DateTime.Now.Date
                  .AddHours(utcDateTime.Hour)
                  .AddMinutes(utcDateTime.Minute)
                  .ToLocalTime();

И затем вы можете использовать userDateTime.Hour и .Minute для отображения.

0 голосов
/ 26 апреля 2018

Вы должны использовать DateTime.ToLocalTime() и TimeZoneInfo.ConvertTimeToUtc() в C # - см. https://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime(v=vs.110).aspx.

Если вы хотите хранить только те часы, когда вы открыты с понедельника по воскресенье, хорошо. Имейте простую таблицу данных, чтобы описать только время для каждого дня (от 0 = воскресенье до 7 = суббота - это перечисление .Net DayOfWeek). Ваша таблица поиска может выглядеть следующим образом:

0  null
1  08:00:00
2  08:00:00
3  08:00:00
4  08:30:00
5  08:30:00
6  10:30:00

(Используйте любой тип данных, который вам подходит - например, SQL Server 2008+ имеет тип данных TIME. Нулевой можно использовать для Закрытого в этот день, т.е. без открытого времени.)

Когда приходит время показывать ВАШЕ время любому другому пользователю, пользователь должен создать ваше время UTC на лету в тот момент, когда вы отображаете информацию для локального пользователя.

Коник предоставил один подход. Мой подход использует простые строки даты / времени. Чтобы использовать мой подход, просто храните значения времени в день в вашей базе данных. Затем вы можете посмотреть на открытое время для любого дня. Чтобы выразить это время для другого пользователя в любой локали, используйте этот код для преобразования вашего времени в UTC (вы можете заменить строковое значение «08:00:00 AM» строковой переменной, которую вы заполнили после просмотра времени открытия в вашем базы данных):

var StoreOpenTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(Convert.ToDateTime("08:00:00 AM"));

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

var StoreOpenTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(Convert.ToDateTime("04/28/2018 08:00:00 AM"));

Если у вас есть точная переменная StoreOpenTimeInUtc, вы можете использовать ее в качестве значения UTC на чужой машине, которая находится где-нибудь еще на планете Земля. Чтобы преобразовать это значение UTC в местное время, используйте метод .NET ToLocalTime ():

var OpenTimeForLocalUser = StoreOpenTimeInUtc.ToLocalTime();

Обратите внимание, что при таком подходе вы должны хранить только время открытия, как показано выше. Вам не нужно беспокоиться о датах, локальных смещениях от UTC или о чем-либо еще. Просто используйте плечо ConvertTimeToUtc() и ToLocalTime(), как показано.

...