TSQL: Yerel saati UTC'ye nasıl çevirebilirim? (SQL Server 2008)


82

Farklı saat dilimlerinden ve gün ışığından yararlanma saati ayarlarından global saat verilerini işlemesi gereken bir uygulama ile uğraşıyoruz. Buradaki fikir, her şeyi UTC formatında dahili olarak depolamak ve yalnızca yerelleştirilmiş kullanıcı arayüzleri için ileri geri dönüştürmektir. SQL Server, bir zaman, bir ülke ve bir saat dilimi verilen çevirilerle ilgilenmek için herhangi bir mekanizma sunuyor mu?

Bu yaygın bir sorun olmalı, bu yüzden Google'ın kullanışlı bir şey ortaya koymamasına şaşırdım.

Herhangi bir işaret var mı?


Mssql sunucum bir mysql sunucusuna bağlı. Sorgularda mysql CONVERT_TZ (time, srczone, dstzone) çalıştırmanın mümkün olup olmadığını merak ediyorum :-) Garip bu işlev eksik; linux içine yerleştirilmiştir.
Leif Neland

1
@BuschnicK Aşağıdaki cevabıma bakın. Aslında, başkalarının bulması daha kolay olacak şekilde bunu kabul edebileceğinizi düşünüyorum.
Piotr Owsiak

Kısa cevap: SQL Server 2016'dan önce bunu yapmanın yerleşik bir yolu yoktur, bu nedenle önceki sürümlerde özel kod gerektirir.
lehiester

Orta cevap: SQL Server 2016'dan önceki tüm zaman ofseti işlevselliği, Gün Işığından Yararlanma Saati nedeniyle çoğu saat diliminde meydana gelen değişken sapmaları desteklemeden yalnızca mutlak farklarla çalıştı. Sorgular, sunucunun yerel saatinin o anki ofseti olan ve dönüşümü otomatikleştirmeye çalışmak için yararsız olan dışında, zaman farklarına erişmenin herhangi bir yoluna sahip değildir.
lehiester

Yanıtlar:


49

7 yıl geçti ve ...
aslında tam olarak ihtiyacınız olanı yapan bu yeni SQL Server 2016 özelliği var.
AT TIME ZONE olarak adlandırılır ve DST (yaz saati uygulaması) değişikliklerini dikkate alarak tarihi belirli bir saat dilimine dönüştürür.
Daha fazla bilgi burada: https://msdn.microsoft.com/en-us/library/mt612795.aspx


19
Artık bunun üzerinde çalışmıyorum - o projede, SQL Server'da, aynı şirkette ve hatta aynı ülkede ;-) Bu yüzden bana yardımcı olmayacak, ancak bu soruyu bulan insanlara oy vereceğim şimdi.
BuschnicK

2
@BuschnicK evet, anladım, ama ben de seninle aynı soruna bir çözüm bulmaya geldim, bu yüzden gerçek bir çözüm olduğuna göre bir aswer yayınlamaya karar verdim: D
Piotr Owsiak

5
Soru SQL Server 2008 içindir. Bu yanıtı kabul ediyorsanız lütfen soruyu güncelleyin. Thx
Robert

1
UTC'ye çevirmek için 'AT TIME ZONE' UTC 'yapabilirsiniz.
Krzyserious

1
@Piotr Owsiak: Evet, ama giriş zamanının UTC olduğunu varsayıyor, bu da yerel saatten UTC'ye çevirmek istiyorsanız anlamsız ... Yani 7 yıl geçti ve hala bunun doğru bir şekilde işlemeye değmeyeceğini düşünüyorlar. ..
Stefan Steiger

65

Bu, şu anda SQL Server'ın ana bilgisayarıyla aynı UTC uzaklığına sahip tarihler için çalışır; gün ışığından yararlanma değişikliklerini hesaba katmaz. YOUR_DATEDönüştürülecek yerel tarihle değiştirin .

SELECT DATEADD(second, DATEDIFF(second, GETDATE(), GETUTCDATE()), YOUR_DATE);


2
Teşekkürler, bu iyi bir fikir, ancak yalnızca tam olarak bir saat dilimi için - yerel makinenin saat dilimi için çalışıyor. Yine de keyfi zaman dilimleri için çalışmasına ihtiyacımız var ...
BuschnicK

51
Bu, yaz saatini hesaba katmaz
Gabriel McAdams

10
Hayır! Fark, kesin tarihe bağlıdır. Gün ışığı tasarrufuna bağlıdır.
usr

3
Benim durumumda sadece 1 saat dilimine ihtiyacım vardı ve bu harika çalıştı! Teşekkürler.
M Thelen

10
Bugün koştuğunu biliyorsan bu iyi çalışıyor, teşekkürler
David Adlington

22

Bu cevaplardan birkaçı sizi oyun sahasına sokacak olsa da, yaz saati uygulaması nedeniyle SqlServer 2005 ve öncesi için rastgele tarihlerle yapmaya çalıştığınız şeyi yapamazsınız. Mevcut yerel ve mevcut UTC arasındaki farkı kullanmak, bana bugün olduğu gibi ofseti verecektir. Söz konusu tarih için ofsetin ne olacağını belirlemenin bir yolunu bulamadım.

Bununla birlikte, SqlServer 2008'in bu sorunu çözebilecek bazı yeni tarih işlevleri sağladığını biliyorum, ancak daha eski bir sürümü kullananların sınırlamalardan haberdar olması gerekiyor.

Yaklaşımımız, UTC'yi kalıcı kılmak ve dönüşümü, dönüşümün doğruluğu üzerinde daha fazla kontrole sahip olduğumuz müşteri tarafında gerçekleştirmektir.


16

SQL Server 2016 ve daha yeni sürümler ile Azure SQL Veritabanı için yerleşik AT TIME ZONEifadeyi kullanın .

SQL Server'ın eski sürümleri için, burada listelendiği gibi IANA standart saat dilimleri arasında dönüştürme yapmak için SQL Server Saat Dilimi Desteği projemi kullanabilirsiniz .

UTC'den Yerel'e şu şekildedir:

SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')

UTC'ye yerel şu şekildedir:

SELECT Tzdb.LocalToUtc('2015-07-01 00:00:00', 'America/Los_Angeles', 1, 1)

Sayısal seçenekler, yerel saat değerleri gün ışığından yararlanma saatinden etkilendiğinde davranışı kontrol etmeye yönelik işaretlerdir. Bunlar proje belgelerinde ayrıntılı olarak açıklanmıştır.


1
Ne kadar adamım ... Matt'in bu projesi harika ve CLR gerektirmiyor. Matt bu +100 için çok övgüyü hak ediyor
buckley

14

SQL Server 2008 adında bir türe sahiptir datetimeoffset. Bu tür şeyler için gerçekten yararlıdır.

http://msdn.microsoft.com/en-us/library/bb630289.aspx

Daha sonra işlevi SWITCHOFFSETbir saat diliminden diğerine taşımak, ancak yine de aynı UTC değerini korumak için kullanabilirsiniz.

http://msdn.microsoft.com/en-us/library/bb677244.aspx

Rob


6
SWITCHOFFSET gün ışığından yararlanmayı hesaba katmaz, bu nedenle yalnızca bazı durumlarda kullanışlıdır.
robocat

2
Hayır. Ancak soru, talep edilen saat dilimine geçiş yapabilmekle ilgiliydi.
Rob Farley

"Farklı saat dilimleri ve yaz saati uygulaması" sorusundan. Biz de yerel zamanlar için bir çözüm arıyoruz. Öneriniz gün ışığından yararlanma sorununu çözmüyor, değil mi?
robocat

Bir istemcide saat dilimini tespit etmeniz ve ardından belirtilen saat diliminde veritabanı dönüş zamanlarına sahip olmanız gerektiğinde, SWITCHOFFSET işlevi çok kullanışlıdır.
Rob Farley 13

3
@RobFarley İstemcide saat dilimini tespit etmek ve SWITCHOFFSET kullanmak yine de işleri yanlış yapabilir. Dönüştürmekte olduğunuz tarih ve saate Yaz Saati Uygulamasının uygulanıp uygulanmadığını bilmeniz gerekir. Basitçe saat dilimini tespit etmek ve geçerli farkı UTC'ye uygulamak bir saat kapalı olabilir - ve bu, tüm dönüşümlerinizin aynı ülkede olduğu basit bir durumda. Her ülke aynı tarihlerde gün ışığından yararlanma saatine geçiş yapmaz. SWITCHOFFSET, yerel saati kaydederseniz ve aynı ülke içindeki orijinal ve hedef bölge arasındaki farkı bilirseniz iyi çalışır.
Jamie Bkz.

12

İşte bir bölgeyi DateTimebaşka bir bölgeye dönüştürmek için kodDateTime

DECLARE @UTCDateTime DATETIME = GETUTCDATE();
DECLARE @ConvertedZoneDateTime DATETIME;

-- 'UTC' to 'India Standard Time' DATETIME
SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time'
SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS IndiaStandardTime

-- 'India Standard Time' to 'UTC' DATETIME
SET @UTCDateTime = @ConvertedZoneDateTime AT TIME ZONE 'India Standard Time' AT TIME ZONE 'UTC'
SELECT @ConvertedZoneDateTime AS IndiaStandardTime,@UTCDateTime AS UTCDATE

Not : AT TIME ZONE yalnızca SQL Server 2016+ üzerinde çalışır ve avantajı, belirli bir Saat dilimine dönüştürürken Gün Işığını otomatik olarak dikkate almasıdır.


2
Bunu seviyorum, eğer birden fazla AT TIME ZONEaramayı ( kelime öbeği?) Birbirine zincirleyebileceğinizi gösterdiği gerçeğinden başka bir sebep yoksa ! Tek kelimeyle zarif. Daha önce stackoverflow.com/a/44579178/112764'ün ihtiyaçlarımı karşıladığını söylemiştim , ancak bu daha da iyi. Büyük tebrikler.
NateJ

DECLARE @UTCDateTime DATETIME = GETUTCDATE(); DECLARE @ConvertedZoneDateTime DATETIME; -- 'UTC' to 'India Standard Time' to 'Eastern Standard Time' DATETIME SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time' AT TIME ZONE 'Eastern Standard Time' SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS EasternStandardTime Evet, birden fazla AT TIME ZONEaramayı zincirleyebilirsiniz , ancak Gönderen ve Bitiş herhangi bir dönüşüm için yeterlidir ve en çok ihtiyacımız olan
KarthikeyanMlp

4

Yerel bir olayla ilgili olmayan tüm tarih-saat depolaması için DateTimeOffset kullanmaya eğilimliyim (yani: toplantı / parti, vb, müzede 12: 00-15: 00).

Mevcut DTO'yu UTC olarak almak için:

DECLARE @utcNow DATETIMEOFFSET = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME())
DECLARE @utcToday DATE = CONVERT(DATE, @utcNow);
DECLARE @utcTomorrow DATE = DATEADD(D, 1, @utcNow);
SELECT  @utcToday [today]
        ,@utcTomorrow [tomorrow]
        ,@utcNow [utcNow]

NOT: Tel üzerinden gönderirken her zaman UTC'yi kullanacağım ... istemci tarafı JS yerel UTC'ye / buradan kolayca alabilir. Görmek:new Date().toJSON() ...

Aşağıdaki JS, bir UTC / GMT tarihinin ISO8601 biçiminde yerel bir tarih saatine ayrıştırılmasını gerçekleştirecektir.

if (typeof Date.fromISOString != 'function') {
  //method to handle conversion from an ISO-8601 style string to a Date object
  //  Date.fromISOString("2009-07-03T16:09:45Z")
  //    Fri Jul 03 2009 09:09:45 GMT-0700
  Date.fromISOString = function(input) {
    var date = new Date(input); //EcmaScript5 includes ISO-8601 style parsing
    if (!isNaN(date)) return date;

    //early shorting of invalid input
    if (typeof input !== "string" || input.length < 10 || input.length > 40) return null;

    var iso8601Format = /^(\d{4})-(\d{2})-(\d{2})((([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d{1,12}))?)?)?)?)?([Zz]|([-+])(\d{2})\:?(\d{2}))?$/;

    //normalize input
    var input = input.toString().replace(/^\s+/,'').replace(/\s+$/,'');

    if (!iso8601Format.test(input))
      return null; //invalid format

    var d = input.match(iso8601Format);
    var offset = 0;

    date = new Date(+d[1], +d[2]-1, +d[3], +d[7] || 0, +d[8] || 0, +d[10] || 0, Math.round(+("0." + (d[12] || 0)) * 1000));

    //use specified offset
    if (d[13] == 'Z') offset = 0-date.getTimezoneOffset();
    else if (d[13]) offset = ((parseInt(d[15],10) * 60) + (parseInt(d[16],10)) * ((d[14] == '-') ? 1 : -1)) - date.getTimezoneOffset();

    date.setTime(date.getTime() + (offset * 60000));

    if (date.getTime() <= new Date(-62135571600000).getTime()) // CLR DateTime.MinValue
      return null;

    return date;
  };
}

+1 Ben de DateTimeOffset'e geçtim. UTC + yerel dönüşümlerle ilgili bir dizi sorunu önler. Bununla birlikte, benzer nedenlerden ötürü, tel üzerinden ofset içeren değerler (JSON aracılığıyla) göndermenizi de öneririm.
user2864740

Bir not olarak, sizin yaptığınız şeyi tam tersi şekilde yaparım . Yerel bir olaya bağlı DateTimes için bir DateTimeOffset depoluyorum. Yerel bir olaya bağlı olmayan bir DateTime için, UTC'de bir DateTime depoluyorum. İlki iki ilgili veri noktasına sahiptir (yerel saatte ne zaman ve bu hangi yerel saattir), ikincisi ise (ne zaman)
Martijn

@Martijn Ancak saat dilimi size konumu vermiyor ve yine de ayrı olarak kaydetmeniz gerekiyor.
Tracker1

@ tracker1 Bu, yalnızca konumun saat dilimini bilmesinin bir yolu olduğunda çalışır ve o zaman bile dönüştürmek için tam bir acıdır.
Martijn

3

Evet, burada detaylandırıldığı gibi bir dereceye kadar .
Kullandığım yaklaşım (2008 öncesi), dönüştürmeyi DB'ye eklemeden önce .NET iş mantığında yapmaktır.


1

UTC tarih saatini almak için GETUTCDATE () işlevini kullanabilirsiniz Muhtemelen GETUTCDATE () ile GETDATE () arasındaki farkı seçebilir ve bu farkı tarihlerinizi UTC'ye ayarlamak için kullanabilirsiniz.

Ancak, iş katmanında (örneğin .NET'te) doğru tarih saatini kontrol etmenin çok daha kolay olduğu konusunda önceki mesaja katılıyorum.


12
Hayır! Fark, kesin tarihe bağlıdır. Gün ışığı tasarrufuna bağlıdır.
usr

3
Gün ışığından yararlanmayı hesaba katmaz. Bir süredir bunun gibi çözümler kullanıyordum ve büyük sorunlara neden oldu. Karşılaştırma yaptığınız tarihin DST'de olup olmadığını belirlemelisiniz.
Jeff Davis

-1

Örnek kullanım:

SELECT
    Getdate=GETDATE()
    ,SysDateTimeOffset=SYSDATETIMEOFFSET()
    ,SWITCHOFFSET=SWITCHOFFSET(SYSDATETIMEOFFSET(),0)
    ,GetutcDate=GETUTCDATE()
GO

İadeler:

Getdate SysDateTimeOffset   SWITCHOFFSET    GetutcDate
2013-12-06 15:54:55.373 2013-12-06 15:54:55.3765498 -08:00  2013-12-06 23:54:55.3765498 +00:00  2013-12-06 23:54:55.373
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.