C#の特定のタイムゾーンでのDateTimeの作成


162

タイムゾーンが誤って設定されて修正されたために、マシンでタイムゾーンが変更された場合のケースをテストする単体テストを作成しようとしています。

テストでは、どのローカルタイムゾーンでもDateTimeオブジェクトを作成して、テストの実行者がそれらの場所に関係なく正常に実行できるようにする必要があります。

DateTimeコンストラクターから確認できることから、TimeZoneをローカルタイムゾーン、UTCタイムゾーン、または指定しないのいずれかに設定できます。

PSTなどの特定のタイムゾーンでDateTimeを作成するにはどうすればよいですか?


回答:


216

Jonの答えTimeZoneについて語っていますが、代わりにTimeZoneInfoを使用することをお勧めします。

個人的には、可能な限りUTCで物事を維持するのが好きです(少なくとも過去について。将来のためにUTCを保存することには潜在的な問題があります)。そのため、次のような構造をお勧めします。

public struct DateTimeWithZone
{
    private readonly DateTime utcDateTime;
    private readonly TimeZoneInfo timeZone;

    public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
    {
        var dateTimeUnspec = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
        utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTimeUnspec, timeZone); 
        this.timeZone = timeZone;
    }

    public DateTime UniversalTime { get { return utcDateTime; } }

    public TimeZoneInfo TimeZone { get { return timeZone; } }

    public DateTime LocalTime
    { 
        get 
        { 
            return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); 
        }
    }        
}

わかりやすくするために、 "TimeZone"の名前を "TimeZoneInfo"に変更することをお勧めします。


5
同等のSQL Serverコンストラクトは知りません。タイムゾーン名を1つの列とし、UTC値を別の列にすることをお勧めします。それらを個別にフェッチすると、かなり簡単にインスタンスを作成できます。
ジョンスキート、

2
DateTimeとTimeZoneInfoを受け取るコンストラクターの予想される使用法についてはわかりませんが、dateTime.ToUniversalTime()メソッドを呼び出していることを考えると、ローカル時間である可能性があると推測していると思います。その場合、渡されたTimeZoneInfoを実際に使用してUTCに変換する必要があると思います。
IDisposable

2
@ChrisMoschini:その時点で、あなたは自分自身のIDスキームを発明しているだけです-これは、世界の他の誰も使用していないスキームです。おかげで、業界標準のzoneinfoに固執します。(たとえば、「ヨーロッパ/ロンドン」がどのように無意味であるかを理解するのは難しいです。)
ジョンスキート

2
@ChrisMoschini:別の例:CST。それはUTC-5またはUTC-6ですか?ISTはどうですか?データベースにイスラエル、インド、アイルランドはありますか?(そして、現在のオフセットがわかっている場合でも、同じ略語を使用しているさまざまな国が異なる時間に変更される可能性があります。そのため、実際のタイムゾーンが意味するところについてはあいまいさが残ります。タイムゾーン!=オフセット。)あなたの主張に戻ると:略語を使用すると、問題が最もよく解決されます。業界標準のタイムゾーンIDの使用はどのように悪化しましたか?
ジョンスキート2013年

6
@ChrisMoschini:あいまいな省略形ではなく、業界標準の明確なzoneinfo IDを使用することを引き続きお勧めします。これは誰のライブラリを好むかという問題ではありません-ライブラリの作者は実際には問題ではありません。誰かが適切な識別子を選択した別のライブラリを使用したい場合は、それで問題ありません。タイムゾーンの識別子の選択は重要ですが、ISTの例で示したように、省略形あいまいであることを読者が認識することは非常に重要だと思います。
Jon Skeet、

54

DateTimeOffset構造は、まさにこのタイプの使用のために作成されました。

参照:http : //msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx

特定のタイムゾーンでDateTimeOffsetオブジェクトを作成する例を次に示します。

DateTimeOffset do1 = new DateTimeOffset(2008, 8, 22, 1, 0, 0, new TimeSpan(-5, 0, 0));


1
おかげで、これはそれを達成するための良い方法です。適切なタイムゾーン内でDateTimeOffsetオブジェクトを取得したら、.UtcDateTimeプロパティを使用して、作成したUTC時間を取得できます。日付をUTCで保存する場合、各ユーザーの日付を現地時間に変換することは大したことではありません:)
Redth

2
一部のTimeZonesがサマータイムを尊重する一方で、他のタイムゾーンはそれを守らないため、これは夏時間を正しく処理するとは思いません。また、「当日」のDSTが開始/終了し、その日の一部がオフになります。
crokusek

14
レッスン。DSTは特定のタイムゾーンの規則です。DateTimeOffsetは、どのタイムゾーンにも関連付けられていません。-5などのUTCオフセット値とタイムゾーンを混同しないでください。これはタイムゾーンではなく、オフセットです。同じオフセットが多くのタイムゾーンで共有されることが多いため、タイムゾーンを参照するあいまいな方法です。DateTimeOffsetはタイムゾーンではなくオフセットに関連付けられているため、DSTルールを適用できない可能性があります。そのため、午前3時は、DateTimeOffset構造(例:HoursプロパティとTimeOfDayプロパティ)では例外なく、1年の毎日の午前3時になります。
Triynko 2016

混乱する可能性があるのは、DateTimeOffsetのLocalDateTimeプロパティを見る場合です。そのプロパティはDateTimeOffsetではなく、DateTimeKind.Localの種類のDateTimeインスタンスです。そのインスタンスは、ローカルシステムのタイムゾーンに関係なく、タイムゾーンに関連付けられています。そのプロパティは夏時間を反映します。
Triynko 2016

4
したがって、DateTimeOffsetの実際の問題は、十分な情報が含まれていないことです。タイムゾーンではなくオフセットが含まれます。オフセットは、複数のタイムゾーンがあいまいです。
Triynko 2016

41

ここでの他の回答は役に立ちますが、特に太平洋にアクセスする方法はカバーしていません。

public static DateTime GmtToPacific(DateTime dateTime)
{
    return TimeZoneInfo.ConvertTimeFromUtc(dateTime,
        TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
}

奇妙なことに、「太平洋標準時」は通常「太平洋夏時間」とは異なるものを意味しますが、この場合は一般的に太平洋時間を指します。実際、を使用FindSystemTimeZoneByIdしてフェッチする場合、使用可能なプロパティの1つは、そのタイムゾーンが現在夏時間かどうかを示すブール値です。

これのより一般化された例は、ユーザーがどこから要求しているかなどに基づいて、さまざまなTimeZonesで必要なDateTimeを処理するために一緒に投げたライブラリで見ることができます。

https://github.com/b9chris/TimeZoneInfoLib.Net

時間のリストはWindowsレジストリから取得されるため、これはWindowsの外部では機能しません(LinuxのMonoなど)。 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\

その下にはキー(レジストリエディターのフォルダーアイコン)があります。これらのキーの名前は、渡すものですFindSystemTimeZoneById。Linuxでは、個別のLinux標準タイムゾーン定義セットを使用する必要がありますが、これについては十分に調査していません。


1
さらに、ConvertTimeBySystemTimeZoneId()があります。例:TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow、 "Central Standard Time")
ブレント

WindowsのTimeZone Id Listでもこの回答を確認できます。stackoverflow.com
yu yang Jian

7

私は、拡張メソッドを使用して、Jon SkeetのWeb用の回答を少し変更しました。チャームのように紺碧にも効きます。

public static class DateTimeWithZone
{

private static readonly TimeZoneInfo timeZone;

static DateTimeWithZone()
{
//I added web.config <add key="CurrentTimeZoneId" value="Central Europe Standard Time" />
//You can add value directly into function.
    timeZone = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["CurrentTimeZoneId"]);
}


public static DateTime LocalTime(this DateTime t)
{
     return TimeZoneInfo.ConvertTime(t, timeZone);   
}
}

2

そのためのカスタムオブジェクトを作成する必要があります。カスタムオブジェクトには2つの値が含まれます。

CLRが提供するデータ型が既に存在するかどうかはわかりませんが、少なくともTimeZoneコンポーネントはすでに利用可能です。


2

私はジョン・スキートの答えが好きですが、1つ追加したいと思います。ジョンが俳優が常にローカルタイムゾーンで渡されることを期待していたかどうかはわかりません。しかし、私はそれがローカル以外のものである場合にそれを使いたいです。

私はデータベースから値を読み取っていて、そのデータベースがどのタイムゾーンにあるかを知っています。したがって、ctorでは、データベースのタイムゾーンを渡します。でもそれなら現地時間での値をお願いします。JonのLocalTimeは、ローカルタイムゾーンの日付に変換された元の日付を返しません。元のタイムゾーンに変換された日付を返します(ctorに渡したものは何でも)。

これらのプロパティ名はそれを明確にすると思います...

public DateTime TimeInOriginalZone { get { return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); } }
public DateTime TimeInLocalZone    { get { return TimeZoneInfo.ConvertTime(utcDateTime, TimeZoneInfo.Local); } }
public DateTime TimeInSpecificZone(TimeZoneInfo tz)
{
    return TimeZoneInfo.ConvertTime(utcDateTime, tz);
}

0

TimeZonesクラスを使用すると、タイムゾーン固有の日付を簡単に作成できます。

TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById(TimeZones.Paris.Id));

1
申し訳ありませんが、ここではAsp .NET Core 2.2では使用できません。VS2017は、Outlook Nugetパッケージをインストールすることを提案しています。
マチャド

例=> TimeZoneInfo.ConvertTime(DateTime.Now、TimeZoneInfo.FindSystemTimeZoneById( "Pacific Standard Time"))
AZ_
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.