web-dev-qa-db-ja.com

NodaTimeを使用したタイムゾーン間の変換

私は現在、レガシーバックエンドがユーザーの現在のタイムゾーン(より具体的にはオフセット)に基づいて日時の解決をサポートできるようにしようとしています。私たちのサーバーは東部標準時であり、私たちの日時のほとんどはそこから始まります。ただし、他のタイムゾーンにいるユーザーの場合、それらの日時を取得するときに、タイムゾーンへの変換(またはこの場合はオフセット)が必要です。また、ユーザーからの日時は、サーバーに永続化する前に東部標準時に変換する必要があります。開発中のフロントエンドがWebベースであるため、ユーザーのオフセットを数分で取得し、その値をヘッダー内のサービスレイヤーに渡すことができます。 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 +/-オフセット->クライアント

19
PureCognition

customResolverが何であるかはわかりませんが、最初の方法は問題ないように見えます。

2番目の方法は少しずれています。私は提案します:

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"];

...そしてそれをどこでも使用します。

3番目と4番目の方法は、私が慣用的なものと考えるものではありません。3番目の方法では、次のように使用する必要があります。

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();
}

4番目の方法は、OffsetDateTimeを使用した変換に関して必要なすべてを提供するわけではないため、少し注意が必要です。使用したコードはおそらく問題ありませんが、OffsetDateTimeを使用できれば確かにクリーンになります。

編集:4番目のメソッドをよりクリーンにするために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();
}
38
Jon Skeet

最初のメソッドは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();
        }
    }
}
0
Max Booreviy