回答:
現在のUTC日付を現地時間に変換する最良の方法は、CLRを使用することです。コード自体は簡単です。難しい部分は通常、CLRが純粋な悪または怖いものではないことを人々に確信させることです...
多くの例の1つについては、トピックに関するHarsh Chawlaのブログ投稿を参照してください。
残念ながら、CLRベースのソリューションを除き、このタイプの変換を処理できる組み込み機能はありません。このようなことを行うT-SQL関数を作成することもできますが、その場合は日付変更ロジックを自分で実装する必要があり、それは明らかに簡単ではありません。
Microsoft SQL Serverで日時とタイムゾーンの処理に苦労している人を支援するために、codeplexでT-SQL Toolboxプロジェクトを開発して公開しました。オープンソースであり、完全に無料で使用できます。
事前に入力された構成テーブルに加えて、プレーンT-SQL(CLRなし)を使用した簡単な日時変換UDFを提供します。また、DST(夏時間)を完全にサポートしています。
サポートされているすべてのタイムゾーンのリストは、 "DateTimeUtil.Timezone"テーブル(T-SQL Toolboxデータベース内で提供)にあります。
この例では、次のサンプルを使用できます。
SELECT [DateTimeUtil].[UDF_ConvertUtcToLocalByTimezoneIdentifier] (
'W. Europe Standard Time', -- the target local timezone
'2014-03-30 00:55:00' -- the original UTC datetime you want to convert
)
これは、変換されたローカル日時値を返します。
残念ながら、新しいデータ型(DATE、TIME、DATETIME2)のために、SQL Server 2008以降でのみサポートされています。ただし、完全なソースコードが提供されているため、テーブルとUDFをDATETIMEに置き換えることで簡単に調整できます。テストに使用できるMSSQL 2005はありませんが、MSSQL 2005でも動作するはずです。質問がある場合は、教えてください。
私は常にこのTSQLコマンドを使用します。
-- the utc value
declare @utc datetime = '20/11/2014 05:14'
-- the local time
select DATEADD(hh, DATEDIFF(hh, getutcdate(), getdate()), @utc)
それは非常にシンプルで、仕事をします。
日時を正確に変換するように見えるユーザー定義関数を提供するStackOverflowでこの答えを見つけました
変更する必要があるのは、@offset
この関数を実行しているSQLサーバーのタイムゾーンオフセットに設定するための上部の変数だけです。私の場合、SQLサーバーはEST(GMT-5)を使用しています
これは完璧ではなく、おそらく30分または15分のTZオフセット(Kevinが推奨するようなCLR関数をお勧めします)などの多くのケースでは機能しませんが、北のほとんどの一般的なタイムゾーンでは十分に機能しますアメリカ。
CREATE FUNCTION [dbo].[UDTToLocalTime](@UDT AS DATETIME) RETURNS DATETIME AS BEGIN --==================================================== --Set the Timezone Offset (NOT During DST [Daylight Saving Time]) --==================================================== DECLARE @Offset AS SMALLINT SET @Offset = -5 --==================================================== --Figure out the Offset Datetime --==================================================== DECLARE @LocalDate AS DATETIME SET @LocalDate = DATEADD(hh, @Offset, @UDT) --==================================================== --Figure out the DST Offset for the UDT Datetime --==================================================== DECLARE @DaylightSavingOffset AS SMALLINT DECLARE @Year as SMALLINT DECLARE @DSTStartDate AS DATETIME DECLARE @DSTEndDate AS DATETIME --Get Year SET @Year = YEAR(@LocalDate) --Get First Possible DST StartDay IF (@Year > 2006) SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-03-08 02:00:00' ELSE SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-04-01 02:00:00' --Get DST StartDate WHILE (DATENAME(dw, @DSTStartDate) <> 'sunday') SET @DSTStartDate = DATEADD(day, 1,@DSTStartDate) --Get First Possible DST EndDate IF (@Year > 2006) SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-11-01 02:00:00' ELSE SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-10-25 02:00:00' --Get DST EndDate WHILE (DATENAME(dw, @DSTEndDate) <> 'sunday') SET @DSTEndDate = DATEADD(day,1,@DSTEndDate) --Get DaylightSavingOffset SET @DaylightSavingOffset = CASE WHEN @LocalDate BETWEEN @DSTStartDate AND @DSTEndDate THEN 1 ELSE 0 END --==================================================== --Finally add the DST Offset --==================================================== RETURN DATEADD(hh, @DaylightSavingOffset, @LocalDate) END GO
SQL Server 2016+の場合、AT TIME ZONEを使用できます。夏時間を自動的に処理します。
Stack Overflowでの同様の質問に対するいくつかの良い回答があります。Bob Albrightによる2番目の回答の T-SQLアプローチを使用して、データ変換コンサルタントによる混乱を解決しました。
ほぼすべてのデータで機能しましたが、彼のアルゴリズムは1987年4月5日までの日付に対してのみ機能し、1940年代からまだ適切に変換されていない日付があることに気付きました。最終的には、UTC
SQL APIデータベースの日付を、Java APIを使用しUTC
て現地時間に変換するサードパーティプログラムのアルゴリズムに合わせる必要がありました。
以下のような私CLR
の例上記のケビンFeaselの答えで厳しいチャウラの例を使用して、私も私たちのフロントエンドを行うために、Javaを使用しているため、Javaの使用するソリューションと比較したいUTC
現地時間変換に。
ウィキペディアでは、1987年以前のタイムゾーン調整を含む8つの異なる憲法修正に言及しており、それらの多くは異なる状態に非常にローカライズされているため、CLRとJavaがそれらを異なる解釈をする可能性があります。フロントエンドアプリケーションコードは、ドットネットまたはJavaを使用していますか、または1987年以前の日付が問題になっていますか?
これは、CLRストアドプロシージャを使用して簡単に実行できます。
[SqlFunction]
public static SqlDateTime ToLocalTime(SqlDateTime UtcTime, SqlString TimeZoneId)
{
if (UtcTime.IsNull)
return UtcTime;
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneId.Value);
var localTime = TimeZoneInfo.ConvertTimeFromUtc(UtcTime.Value, timeZone);
return new SqlDateTime(localTime);
}
使用可能なTimeZonesをテーブルに保存できます。
CREATE TABLE TimeZones
(
TimeZoneId NVARCHAR(32) NOT NULL CONSTRAINT PK_TimeZones PRIMARY KEY,
DisplayName NVARCHAR(64) NOT NULL,
SupportsDaylightSavingTime BIT NOT NULL,
)
そして、このストアドプロシージャは、サーバー上の可能なタイムゾーンでテーブルを埋めます。
public partial class StoredProcedures
{
[SqlProcedure]
public static void PopulateTimezones()
{
using (var sql = new SqlConnection("Context Connection=True"))
{
sql.Open();
using (var cmd = sql.CreateCommand())
{
cmd.CommandText = "DELETE FROM TimeZones";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO [dbo].[TimeZones]([TimeZoneId], [DisplayName], [SupportsDaylightSavingTime]) VALUES(@TimeZoneId, @DisplayName, @SupportsDaylightSavingTime);";
var Id = cmd.Parameters.Add("@TimeZoneId", SqlDbType.NVarChar);
var DisplayName = cmd.Parameters.Add("@DisplayName", SqlDbType.NVarChar);
var SupportsDaylightSavingTime = cmd.Parameters.Add("@SupportsDaylightSavingTime", SqlDbType.Bit);
foreach (var zone in TimeZoneInfo.GetSystemTimeZones())
{
Id.Value = zone.Id;
DisplayName.Value = zone.DisplayName;
SupportsDaylightSavingTime.Value = zone.SupportsDaylightSavingTime;
cmd.ExecuteNonQuery();
}
}
}
}
}
SQL Serverバージョン2016は、この問題を完全に解決します。以前のバージョンでは、おそらくCLRソリューションが最も簡単です。または、特定のDSTルール(USのみなど)の場合、T-SQL関数は比較的単純です。
ただし、一般的なT-SQLソリューションが可能になると思います。xp_regread
動作する限り、これを試してください:
CREATE TABLE #tztable (Value varchar(50), Data binary(56));
DECLARE @tzname varchar(150) = 'SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
EXEC master.dbo.xp_regread 'HKEY_LOCAL_MACHINE', @tzname, 'TimeZoneKeyName', @tzname OUT;
SELECT @tzname = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\' + @tzname
INSERT INTO #tztable
EXEC master.dbo.xp_regread 'HKEY_LOCAL_MACHINE', @tzname, 'TZI';
SELECT -- See http://msdn.microsoft.com/ms725481
CAST(CAST(REVERSE(SUBSTRING(Data, 1, 4)) AS binary(4)) AS int) AS BiasMinutes, -- UTC = local + bias: > 0 in US, < 0 in Europe!
CAST(CAST(REVERSE(SUBSTRING(Data, 5, 4)) AS binary(4)) AS int) AS ExtraBias_Std, -- 0 for most timezones
CAST(CAST(REVERSE(SUBSTRING(Data, 9, 4)) AS binary(4)) AS int) AS ExtraBias_DST, -- -60 for most timezones: DST makes UTC 1 hour earlier
-- When DST ends:
CAST(CAST(REVERSE(SUBSTRING(Data, 13, 2)) AS binary(2)) AS smallint) AS StdYear, -- 0 = yearly (else once)
CAST(CAST(REVERSE(SUBSTRING(Data, 15, 2)) AS binary(2)) AS smallint) AS StdMonth, -- 0 = no DST
CAST(CAST(REVERSE(SUBSTRING(Data, 17, 2)) AS binary(2)) AS smallint) AS StdDayOfWeek, -- 0 = Sunday to 6 = Saturday
CAST(CAST(REVERSE(SUBSTRING(Data, 19, 2)) AS binary(2)) AS smallint) AS StdWeek, -- 1 to 4, or 5 = last <DayOfWeek> of <Month>
CAST(CAST(REVERSE(SUBSTRING(Data, 21, 2)) AS binary(2)) AS smallint) AS StdHour, -- Local time
CAST(CAST(REVERSE(SUBSTRING(Data, 23, 2)) AS binary(2)) AS smallint) AS StdMinute,
CAST(CAST(REVERSE(SUBSTRING(Data, 25, 2)) AS binary(2)) AS smallint) AS StdSecond,
CAST(CAST(REVERSE(SUBSTRING(Data, 27, 2)) AS binary(2)) AS smallint) AS StdMillisec,
-- When DST starts:
CAST(CAST(REVERSE(SUBSTRING(Data, 29, 2)) AS binary(2)) AS smallint) AS DSTYear, -- See above
CAST(CAST(REVERSE(SUBSTRING(Data, 31, 2)) AS binary(2)) AS smallint) AS DSTMonth,
CAST(CAST(REVERSE(SUBSTRING(Data, 33, 2)) AS binary(2)) AS smallint) AS DSTDayOfWeek,
CAST(CAST(REVERSE(SUBSTRING(Data, 35, 2)) AS binary(2)) AS smallint) AS DSTWeek,
CAST(CAST(REVERSE(SUBSTRING(Data, 37, 2)) AS binary(2)) AS smallint) AS DSTHour,
CAST(CAST(REVERSE(SUBSTRING(Data, 39, 2)) AS binary(2)) AS smallint) AS DSTMinute,
CAST(CAST(REVERSE(SUBSTRING(Data, 41, 2)) AS binary(2)) AS smallint) AS DSTSecond,
CAST(CAST(REVERSE(SUBSTRING(Data, 43, 2)) AS binary(2)) AS smallint) AS DSTMillisec
FROM #tztable;
DROP TABLE #tztable
(複雑な)T-SQL関数は、このデータを使用して、現在のDSTルール中のすべての日付の正確なオフセットを決定できます。
DECLARE @TimeZone VARCHAR(50)
EXEC MASTER.dbo.xp_regread 'HKEY_LOCAL_MACHINE', 'SYSTEM\CurrentControlSet\Control\TimeZoneInformation', 'TimeZoneKeyName', @TimeZone OUT
SELECT @TimeZone
DECLARE @someUtcTime DATETIME
SET @someUtcTime = '2017-03-05 15:15:15'
DECLARE @TimeBiasAtSomeUtcTime INT
SELECT @TimeBiasAtSomeUtcTime = DATEDIFF(MINUTE, @someUtcTime, @someUtcTime AT TIME ZONE @TimeZone)
SELECT DATEADD(MINUTE, @TimeBiasAtSomeUtcTime * -1, @someUtcTime)
以下は、特定の英国アプリケーション向けに書かれた、純粋にSELECTに基づいた回答です。
夏時間の開始日の午前0時から午前1時の間は適用されません。これは修正できますが、作成されたアプリケーションでは必要ありません。
-- A variable holding an example UTC datetime in the UK, try some different values:
DECLARE
@App_Date datetime;
set @App_Date = '20250704 09:00:00'
-- Outputting the local datetime in the UK, allowing for daylight saving:
SELECT
case
when @App_Date >= dateadd(day, 1 - datepart(weekday, dateadd(day, -1, dateadd(month, 3, dateadd(year, datediff(year, 0, @App_Date), 0)))), dateadd(day, -1, dateadd(month, 3, dateadd(year, datediff(year, 0, @App_Date), 0))))
and @App_Date < dateadd(day, 1 - datepart(weekday, dateadd(day, -1, dateadd(month, 10, dateadd(year, datediff(year, 0, @App_Date), 0)))), dateadd(day, -1, dateadd(month, 10, dateadd(year, datediff(year, 0, @App_Date), 0))))
then DATEADD(hour, 1, @App_Date)
else @App_Date
end