タイム ゾーン間での時刻の変換
更新 : 2007 年 11 月
タイム ゾーン間での日時の違いを処理するアプリケーションの重要性が増しています。アプリケーションは、DateTime 構造体にある現地時刻ですべての時刻を表現するだけで良いとは想定できなくなりました。たとえば、米国東部の現在時刻を表示する Web ページは、東アジアの顧客にとっては妥当なものではありません。このトピックでは、あるタイム ゾーンから別のタイム ゾーンに時刻を変換する方法、およびタイム ゾーンの取り扱いが不適切な DateTimeOffset 値を変換する方法について説明します。
世界協定時刻への変換
世界協定時刻 (UTC) は、高精度で、原子時計に基づく時刻基準です。世界のタイム ゾーンは、UTC からの正または負のオフセットとして表されます。したがって、UTC は、タイム ゾーンの影響を受けない時刻、またはどのタイム ゾーンからも中立の時刻を提供します。コンピュータ間での日時の統一性が重要な場合は、UTC 時刻を使用することをお勧めします。日時の使用についての詳細および他のベスト プラクティスについては、「Coding Best Practices Using DateTime in the .NET Framework」を参照してください。個別のタイム ゾーンを UTC に変換すると、時刻の比較が容易になります。
メモ : |
---|
DateTimeOffset 構造体をシリアル化して、ある時刻を明確に表すこともできます。DateTimeOffset オブジェクトは UTC からのオフセットで日時の値を格納するので、常に UTC に対する相対値で特定の時刻を表します。 |
時刻を UTC に変換する最も簡単な方法は、static (Visual Basic では Shared) の TimeZoneInfo.ConvertTimeToUtc(DateTime) メソッドを呼び出す方法です。メソッドで実行される具体的な変換は、次の表に示すように、dateTime パラメータの Kind プロパティの値によって決まります。
DateTime.Kind プロパティ |
変換 |
---|---|
現地時刻を UTC に変換します。 |
|
dateTime パラメータが現地時刻だと想定し、現地時刻を UTC に変換します。 |
|
dateTime パラメータを変更しないで返します。 |
次に示すコードは、現在の現地時刻を UTC に変換し、結果をコンソールに表示します。
Dim dateNow As Date = Date.Now
Console.WriteLine("The date and time are {0} UTC.", _
TimeZoneInfo.ConvertTimeToUtc(dateNow))
DateTime dateNow = DateTime.Now;
Console.WriteLine("The date and time are {0} UTC.",
TimeZoneInfo.ConvertTimeToUtc(dateNow));
メモ : |
---|
TimeZoneInfo.ConvertTimeToUtc(DateTime) メソッドでは、TimeZone.ToUniversalTime メソッドおよび DateTime.ToUniversalTime メソッドと同一の結果が生成されるとは限りません。ホスト システムのローカル タイム ゾーンに複数の調整規則が含まれる場合、TimeZoneInfo.ConvertTimeToUtc(DateTime) は、適切な規則を特定の日付および時刻に適用します。他の 2 つのメソッドは、最新の調整規則を常に適用します。 |
日時値が現地時刻または UTC のどちらも表していない場合、ToUniversalTime メソッドは正しくない結果を返す可能性があります。ただし、TimeZoneInfo.ConvertTimeToUtc メソッドを使用すると、指定したターム ゾーンから日時を変換できます。目的のタイム ゾーンを表す TimeZoneInfo オブジェクトを取得する方法の詳細については、「ローカル システムで定義されているタイム ゾーンの検索」を参照してください。次に示すコードでは、TimeZoneInfo.ConvertTimeToUtc メソッドを使用して、米国東部標準時を UTC に変換しています。
Dim easternTime As New Date(2007, 01, 02, 12, 16, 00)
Dim easternZoneId As String = "Eastern Standard Time"
Try
Dim easternZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId)
Console.WriteLine("The date and time are {0} UTC.", _
TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone))
Catch e As TimeZoneNotFoundException
Console.WriteLine("Unable to find the {0} zone in the registry.", _
easternZoneId)
Catch e As InvalidTimeZoneException
Console.WriteLine("Registry data on the {0} zone has been corrupted.", _
easternZoneId)
End Try
DateTime easternTime = new DateTime(2007, 01, 02, 12, 16, 00);
string easternZoneId = "Eastern Standard Time";
try
{
TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId);
Console.WriteLine("The date and time are {0} UTC.",
TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to find the {0} zone in the registry.",
easternZoneId);
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the {0} zone has been corrupted.",
easternZoneId);
}
DateTime オブジェクトの Kind プロパティとタイム ゾーンが一致してない場合、このメソッドは ArgumentException をスローします。不一致が発生するのは、Kind プロパティが DateTimeKind.Local であるのに TimeZoneInfo オブジェクトがローカル タイム ゾーンを表していない場合、または Kind プロパティが DateTimeKind.Utc であるのに TimeZoneInfo オブジェクトが DateTimeKind.Utc と等しくない場合です。
これらのメソッドはすべて、DateTime 値をパラメータとして受け取り、DateTime 値を返します。DateTimeOffset 値の場合、DateTimeOffset 構造体には、現在のインスタンスの日時を UTC に変換する ToUniversalTime インスタンス メソッドがあります。次に示す例では、ToUniversalTime メソッドを呼び出して、現地時刻と他のいくつかの時刻を UTC に変換しています。
Dim localTime, otherTime, universalTime As DateTimeOffset
' Define local time in local time zone
localTime = New DateTimeOffset(#6/15/2007 12:00:00PM#)
Console.WriteLine("Local time: {0}", localTime)
Console.WriteLine()
' Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero)
Console.WriteLine("Other time: {0}", otherTime)
Console.WriteLine("{0} = {1}: {2}", _
localTime, otherTime, _
localTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
localTime, otherTime, _
localTime.EqualsExact(otherTime))
Console.WriteLine()
' Convert other time to UTC
universalTime = localTime.ToUniversalTime()
Console.WriteLine("Universal time: {0}", universalTime)
Console.WriteLine("{0} = {1}: {2}", _
otherTime, universalTime, _
universalTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
otherTime, universalTime, _
universalTime.EqualsExact(otherTime))
Console.WriteLine()
' The example produces the following output to the console:
' Local time: 6/15/2007 12:00:00 PM -07:00
'
' Other time: 6/15/2007 7:00:00 PM +00:00
' 6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
' 6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
'
' Universal time: 6/15/2007 7:00:00 PM +00:00
' 6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
' 6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True
DateTimeOffset localTime, otherTime, universalTime;
// Define local time in local time zone
localTime = new DateTimeOffset(new DateTime(2007, 6, 15, 12, 0, 0));
Console.WriteLine("Local time: {0}", localTime);
Console.WriteLine();
// Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero);
Console.WriteLine("Other time: {0}", otherTime);
Console.WriteLine("{0} = {1}: {2}",
localTime, otherTime,
localTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
localTime, otherTime,
localTime.EqualsExact(otherTime));
Console.WriteLine();
// Convert other time to UTC
universalTime = localTime.ToUniversalTime();
Console.WriteLine("Universal time: {0}", universalTime);
Console.WriteLine("{0} = {1}: {2}",
otherTime, universalTime,
universalTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
otherTime, universalTime,
universalTime.EqualsExact(otherTime));
Console.WriteLine();
// The example produces the following output to the console:
// Local time: 6/15/2007 12:00:00 PM -07:00
//
// Other time: 6/15/2007 7:00:00 PM +00:00
// 6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
// 6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
//
// Universal time: 6/15/2007 7:00:00 PM +00:00
// 6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
// 6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True
指定したタイム ゾーンへの UTC の変換
UTC から現地時刻への変換については、後の「UTC から現地時刻への変換」を参照してください。任意のタイム ゾーンを指定して、UTC をそのタイム ゾーンの時刻に変換するには、ConvertTimeFromUtc メソッドを呼び出します。このメソッドは、次の 2 つのパラメータを受け取ります。
変換対象の UTC。このパラメータは、Kind プロパティが DateTimeKind.Utc または DateTimeKind.Unspecified に設定された DateTime 値である必要があります。
UTC の変換先となるタイム ゾーン。
次に示すコードでは、UTC を米国中部標準時に変換しています。
Dim timeUtc As Date = Date.UtcNow
Try
Dim cstZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Dim cstTime As Date = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone)
Console.WriteLine("The date and time are {0} {1}.", _
cstTime, _
IIf(cstZone.IsDaylightSavingTime(cstTime), _
cstZone.DaylightName, cstZone.StandardName))
Catch e As TimeZoneNotFoundException
Console.WriteLine("The registry does not define the Central Standard Time zone.")
Catch e As InvalidTimeZoneException
Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.")
End Try
DateTime timeUtc = DateTime.UtcNow;
try
{
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
Console.WriteLine("The date and time are {0} {1}.",
cstTime,
cstZone.IsDaylightSavingTime(cstTime) ?
cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Central Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Central STandard Time zone has been corrupted.");
}
UTC から現地時刻への変換
UTC を現地時刻に変換するには、変換する時刻を表す DateTime オブジェクトの ToLocalTime メソッドを呼び出します。メソッドの具体的な動作は、次の表に示すように、オブジェクトの Kind プロパティの値によって異なります。
DateTime.Kind プロパティ |
変換 |
---|---|
DateTimeKind.Local |
DateTime の値を変更しないで返します。 |
DateTimeKind.Unspecified |
DateTime の値を UTC と想定し、UTC を現地時刻に変換します。 |
DateTimeKind.Utc |
DateTime の値を現地時刻に変換します。 |
メモ TimeZone.ToLocalTime メソッドの動作は、DateTime.ToLocalTime メソッドと同じです。受け取るパラメータは、変換する日時の値 1 つだけです。
static (Visual Basic では Shared) TimeZoneInfo.ConvertTime メソッドを使用すると、任意のタイム ゾーンの時刻を指定して、現地時刻に変換することもできます。この方法については、次のセクションで説明します。
任意の 2 つのタイム ゾーン間での時刻の変換
次に示す TimeZoneInfo クラスの 2 つの static (Visual Basic では Shared) メソッドを使用すると、任意の 2 つのタイム ゾーン間で時刻を変換できます。
-
このメソッドのパラメータは、変換する日時値、その日時値のタイム ゾーンを表す TimeZoneInfo オブジェクト、および変換後のタイム ゾーンを表す TimeZoneInfo オブジェクトです。
-
このメソッドのパラメータは、変換する日時値、その日時値のタイム ゾーンの識別子、および変換後のタイム ゾーンの識別子です。
どちらのメソッドでも、変換する日時値の Kind プロパティと、もう 1 つの日時値に対応するタイム ゾーンを表す TimeZoneInfo オブジェクトまたはタイム ゾーン識別子が必要です。それ以外の場合は、ArgumentException がスローされます。たとえば、日時値の Kind プロパティが DateTimeKind.Local の場合、メソッドにパラメータとして渡される TimeZoneInfo オブジェクトが TimeZoneInfo.Local ではないと、例外がスローされます。また、メソッドにパラメータとして渡される識別子が TimeZoneInfo.Local.Id に等しくない場合にも、例外がスローされます。
ConvertTime メソッドを使用してハワイ標準時を現地時刻に変換する例を次に示します。
Dim hwTime As Date = #2/01/2007 8:00:00 AM#
Try
Dim hwZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time")
Console.WriteLine("{0} {1} is {2} local time.", _
hwTime, _
IIf(hwZone.IsDaylightSavingTime(hwTime), hwZone.DaylightName, hwZone.StandardName), _
TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local))
Catch e As TimeZoneNotFoundException
Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.")
Catch e As InvalidTimeZoneException
Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.")
End Try
DateTime hwTime = new DateTime(2007, 02, 01, 08, 00, 00);
try
{
TimeZoneInfo hwZone = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
Console.WriteLine("{0} {1} is {2} local time.",
hwTime,
hwZone.IsDaylightSavingTime(hwTime) ? hwZone.DaylightName : hwZone.StandardName,
TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Hawaiian STandard Time zone has been corrupted.");
}
DateTimeOffset 値の変換
DateTimeOffset オブジェクトはインスタンス化されるときにタイム ゾーンとの関連付けを解除されるので、このオブジェクトによって表される日時値は、完全なタイム ゾーン対応ではありません。ただし、多くの場合、アプリケーションで必要になるのは、特定のタイム ゾーンでの時刻ではなく、UTC に対する 2 つの異なるオフセットに基づいて日時を変換することだけです。この変換は、現在のインスタンスの ToOffset メソッドを呼び出すことで実行できます。このメソッドの唯一のパラメータは、メソッドから返される新しい日時値のオフセットです。
たとえば、Web ページに対するユーザー要求の日時がわかっていて、MM/dd/yyyy hh:mm:ss zzzz の形式で文字列としてシリアル化されている場合、次の ReturnTimeOnServer メソッドは、この日時値を Web サーバーでの日時に変換します。
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
Dim format As String = "M/d/yyyy H:m:s zzz"
Dim serverOffset As TimeSpan = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now)
Try
Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
Dim serverTime As DateTimeOffset = clientTime.ToOffset(serverOffset)
Return serverTime
Catch e As FormatException
Return DateTimeOffset.MinValue
End Try
End Function
public DateTimeOffset ReturnTimeOnServer(string clientString)
{
string format = @"M/d/yyyy H:m:s zzz";
TimeSpan serverOffset = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);
try
{
DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture);
DateTimeOffset serverTime = clientTime.ToOffset(serverOffset);
return serverTime;
}
catch (FormatException)
{
return DateTimeOffset.MinValue;
}
}
このメソッドに、UTC より 5 時間早いタイム ゾーンの日時を表す "9/1/2007 5:32:07 -05:00" という文字列を渡すと、サーバーが米国の太平洋標準時ゾーンに存在する場合は、9/1/2007 3:32:07 AM -07:00 という値が返ります。
TimeZoneInfo クラスには、複数の DateTimeOffset 値を使用してタイム ゾーンの変換を実行する TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) メソッドのオーバーロードも含まれます。このメソッドのパラメータは、DateTimeOffset 値と、変換後のタイム ゾーンへの参照です。このメソッドを呼び出すと、DateTimeOffset 値が返ります。たとえば、前の例の ReturnTimeOnServer メソッドは、ConvertTime(DateTimeOffset, TimeZoneInfo) メソッドの呼び出しを使用して、次のように書き換えることができます。
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
Dim format As String = "M/d/yyyy H:m:s zzz"
Try
Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
Dim serverTime As DateTimeOffset = TimeZoneInfo.ConvertTime(clientTime, TimeZoneInfo.Local)
Return serverTime
Catch e As FormatException
Return DateTimeOffset.MinValue
End Try
End Function
public DateTimeOffset ReturnTimeOnServer(string clientString)
{
string format = @"M/d/yyyy H:m:s zzz";
try
{
DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format,
CultureInfo.InvariantCulture);
DateTimeOffset serverTime = TimeZoneInfo.ConvertTime(clientTime,
TimeZoneInfo.Local);
return serverTime;
}
catch (FormatException)
{
return DateTimeOffset.MinValue;
}
}