Преобразование между часовыми поясами с помощью Noda Time
В настоящее время я пытаюсь убедиться, что наш унаследованный бэкэнд может поддерживать разрешение времени на основе текущего часового пояса пользователя (или, более конкретно, смещения). Наши серверы находятся в восточном стандартном времени, и большинство наших дат происходит там. Тем не менее, для пользователей, которые находятся в других часовых поясах, преобразование в их часовой пояс (или, в данном случае, смещение) необходимо при получении этих дат. Кроме того, даты, приходящие от пользователя, должны быть переведены в восточное стандартное время перед сохранением на сервере. Учитывая, что внешний интерфейс, который мы разрабатываем, основан на веб-технологиях, я могу получить смещение пользователя за считанные минуты и передать это значение в мой уровень обслуживания в заголовке. Я посмотрел на Noda Time и думаю, что это отличный API. Это заставило меня задуматься о времени в более утонченном вопросе, но я все еще не уверен на 100%, что правильно использовал его правильно. Вот методы, которые я написал для преобразований, описанных выше. Я проверил их, и они, кажется, работают. Учитывая сценарий выше, похоже ли это на правильное использование библиотеки? Я правильно думаю о дате?
public static DateTime ConvertToUtcFromEasternTimeZone(DateTime easternDateTime)
{
NodaTime.DateTimeZone easternTimeZone = NodaTime.DateTimeZoneProviders.Tzdb.GetZoneOrNull("America/New_York");
ZoneLocalMappingResolver customResolver = Resolvers.CreateMappingResolver(Resolvers.ReturnLater, Resolvers.ReturnStartOfIntervalAfter);
var easternLocalDateTime = LocalDateTime.FromDateTime(easternDateTime);
var easternZonedDateTime = easternTimeZone.ResolveLocal(easternLocalDateTime, customResolver);
return easternZonedDateTime.ToDateTimeUtc();
}
public static DateTime ConvertToEasternTimeZoneFromUtc(DateTime utcDateTime)
{
NodaTime.DateTimeZone easternTimeZone = NodaTime.DateTimeZoneProviders.Tzdb.GetZoneOrNull("America/New_York");
NodaTime.DateTimeZone utcTimeZone = NodaTime.DateTimeZoneProviders.Tzdb.GetZoneOrNull("UTC");
ZoneLocalMappingResolver customResolver = Resolvers.CreateMappingResolver(Resolvers.ReturnLater, Resolvers.ReturnStartOfIntervalAfter);
var utcLocal = LocalDateTime.FromDateTime(utcDateTime);
var utcZonedDateTime = utcTimeZone.ResolveLocal(utcLocal, customResolver);
var easternZonedDateTime = utcZonedDateTime.ToInstant().InZone(easternTimeZone);
return easternZonedDateTime.ToDateTimeUnspecified();
}
public static DateTime ConvertToUtc(DateTime dateTime, int offsetInMinutes)
{
LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);
var convertedDateTime = localDateTime.PlusMinutes(offsetInMinutes).ToDateTimeUnspecified();
return convertedDateTime;
}
public static DateTime ConvertFromUtc(DateTime dateTime, int offsetInMinutes)
{
LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);
var convertedDateTime = localDateTime.PlusMinutes(-offsetInMinutes).ToDateTimeUnspecified();
return convertedDateTime;
}
Идея заключается в том, что часовой пояс имеет значение, когда я решаю время между временем UTC и часовым поясом в базе данных. Когда я решаю между временем клиента и временем UTC, тогда смещение имеет значение.
В будущем мы можем сохранять время UTC, и это будет легче. В настоящее время это решение является пробелом.
Идея в том, что мы собираемся идти от...
клиент -> UTC +/- смещение -> UTC -> восточное время -> база данных
база данных -> восточное время -> UTC -> UTC +/- смещение -> клиент
в конце концов...
клиент -> UTC +/- смещение -> UTC -> база данных
база данных -> UTC -> UTC +/- смещение -> клиент
1 ответ
Ваш первый метод выглядит хорошо, хотя мы не знаем, что customResolver
является.
Ваш второй метод немного не подходит. Я бы предложил:
public static DateTime ConvertToEasternTimeZoneFromUtc(DateTime utcDateTime)
{
var easternTimeZone = DateTimeZoneProviders.Tzdb["America/New_York"];
return Instant.FromDateTimeUtc(utcDateTime)
.InZone(easternTimeZone)
.ToDateTimeUnspecified();
}
Обратите внимание, что вам не нужно искать восточный часовой пояс при каждом вызове метода - просто имейте:
private static readonly DateTimeZone EasternTimeZone =
DateTimeZoneProviders.Tzdb["America/New_York"];
... тогда используйте это везде.
Третий и четвертый методы - это не то, что я считаю идиоматическими - для третьего метода вы должны использовать:
public static DateTime ConvertToUtc(DateTime dateTime, int offsetInMinutes)
{
var offset = Offset.FromMinutes(offsetInMinutes);
var localDateTime = LocalDateTime.FromDateTime(dateTime);
return new OffsetDateTime(localDateTime, offset).ToInstant()
.ToDateTimeUtc();
}
Четвертый метод кажется немного сложнее, так как мы не предоставляем все, что должны, с точки зрения конверсии с OffsetDateTime
, Код, который вы использовали, вероятно, в порядке, но он, безусловно, будет чище, если вы сможете использовать OffsetDateTime
,
РЕДАКТИРОВАТЬ: я теперь добавил метод Instant
сделать четвертый метод чище. Это будет часть 1.2.0, и вы можете использовать:
public static DateTime ConvertFromUtc(DateTime dateTime, int offsetInMinutes)
{
var offset = Offset.FromMinutes(offsetInMinutes);
var instant = Instant.FromDateTimeUtc(dateTime);
return instant.WithOffset(offset)
.LocalDateTime
.ToDateTimeUnspecified();
}
Хочу добавить, что первый способ можно было переписать без customResolver
.
using System;
using NodaTime;
namespace qwerty
{
class Program
{
static void Main(string[] args)
{
var convertedInUTC = ConvertToUtcFromCustomTimeZone("America/Chihuahua", DateTime.Now);
Console.WriteLine(convertedInUTC);
}
private static DateTime ConvertToUtcFromCustomTimeZone(string timezone, DateTime datetime)
{
DateTimeZone zone = DateTimeZoneProviders.Tzdb[timezone];
var localtime = LocalDateTime.FromDateTime(datetime);
var zonedtime = localtime.InZoneLeniently(zone);
return zonedtime.ToInstant().InZone(zone).ToDateTimeUtc();
}
}
}