web-dev-qa-db-ja.com

C#で特定のタイムゾーンにDateTimeを作成する

マシンでタイムゾーンが変更されたときのケースをテストするためのユニットテストを作成しようとしています。

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

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

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

133
Jack Hughes

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

個人的には、可能な限りUTCで物事を維持するのが好きです(少なくとも過去については、 futureに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」に変更することをお勧めします-短い名前を自分で使います。

187
Jon Skeet

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

45
Clever Human

ここの他の回答は便利ですが、特に太平洋へのアクセス方法についてはカバーしていません-ここに行きます:

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標準セットを使用する必要がありますが、これについては十分に検討していません。

33
Chris Moschini

私は Jon Skeet answer 拡張メソッドを使用したWeb用に少し変更しました。また、Azureでもチャームのように機能します。

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);   
}
}
6
Jernej Novak

Jon Skeetの答えが好きですが、一つだけ付け加えたいと思います。ジョンが常にローカルタイムゾーンで渡されることを期待していたかどうかはわかりません。しかし、私はそれがローカル以外の何かである場合にそれを使用したいと思います。

私はデータベースから値を読んでおり、そのデータベースがどのタイムゾーンにあるかを知っています。そのため、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);
}
2
Gabe Halsmer

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

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

2
Jon Limjap