Se devi convertire date diverse da oggi in fusi orari diversi, devi occuparti dell'ora legale. Volevo una soluzione che potrebbe essere fatto senza preoccuparsi di versione del database, senza l'utilizzo di funzioni memorizzate e qualcosa che potrebbe essere facilmente portato su Oracle.
Penso che Warren sia sulla strada giusta per ottenere le date corrette per l'ora legale, ma per renderlo più utile per più fuso orario e regole diverse per i paesi e anche per la regola che è cambiata negli Stati Uniti tra il 2006 e il 2007, qui una variazione sulla soluzione di cui sopra. Si noti che questo non solo ci ha i fusi orari, ma anche l'Europa centrale. L'Europa centrale segue l'ultima domenica di aprile e l'ultima domenica di ottobre. Noterete anche che gli Stati Uniti nel 2006, segue la vecchia prima domenica di aprile, ultima domenica di regola ottobre.
Questo codice SQL può sembrare un po 'brutto, ma basta copiarlo e incollarlo in SQL Server e provarlo. Si noti che ci sono 3 sezioni per anni, fusi orari e regole. Se vuoi un altro anno, aggiungilo al sindacato dell'anno. Lo stesso vale per un altro fuso orario o regola.
select yr, zone, standard, daylight, rulename, strule, edrule, yrstart, yrend,
dateadd(day, (stdowref + stweekadd), stmonthref) dstlow,
dateadd(day, (eddowref + edweekadd), edmonthref) dsthigh
from (
select yrs.yr, z.zone, z.standard, z.daylight, z.rulename, r.strule, r.edrule,
yrs.yr + '-01-01 00:00:00' yrstart,
yrs.yr + '-12-31 23:59:59' yrend,
yrs.yr + r.stdtpart + ' ' + r.cngtime stmonthref,
yrs.yr + r.eddtpart + ' ' + r.cngtime edmonthref,
case when r.strule in ('1', '2', '3') then case when datepart(dw, yrs.yr + r.stdtpart) = '1' then 0 else 8 - datepart(dw, yrs.yr + r.stdtpart) end
else (datepart(dw, yrs.yr + r.stdtpart) - 1) * -1 end stdowref,
case when r.edrule in ('1', '2', '3') then case when datepart(dw, yrs.yr + r.eddtpart) = '1' then 0 else 8 - datepart(dw, yrs.yr + r.eddtpart) end
else (datepart(dw, yrs.yr + r.eddtpart) - 1) * -1 end eddowref,
datename(dw, yrs.yr + r.stdtpart) stdow,
datename(dw, yrs.yr + r.eddtpart) eddow,
case when r.strule in ('1', '2', '3') then (7 * CAST(r.strule AS Integer)) - 7 else 0 end stweekadd,
case when r.edrule in ('1', '2', '3') then (7 * CAST(r.edrule AS Integer)) - 7 else 0 end edweekadd
from (
select '2005' yr union select '2006' yr -- old us rules
UNION select '2007' yr UNION select '2008' yr UNION select '2009' yr UNION select '2010' yr UNION select '2011' yr
UNION select '2012' yr UNION select '2013' yr UNION select '2014' yr UNION select '2015' yr UNION select '2016' yr
UNION select '2017' yr UNION select '2018' yr UNION select '2018' yr UNION select '2020' yr UNION select '2021' yr
UNION select '2022' yr UNION select '2023' yr UNION select '2024' yr UNION select '2025' yr UNION select '2026' yr
) yrs
cross join (
SELECT 'ET' zone, '-05:00' standard, '-04:00' daylight, 'US' rulename
UNION SELECT 'CT' zone, '-06:00' standard, '-05:00' daylight, 'US' rulename
UNION SELECT 'MT' zone, '-07:00' standard, '-06:00' daylight, 'US' rulename
UNION SELECT 'PT' zone, '-08:00' standard, '-07:00' daylight, 'US' rulename
UNION SELECT 'CET' zone, '+01:00' standard, '+02:00' daylight, 'EU' rulename
) z
join (
SELECT 'US' rulename, '2' strule, '-03-01' stdtpart, '1' edrule, '-11-01' eddtpart, 2007 firstyr, 2099 lastyr, '02:00:00' cngtime
UNION SELECT 'US' rulename, '1' strule, '-04-01' stdtpart, 'L' edrule, '-10-31' eddtpart, 1900 firstyr, 2006 lastyr, '02:00:00' cngtime
UNION SELECT 'EU' rulename, 'L' strule, '-03-31' stdtpart, 'L' edrule, '-10-31' eddtpart, 1900 firstyr, 2099 lastyr, '01:00:00' cngtime
) r on r.rulename = z.rulename
and datepart(year, yrs.yr) between firstyr and lastyr
) dstdates
Per le regole, utilizzare 1, 2, 3 o L per la prima, la seconda, la terza o l'ultima domenica. La parte della data indica il mese e in base alla regola, il primo giorno del mese o l'ultimo giorno del mese per il tipo di regola L.
Ho inserito la query sopra riportata in una vista. Ora, ogni volta che voglio una data con l'offset del fuso orario o convertito in ora UTC, mi limito a unirmi a questa vista e selezionare la data nel formato data. Invece di datetime, li ho convertiti in datetimeoffset.
select createdon, dst.zone
, case when createdon >= dstlow and createdon < dsthigh then dst.daylight else dst.standard end pacificoffsettime
, TODATETIMEOFFSET(createdon, case when createdon >= dstlow and createdon < dsthigh then dst.daylight else dst.standard end) pacifictime
, SWITCHOFFSET(TODATETIMEOFFSET(createdon, case when createdon >= dstlow and createdon < dsthigh then dst.daylight else dst.standard end), '+00:00') utctime
from (select '2014-01-01 12:00:00' createdon union select '2014-06-01 12:00:00' createdon) photos
left join US_DAYLIGHT_DATES dst on createdon between yrstart and yrend and zone = 'PT'
Questi orari sono tutti "locali" o sono tutti orari diversi da più fusi orari? – SqlRyan
@rwmnau, sono tutti locali per me. – James
possibile duplicato di [TSQL: Come convertire l'ora locale in UTC? (SQL Server 2008)] (http://stackoverflow.com/questions/1205142/tsql-how-to-convert-local-time-to-utc-sql-server-2008) – BroSlow