DST से पहले या बाद की तारीख के लिए मुझे UTC और स्थानीय समय के बीच सही ऑफ़सेट कैसे मिल सकता है?


29

मैं वर्तमान में UTC डेटाइम से स्थानीय डेटाइम प्राप्त करने के लिए निम्नलिखित का उपयोग करता हूं:

SET @offset = DateDiff(minute, GetUTCDate(), GetDate())
SET @localDateTime = DateAdd(minute, @offset, @utcDateTime)

मेरी समस्या यह है कि यदि दिन के उजाले की बचत समय के बीच होती है GetUTCDate()और @utcDateTime, @localDateTimeएक घंटा समाप्त हो जाती है।

क्या किसी तारीख के लिए utc से स्थानीय समय में बदलने का एक आसान तरीका है जो वर्तमान तारीख नहीं है?

मैं SQL Server 2005 का उपयोग कर रहा हूँ

जवाबों:


18

गैर-वर्तमान यूटीसी तिथि को स्थानीय समय में बदलने का सबसे अच्छा तरीका सीएलआर का उपयोग करना है। कोड ही आसान है; कठिन हिस्सा आमतौर पर लोगों को समझा रहा है कि सीएलआर शुद्ध बुराई या डरावना नहीं है ...

कई उदाहरणों में से एक के लिए, इस विषय पर हर्ष चावला के ब्लॉग पोस्ट को देखें

दुर्भाग्य से, इसमें कुछ भी अंतर्निहित नहीं है जो इस प्रकार के रूपांतरण को संभाल सकता है, सीएलआर-आधारित समाधानों के लिए बचा सकता है। आप एक टी-एसक्यूएल फ़ंक्शन लिख सकते हैं जो ऐसा कुछ करता है, लेकिन फिर आपको तिथि-परिवर्तन तर्क को लागू करना होगा, और मुझे लगता है कि निश्चित रूप से आसान नहीं होगा।


समय के साथ क्षेत्रीय विविधताओं की वास्तविक जटिलता को देखते हुए, यह कहना कि "निश्चित रूप से आसान नहीं है" शुद्ध टी-एसक्यूएल में यह प्रयास करना शायद इसे समझ रहा है; ;-) तो हाँ, SQLCLR इस ऑपरेशन को करने का एकमात्र विश्वसनीय और कुशल साधन है। उसके लिए +1। FYI करें: लिंक की गई ब्लॉग पोस्ट कार्यात्मक रूप से सही है लेकिन सर्वोत्तम प्रथाओं का पालन नहीं करती है इसलिए दुर्भाग्यपूर्ण है। UTC और सर्वर स्थानीय समय के बीच परिवर्तित करने के लिए कार्य SQL # लाइब्रेरी (जो मैं लेखक हूं) में उपलब्ध हैं, लेकिन नि: शुल्क संस्करण में नहीं
सोलोमन रटज़की 16

1
सीएलआर को बुराई मिलती है जब इसे जोड़ा जाना चाहिए WITH PERMISSION_SET = UNSAFE। कुछ वातावरण इसे AWS RDS की तरह अनुमति नहीं देते हैं। और यह, ठीक है, असुरक्षित है। दुर्भाग्य से, वहाँ कोई .Net समय क्षेत्र पूर्ण कार्यान्वयन नहीं है जो unsafeअनुमति के बिना उपयोग करने योग्य है । यहाँ और यहाँ देखें ।
Frédéric

15

मैंने Microsoft SQL सर्वर में डेटाइम और टाइमज़ोन हैंडलिंग के साथ संघर्ष करने वाले किसी भी व्यक्ति की मदद करने के लिए कोडप्लेक्स पर टी-एसक्यूएल टूलबॉक्स प्रोजेक्ट को विकसित और प्रकाशित किया है । यह खुला स्रोत है और पूरी तरह से उपयोग करने के लिए स्वतंत्र है।

यह बॉक्स से बाहर पूर्व-भरे कॉन्फ़िगरेशन तालिकाओं के साथ सादे टी-एसक्यूएल (कोई सीएलआर) का उपयोग करके आसान डेटाटाइम रूपांतरण यूडीएफ प्रदान करता है। और इसमें पूर्ण DST (दिन के समय की बचत का समय) का समर्थन है।

सभी समर्थित टाइमज़ोन की एक सूची तालिका में मिल सकती है "DateTimeUtil.Timezone" (टी-एसक्यूएल टूलबॉक्स डेटाबेस के भीतर प्रदान की गई)।

अपने उदाहरण में, आप निम्नलिखित नमूने का उपयोग कर सकते हैं:

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
)

यह परिवर्तित स्थानीय डेटाइम मान लौटाएगा।

दुर्भाग्य से, यह SQL Server 2008 या बाद में केवल नए डेटा प्रकारों (DATE, TIME, DATETIME2) के कारण समर्थित है। लेकिन जब पूर्ण स्रोत कोड प्रदान किया जाता है, तो आप DATETIME द्वारा टेबल और UDF को आसानी से बदलकर समायोजित कर सकते हैं। मेरे पास परीक्षण के लिए एक MSSQL 2005 उपलब्ध नहीं है, लेकिन इसे MSSQL 2005 के साथ भी काम करना चाहिए। सवालों के मामले में, बस मुझे बताएं।


12

मैं हमेशा इस TSQL कमांड का उपयोग करता हूं।

-- the utc value 
declare @utc datetime = '20/11/2014 05:14'

-- the local time

select DATEADD(hh, DATEDIFF(hh, getutcdate(), getdate()), @utc)

यह बहुत सरल है और यह काम करता है।


2
ऐसे टाइमज़ोन हैं जो UTC से पूरे एक घंटे की ऑफ़सेट नहीं हैं इसलिए DATEPART का उपयोग करने से आपको समस्या हो सकती है।
माइकल ग्रीन

4
माइकल ग्रीन की टिप्पणी के बारे में, आप इसे SELATE DATEADD (MINUTE, DatedIFF (MINUTE, GETUTCDATE) (), GETDATE ()), @utc) में बदलकर संबोधित कर सकते हैं।
पंजीकृत उपयोगकर्ता

4
यह काम नहीं करता है क्योंकि आप केवल यह निर्धारित कर रहे हैं कि वर्तमान समय डीएसटी है या नहीं, फिर ऐसे समय की तुलना करना जो डीएसटी हो सकता है या नहीं। यूके में आपके उपरोक्त उदाहरण कोड और डेटाटाइम का उपयोग करते हुए वर्तमान में मुझे बताया गया है कि यह सुबह 6:14 होना चाहिए, हालांकि नवंबर डीएसटी के बाहर है, इसलिए इसे 5:14 बजे जीएमटी और यूटीसी संयोग के रूप में होना चाहिए।
मैट

जब तक मैं इससे सहमत नहीं होता, तब तक यह वास्तविक प्रश्न को संबोधित नहीं करता है, जहां तक ​​इस उत्तर का सवाल है कि मुझे लगता है कि निम्नलिखित बेहतर है: सेलेक्ट DATEADD (MINUTE, DATEPART (TZoffset, SYSDATETIMEOFFSET ()), @utc)
Eamon

@ लूडो बर्नर्ट्स: पहला उपयोग मिलीसेकंड, दूसरा: यह काम नहीं करता है क्योंकि आज यूटीसी ऑफसेट एक निश्चित समय पर यूटीसी-ऑफसेट की तुलना में अलग हो सकता है (दिन के उजाले की बचत - गर्मियों बनाम सर्दियों के समय) ...
क्वैंडरी

11

मुझे यह उत्तर StackOverflow पर मिला है जो एक यूज़र डिफ़ाइंड फ़ंक्शन प्रदान करता है जो डेटाेटाइम का सटीक अनुवाद करता है

केवल एक चीज जिसे आपको संशोधित करने की आवश्यकता है वह @offsetइस फ़ंक्शन को चलाने वाले SQL सर्वर के टाइमज़ोन ऑफ़सेट पर सेट करने के लिए शीर्ष पर चर है। मेरे मामले में, हमारा SQL सर्वर EST का उपयोग करता है, जो GMT - 5 है

यह सही नहीं है और संभवत: ऐसे कई मामलों के लिए काम नहीं करेगा, जिनमें आधे-घंटे या 15 मिनट के टीएस ऑफ़सेट हैं (उन लोगों के लिए जो केविन की तरह सीएलआर फ़ंक्शन की सिफारिश करेंगे ), हालांकि यह उत्तर में सबसे सामान्य समय क्षेत्रों के लिए पर्याप्त रूप से काम करता है अमेरिका।

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

4

SQL सर्वर 2016+ के लिए, आप एटी टाइम ज़ोन का उपयोग कर सकते हैं । यह स्वचालित रूप से दिन की रोशनी की बचत समय को संभाल लेगा ।



3

स्टैक ओवरफ्लो पर पूछे गए एक समान प्रश्न के कुछ अच्छे उत्तर हैं । मैं बॉब अलब्राइट द्वारा एक डेटा रूपांतरण सलाहकार द्वारा उत्पन्न गड़बड़ी को साफ करने के लिए दूसरे जवाब से एक टी-एसक्यूएल दृष्टिकोण का उपयोग करके घाव करता हूं ।

इसने हमारे लगभग सभी डेटा के लिए काम किया, लेकिन फिर मुझे बाद में एहसास हुआ कि उसका एल्गोरिथ्म केवल दिनांक 5 अप्रैल 1987 तक काम करता है , और हमारे पास 1940 के दशक की कुछ तारीखें थीं जो अभी भी ठीक से परिवर्तित नहीं हुई थीं। अंततः हमें UTCअपने SQL सर्वर डेटाबेस में तारीखों की आवश्यकता थी ताकि तीसरे पक्ष के कार्यक्रम में एल्गोरिथ्म के साथ लाइन बनाई जा सके जिसने UTCस्थानीय समय से बदलने के लिए जावा एपीआई का उपयोग किया ।

मैं जैसे CLRउदाहरण के ऊपर केविन Feasel के जवाब में हर्ष चावला के उदाहरण का उपयोग, और मैं भी यह एक समाधान है, जावा का उपयोग करता है के बाद से हमारे सामने अंत करने के लिए जावा का उपयोग करता है करने के लिए की तुलना करना चाहते UTCस्थानीय समय रूपांतरण के लिए।

विकिपीडिया में 8 अलग-अलग संवैधानिक संशोधनों का उल्लेख है, जिसमें 1987 से पहले के समय क्षेत्र समायोजन शामिल हैं, और उनमें से कई अलग-अलग राज्यों में बहुत स्थानीय हैं, इसलिए एक मौका है कि सीएलआर और जावा उन्हें अलग तरीके से व्याख्या कर सकते हैं। क्या आपका फ्रंट-एंड एप्लिकेशन कोड डॉटनेट या जावा का उपयोग करता है, या आपके लिए 1987 से पहले की तारीखें हैं?


2

आप इसे सीएलआर संग्रहित प्रक्रिया के साथ आसानी से कर सकते हैं।

[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();
                }
            }
        }
    }
}

सीएलआर को बुराई मिलती है जब इसे जोड़ा जाना चाहिए WITH PERMISSION_SET = UNSAFE। कुछ वातावरण इसे AWS RDS की तरह अनुमति नहीं देते हैं। और यह, ठीक है, असुरक्षित है। दुर्भाग्य से, वहाँ कोई .Net समय क्षेत्र पूर्ण कार्यान्वयन नहीं है जो unsafeअनुमति के बिना उपयोग करने योग्य है । यहाँ और यहाँ देखें ।
Frédéric

2

SQL सर्वर संस्करण 2016 इस समस्या को एक बार और सभी के लिए हल कर देगा । पूर्व संस्करणों के लिए एक सीएलआर समाधान शायद सबसे आसान है। या एक विशिष्ट डीएसटी नियम (जैसे केवल यूएस) के लिए, एक टी-एसक्यूएल फ़ंक्शन अपेक्षाकृत सरल हो सकता है।

हालाँकि, मुझे लगता है कि एक सामान्य 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

ए (जटिल) टी-एसक्यूएल फ़ंक्शन इस डेटा का उपयोग वर्तमान डीएसटी नियम के दौरान सभी तिथियों के लिए सटीक ऑफसेट निर्धारित करने के लिए कर सकता है ।


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

2
हाय जोस्ट! पोस्ट करने का शुक्रिया। यदि आप अपने उत्तर में कुछ स्पष्टीकरण जोड़ते हैं, तो यह समझने में बहुत आसान साबित हो सकता है।
लोवलीबा जुना

2

यहाँ एक विशिष्ट यूके एप्लिकेशन के लिए लिखा गया उत्तर है और पूरी तरह से SELECT पर आधारित है।

  1. कोई समयक्षेत्र ऑफसेट (जैसे यूके)
  2. मार्च के अंतिम रविवार से शुरू होने वाली बचत और अक्टूबर के अंतिम रविवार (ब्रिटेन के नियमों) को पूरा करने के लिए लिखा गया
  3. दिन के उजाले की बचत शुरू होने पर मध्यरात्रि और 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

आप छोटे लोगों के बजाय लंबी तिथि वाले भाग के नामों का उपयोग करने पर विचार कर सकते हैं। सिर्फ स्पष्टता के लिए। कई "बुरी आदतों" पर हारून बर्ट्रेंड का उत्कृष्ट लेख
मैक्स वर्नोन

डेटाबेस व्यवस्थापकों में भी आपका स्वागत है - यदि आप पहले से ही नहीं आए हैं तो कृपया यात्रा करें !
मैक्स वर्नोन

1
सभी को धन्यवाद, उपयोगी टिप्पणियाँ और उपयोगी सुझाव संपादित करें, मैं यहाँ कुल नौसिखिया हूँ, किसी तरह मैं 1 अंक जमा करने में कामयाब रहा जो कि fab :-) है।
colinp_1

अब आपके पास 11.
मैक्स वर्नोन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.