TSQL: Làm thế nào để chuyển đổi giờ địa phương sang UTC? (SQL Server 2008)


82

Chúng tôi đang xử lý một ứng dụng cần xử lý dữ liệu thời gian toàn cầu từ các múi giờ khác nhau và cài đặt giờ tiết kiệm ánh sáng ban ngày. Ý tưởng là lưu trữ mọi thứ ở định dạng UTC trong nội bộ và chỉ chuyển đổi qua lại cho các giao diện người dùng được bản địa hóa. SQL Server có cung cấp bất kỳ cơ chế nào để xử lý các bản dịch được cung cấp cho một thời gian, quốc gia và múi giờ không?

Đây hẳn là một vấn đề phổ biến, vì vậy tôi ngạc nhiên khi google sẽ không đưa ra bất cứ thứ gì có thể sử dụng được.

Bất kỳ gợi ý?


Tôi có máy chủ mssql của mình được liên kết với máy chủ mysql. Tôi tự hỏi liệu có thể chạy mysql CONVERT_TZ (thời gian, srczone, dstzone) trên các truy vấn không :-) Kỳ lạ là chức năng này bị thiếu; nó được xây dựng trong linux.
Leif Neland

1
@BuschnicK Xem câu trả lời của tôi bên dưới. Trên thực tế, tôi nghĩ bạn có thể chấp nhận nó ngay cả khi người khác tìm thấy nó dễ dàng hơn.
Piotr Owsiak

Câu trả lời ngắn gọn: không có cách tích hợp nào để thực hiện việc này trước SQL Server 2016, vì vậy nó sẽ yêu cầu mã tùy chỉnh trên các phiên bản trước đó.
lehiester

Câu trả lời trung bình: tất cả các chức năng bù đắp thời gian trước SQL Server 2016 chỉ hoạt động với hiệu số tuyệt đối, không hỗ trợ hiệu số biến đổi xảy ra trong hầu hết các múi giờ do Giờ tiết kiệm ánh sáng ban ngày. Các truy vấn không có bất kỳ cách nào để truy cập khoảng cách thời gian ngoại trừ bất kỳ điều gì xảy ra là khoảng thời gian bù giờ địa phương hiện tại của máy chủ, điều này vô ích khi cố gắng tự động chuyển đổi.
lehiester 10/09/18

Câu trả lời:


49

7 năm trôi qua và ...
thực sự có tính năng SQL Server 2016 mới này thực hiện chính xác những gì bạn cần.
Nó được gọi là AT TIME ZONE và nó chuyển đổi ngày thành một múi giờ cụ thể xem xét các thay đổi DST (giờ tiết kiệm ánh sáng ban ngày).
Thông tin thêm tại đây: https://msdn.microsoft.com/en-us/library/mt612795.aspx


19
Không còn làm việc này nữa - không phải ở dự án đó, không phải ở SQL Server, không ở cùng một công ty và thậm chí không ở cùng một quốc gia ;-) Vì vậy, nó sẽ không giúp tôi, nhưng tôi sẽ ủng hộ nó cho những người tìm thấy câu hỏi này hiện nay.
BuschnicK

2
@BuschnicK vâng, tôi hiểu rồi, nhưng tôi đến đây để tìm kiếm giải pháp cho vấn đề tương tự như bạn gặp phải nên tôi quyết định đăng một aswer bây giờ rằng có một giải pháp thực sự: D
Piotr Owsiak

5
Câu hỏi dành cho SQL Server 2008. Vui lòng cập nhật câu hỏi nếu bạn chấp nhận câu trả lời này. Cám ơn
Robert

1
Để chuyển đổi sang UTC, bạn có thể thực hiện 'AT TIME ZONE' UTC '.
Krzyserious

1
@Piotr Owsiak: Đúng vậy, nhưng nó giả định rằng thời gian đầu vào là UTC, điều này thật vô nghĩa nếu bạn muốn chuyển đổi từ giờ địa phương sang UTC ... Vậy là đã 7 năm trôi qua, và họ vẫn không nghĩ rằng điều này đáng để xử lý đúng cách. ..
Stefan Steiger

65

Điều này hoạt động cho các ngày hiện có cùng độ lệch UTC như máy chủ của SQL Server; nó không tính đến những thay đổi tiết kiệm ánh sáng ban ngày. Thay thế YOUR_DATEbằng ngày địa phương để chuyển đổi.

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


2
Cảm ơn, đây là một ý tưởng hay nhưng nó chỉ hoạt động cho đúng một múi giờ - của máy cục bộ. Mặc dù vậy, chúng tôi cần nó hoạt động cho các múi giờ tùy ý ...
BuschnicK

51
Điều này không chiếm thời gian tiết kiệm ánh sáng ban ngày
Gabriel McAdams

10
Không! Sự khác biệt phụ thuộc vào ngày chính xác. Nó phụ thuộc vào tiết kiệm ánh sáng ban ngày.
usr

3
Trong trường hợp của tôi, tôi chỉ cần 1 múi giờ và điều này rất hiệu quả! Cảm ơn.
M Thelen

10
Điều này hoạt động tốt nếu bạn biết ngày hôm nay của bạn không phải là lịch sử, cảm ơn
David Adlington

22

Mặc dù một số câu trả lời sẽ đưa bạn đến sân chơi bóng, nhưng bạn không thể làm những gì bạn đang cố gắng làm với các ngày tùy ý cho SqlServer 2005 trở về trước vì thời gian tiết kiệm ánh sáng ban ngày. Sử dụng sự khác biệt giữa UTC cục bộ hiện tại và UTC hiện tại sẽ cung cấp cho tôi phần bù như nó tồn tại ngày nay. Tôi chưa tìm ra cách xác định mức bù đắp cho ngày được đề cập.

Điều đó nói rằng, tôi biết rằng SqlServer 2008 cung cấp một số chức năng ngày mới có thể giải quyết vấn đề đó, nhưng những người sử dụng phiên bản cũ hơn cần phải biết những hạn chế.

Cách tiếp cận của chúng tôi là duy trì UTC và thực hiện chuyển đổi ở phía khách hàng, nơi chúng tôi có nhiều quyền kiểm soát hơn đối với độ chính xác của chuyển đổi.


16

Đối với SQL Server 2016 trở lên và Cơ sở dữ liệu Azure SQL, hãy sử dụng AT TIME ZONEcâu lệnh dựng sẵn .

Đối với các phiên bản cũ hơn của SQL Server, bạn có thể sử dụng dự án Hỗ trợ múi giờ máy chủ SQL của tôi để chuyển đổi giữa các múi giờ chuẩn IANA, như được liệt kê ở đây .

UTC thành Local giống như sau:

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

Local to UTC là như thế này:

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

Các tùy chọn số là cờ để kiểm soát hành vi khi giá trị giờ địa phương bị ảnh hưởng bởi thời gian tiết kiệm ánh sáng ban ngày. Những điều này được mô tả chi tiết trong tài liệu của dự án.


1
Thật tuyệt vời ... dự án này của Matt rất tuyệt và không yêu cầu CLR. Matt xứng đáng tín dụng nhiều cho +100 này
buckley

14

SQL Server 2008 có một kiểu được gọi là datetimeoffset. Nó thực sự hữu ích cho loại công cụ này.

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

Sau đó, bạn có thể sử dụng chức năng SWITCHOFFSETđể di chuyển nó từ múi giờ này sang múi giờ khác, nhưng vẫn giữ nguyên giá trị UTC.

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

Rob


6
SWITCHOFFSET không tính đến việc tiết kiệm ánh sáng ban ngày, vì vậy nó chỉ hữu ích trong một số trường hợp.
robocat

2
Không. Nhưng câu hỏi là về việc có thể xử lý việc chuyển sang bất kỳ múi giờ nào được yêu cầu.
Rob Farley

Từ câu hỏi "các múi giờ khác nhau và cài đặt giờ tiết kiệm ánh sáng ban ngày". Chúng tôi cũng đang tìm kiếm một giải pháp cho thời gian địa phương. Đề xuất của bạn không giải quyết được vấn đề tiết kiệm ánh sáng ban ngày phải không?
robocat

Bất kỳ lúc nào khi bạn cần phát hiện múi giờ tại một máy khách và sau đó có thời gian trả về cơ sở dữ liệu trong múi giờ được chỉ định, hàm SWITCHOFFSET rất hữu ích.
Rob Farley

3
@RobFarley Việc phát hiện múi giờ tại ứng dụng khách và sử dụng SWITCHOFFSET vẫn có thể xảy ra sai sót. Bạn cần biết ngày và giờ bạn đang chuyển đổi có được áp dụng Giờ tiết kiệm ánh sáng ban ngày hay không. Chỉ cần phát hiện múi giờ và áp dụng mức chênh lệch hiện tại cho UTC có thể mất một giờ - và đó là trong trường hợp đơn giản khi tất cả chuyển đổi của bạn ở cùng một quốc gia. Không phải mọi quốc gia đều chuyển sang / từ giờ tiết kiệm ánh sáng ban ngày vào cùng một ngày. SWITCHOFFSET hoạt động hiệu quả nếu bạn lưu trữ giờ địa phương và biết sự khác biệt giữa vùng gốc và vùng đích trong cùng một quốc gia.
JamieSee

12

Đây là mã để chuyển đổi vùng này DateTimesang vùng khácDateTime

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

Lưu ý : AT TIME ZONE chỉ hoạt động trên SQL Server 2016+ và ưu điểm là nó tự động xem xét Daylight khi chuyển đổi sang một múi giờ cụ thể


2
Tôi thích điều này, nếu không vì lý do nào khác ngoài thực tế là nó cho thấy bạn có thể xâu chuỗi nhiều AT TIME ZONEcuộc gọi (cụm từ?) Lại với nhau! Đơn giản là thanh lịch. Tôi đã nói trước đó rằng stackoverflow.com/a/44579178/112764 đáp ứng nhu cầu của tôi, nhưng điều này thậm chí còn tốt hơn. Kudo lớn.
NateJ 19/07/18

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 Có, bạn có thể gắn nhiều AT TIME ZONEcuộc gọi, nhưng TừĐể là đủ cho bất kỳ chuyển đổi và hầu hết chúng ta cần
KarthikeyanMlp

4

Tôi có xu hướng sử dụng DateTimeOffset cho tất cả bộ lưu trữ ngày-giờ không liên quan đến sự kiện địa phương (ví dụ: cuộc họp / bữa tiệc, v.v., 12 giờ đêm - 3 giờ chiều tại bảo tàng).

Để nhận DTO hiện tại dưới dạng UTC:

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]

LƯU Ý: Tôi sẽ luôn sử dụng UTC khi gửi qua đường dây ... JS phía máy khách có thể dễ dàng truy cập / từ UTC cục bộ. Xem: new Date().toJSON()...

JS sau sẽ xử lý phân tích cú pháp một ngày UTC / GMT ở định dạng ISO8601 thành một ngày giờ cục bộ.

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 Tôi cũng đã chuyển sang DateTimeOffset. Nó tránh một số vấn đề với chuyển đổi địa phương + UTC. Tuy nhiên, vì những lý do tương tự, tôi khuyên bạn nên gửi các giá trị bằng offset over-the-wire (thông qua JSON).
user2864740

Là một lưu ý, tôi làm những gì bạn làm chính xác xung quanh theo cách khác. Đối với các DateTimes được gắn với một sự kiện cục bộ, tôi lưu trữ một DateTimeOffset. Đối với DateTime không gắn liền với sự kiện cục bộ, tôi lưu trữ DateTime ở UTC. Các cựu có hai datapoints liên quan (khi là nó theo giờ địa phương, và những gì theo giờ địa phương là thế), sau này chỉ có một (khi là nó)
Martijn

@Martijn Nhưng múi giờ không cung cấp cho bạn vị trí và bạn phải lưu trữ riêng biệt.
Tracker 1

@ tracker1 Điều đó chỉ hoạt động khi vị trí có cách biết múi giờ của nó và thậm chí sau đó, việc chuyển đổi hoàn toàn khó khăn.
Martijn

3

Có, ở một mức độ nào đó như chi tiết ở đây .
Cách tiếp cận mà tôi đã sử dụng (trước năm 2008) là thực hiện chuyển đổi trong logic nghiệp vụ .NET trước khi chèn vào DB.


1

Bạn có thể sử dụng hàm GETUTCDATE () để lấy ngày giờ UTC Có thể bạn có thể chọn sự khác biệt giữa GETUTCDATE () và GETDATE () và sử dụng sự khác biệt này để điều chỉnh ngày của bạn thành UTC

Nhưng tôi đồng ý với thông báo trước đó, rằng việc kiểm soát ngày giờ phù hợp trong lớp nghiệp vụ sẽ dễ dàng hơn nhiều (ví dụ: trong .NET).


12
Không! Sự khác biệt phụ thuộc vào ngày chính xác. Nó phụ thuộc vào tiết kiệm ánh sáng ban ngày.
usr

3
Không tính cho tiết kiệm ánh sáng ban ngày. Tôi đã sử dụng các giải pháp như thế này một thời gian và nó gây ra các vấn đề lớn. Bạn phải xác định xem ngày bạn đang so sánh có tính theo DST hay không.
Jeff Davis

-1

Sử dụng mẫu:

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

Lợi nhuận:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.