J'ai récemment mis à jour mon système pour enregistrer la date et l'heure au format UTC, comme auparavant, ils étaient stockés au format heure locale.
Je dois maintenant convertir toutes les date/heure stockées localement en UTC. Je me demandais s'il existait une fonction intégrée similaire à la méthode ConvertTime
de .NET?
J'essaie d'éviter d'avoir à écrire une application utilitaire pour le faire pour moi.
Aucune suggestion?
Si elles sont toutes locales, alors voici le décalage:
SELECT GETDATE() AS CurrentTime, GETUTCDATE() AS UTCTime
et vous devriez pouvoir mettre à jour toutes les données en utilisant:
UPDATE SomeTable
SET DateTimeStamp = DATEADD(hh, DATEDIFF(hh, GETDATE(), GETUTCDATE()), DateTimeStamp)
Cela fonctionnerait-il ou suis-je en train de manquer un autre angle de ce problème?
Je ne crois pas que le code ci-dessus fonctionnera. La raison en est que cela dépend de la différence entre la date actuelle en heure locale et l'heure UTC. Par exemple, ici en Californie, nous sommes maintenant en PDT (heure avancée du Pacifique); la différence entre cette heure et UTC est de 7 heures. Le code fourni, s'il est exécuté maintenant, ajoutera 7 heures à chaque date que vous souhaitez convertir. Mais si une date historique stockée, ou une date future, est convertie et que cette date n’est pas à l’heure avancée de l’heure, l’ajoute 7, mais le décalage correct est 8. Résultat inférieur: vous ne pouvez pas convertir date/heure correctement entre les fuseaux horaires (y compris UTC, qui ne respecte pas l'heure d'été) en ne regardant que la date du jour. Vous devez considérer la date même de la conversion pour déterminer si l'heure d'été était en vigueur à cette date. De plus, les dates auxquelles le jour et l'heure standard changent d'elles-mêmes ont changé (George Bush a changé les dates pendant son administration pour les États-Unis!). En d'autres termes, toute solution faisant même référence à getdate () ou à getutcdate () ne fonctionne pas. Il doit analyser la date réelle à convertir.
Avec SQL Server 2016, la prise en charge intégrée des fuseaux horaires avec l'instruction AT TIME ZONE
est désormais intégrée. Vous pouvez les chaîner pour effectuer des conversions:
SELECT YourOriginalDateTime AT TIME ZONE 'Pacific Standard Time' AT TIME ZONE 'UTC'
Ou, cela fonctionnerait aussi bien:
SELECT SWITCHOFFSET(YourOriginalDateTime AT TIME ZONE 'Pacific Standard Time', '+00:00')
L’une ou l’autre de ces interprétations interprétera l’entrée en heure du Pacifique, déterminera correctement si l’heure d’été est effective ou non, puis convertira en UTC. Le résultat sera une datetimeoffset
avec un décalage de zéro.
Comme mentionné précédemment, il n'existe pas de méthode intégrée pour effectuer la conversion de date en fonction des règles de fuseau horaire dans SQL Server (au moins à partir de SQL Server 2012).
Vous avez essentiellement trois choix pour bien faire les choses:
Bien que SQL Server n'offre pas d'outils permettant d'effectuer une conversion de date en fonction des règles de fuseau horaire, c'est le framework .NET qui le permet, et tant que vous pouvez utiliser SQL CLR, vous pouvez en tirer parti.
Dans Visual Studio 2012, assurez-vous que les outils de données sont installés (sinon, le projet SQL Server n'apparaît pas comme une option) et créez un nouveau projet SQL Server.
Ajoutez ensuite une nouvelle fonction définie par l'utilisateur SQL C # CLR, appelez-la "ConvertToUtc". VS générera pour vous une plaque de chaudière qui devrait ressembler à ceci:
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString ConvertToUtc()
{
// Put your code here
return new SqlString (string.Empty);
}
}
Nous voulons faire plusieurs changements ici. D'une part, nous voulons renvoyer un SqlDateTime
plutôt qu'un SqlString
. Deuxièmement, nous voulons faire quelque chose d’utile. :)
Votre code révisé devrait ressembler à ceci:
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlDateTime ConvertToUtc(SqlDateTime sqlLocalDate)
{
// convert to UTC and use explicit conversion
// to return a SqlDateTime
return TimeZone.CurrentTimeZone.ToUniversalTime(sqlLocalDate.Value);
}
}
À ce stade, nous sommes prêts à l'essayer. Le moyen le plus simple consiste à utiliser la fonctionnalité intégrée de publication dans Visual Studio. Cliquez avec le bouton droit sur le projet de base de données et sélectionnez "Publier". Configurez votre nom et votre connexion à la base de données, puis cliquez sur "Publier" pour insérer le code dans la base de données ou cliquez sur "Générer le script" si vous souhaitez stocker le script pour la postérité (ou pour insérer les éléments dans la production).
Une fois que vous avez le fichier UDF dans la base de données, vous pouvez le voir en action:
declare @dt as datetime
set @dt = '12/1/2013 1:00 pm'
select dbo.ConvertToUtc(@dt)
Voici une procédure testée qui a mis à niveau ma base de données de local à utc time. La seule entrée requise pour mettre à niveau une base de données consiste à entrer le nombre de minutes que l'heure locale est décalée de l'heure utc dans @Offset et si le fuseau horaire est sujet à des ajustements de l'heure d'été en définissant @ApplyDaylightSavings.
Par exemple, l'heure centrale des États-Unis entrerait @ Offset = -360 et @ ApplyDaylightSavings = 1 pendant 6 heures, et si oui appliquerait le réglage de l'heure d'été.
Fonction de base de données prise en charge
CREATE FUNCTION [dbo].[GetUtcDateTime](@LocalDateTime DATETIME, @Offset smallint, @ApplyDaylightSavings bit)
RETURNS DATETIME AS BEGIN
--====================================================
--Calculate the Offset Datetime
--====================================================
DECLARE @UtcDateTime AS DATETIME
SET @UtcDateTime = DATEADD(MINUTE, @Offset * -1, @LocalDateTime)
IF @ApplyDaylightSavings = 0 RETURN @UtcDateTime;
--====================================================
--Calculate the DST Offset for the UDT Datetime
--====================================================
DECLARE @Year as SMALLINT
DECLARE @DSTStartDate AS DATETIME
DECLARE @DSTEndDate AS DATETIME
--Get Year
SET @Year = YEAR(@LocalDateTime)
--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)
--Finally add the DST Offset if needed
RETURN CASE WHEN @LocalDateTime BETWEEN @DSTStartDate AND @DSTEndDate THEN
DATEADD(MINUTE, -60, @UtcDateTime)
ELSE
@UtcDateTime
END
END
GO
Script de mise à niveau
begin try
begin transaction;
declare @sql nvarchar(max), @Offset smallint, @ApplyDaylightSavings bit;
set @Offset = -360; --US Central Time, -300 for US Eastern Time, -480 for US West Coast
set @ApplyDaylightSavings = 1; --1 for most US time zones except Arizona which doesn't observer daylight savings, 0 for most time zones outside the US
declare rs cursor for
select 'update [' + a.TABLE_SCHEMA + '].[' + a.TABLE_NAME + '] set [' + a.COLUMN_NAME + '] = dbo.GetUtcDateTime([' + a.COLUMN_NAME + '], ' + cast(@Offset as nvarchar) + ', ' + cast(@ApplyDaylightSavings as nvarchar) + ') ;'
from INFORMATION_SCHEMA.COLUMNS a
inner join INFORMATION_SCHEMA.TABLES b on a.TABLE_SCHEMA = b.TABLE_SCHEMA and a.TABLE_NAME = b.TABLE_NAME
where a.DATA_TYPE = 'datetime' and b.TABLE_TYPE = 'BASE TABLE' ;
open rs;
fetch next from rs into @sql;
while @@FETCH_STATUS = 0 begin
exec sp_executesql @sql;
print @sql;
fetch next from rs into @sql;
end
close rs;
deallocate rs;
commit transaction;
end try
begin catch
close rs;
deallocate rs;
declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
rollback transaction;
raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
end catch
Si vous devez convertir des dates autres que celle d'aujourd'hui en différents fuseaux horaires, vous devez gérer l'heure avancée. Je recherchais une solution pouvant être réalisée sans se soucier de la version de la base de données, sans utiliser de fonctions stockées et pouvant facilement être portée à Oracle.
Je pense que Warren est sur la bonne voie pour obtenir les dates correctes pour l'heure avancée, mais pour le rendre plus utile pour plusieurs fuseaux horaires et des règles différentes pour les pays et même pour la règle qui a changé aux États-Unis entre 2006 et 2007, voici une variante de la solution ci-dessus. Notez que cela ne concerne pas seulement les fuseaux horaires, mais aussi l'Europe centrale. L'Europe centrale suit le dernier dimanche d'avril et le dernier dimanche d'octobre. Vous remarquerez également que les États-Unis en 2006 suivent l’ancien premier dimanche d’avril, dernier dimanche d’octobre.
Ce code SQL peut sembler un peu moche, mais copiez-le et collez-le dans SQL Server et essayez-le. Notez qu'il y a 3 sections pour les années, les fuseaux horaires et les règles. Si vous voulez une autre année, ajoutez-la simplement au syndicat de l'année. Idem pour un autre fuseau horaire ou une autre règle.
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
Pour les règles, utilisez 1, 2, 3 ou L pour le premier, deuxième, troisième ou dernier dimanche. La partie date indique le mois et, selon la règle, le premier ou le dernier jour du mois pour le type de règle L.
Je mets la requête ci-dessus dans une vue. Maintenant, chaque fois que je veux une date avec le décalage de fuseau horaire ou converti en heure UTC, je me joins à cette vue et sélectionnez Obtenir la date au format de date. Au lieu de date-heure, j'ai converti ceux-ci en 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'
Voici ma version rapide et sale. Je sais que toutes mes dates utilisaient le fuseau horaire de l’Est des États-Unis. Vous pouvez modifier le décalage ou le rendre plus intelligent si nécessaire. Je faisais une migration ponctuelle donc c'était assez bon.
CREATE FUNCTION [dbo].[ConvertToUtc]
(
@date datetime
)
RETURNS DATETIME
AS
BEGIN
-- Declare the return variable here
DECLARE @utcDate datetime;
DECLARE @offset int;
SET @offset = (SELECT CASE WHEN
@date BETWEEN '1987-04-05 02:00 AM' AND '1987-10-25 02:00 AM'
OR @date BETWEEN '1988-04-03 02:00 AM' AND '1988-10-30 02:00 AM'
OR @date BETWEEN '1989-04-02 02:00 AM' AND '1989-10-29 02:00 AM'
OR @date BETWEEN '1990-04-01 02:00 AM' AND '1990-10-28 02:00 AM'
OR @date BETWEEN '1991-04-07 02:00 AM' AND '1991-10-27 02:00 AM'
OR @date BETWEEN '1992-04-05 02:00 AM' AND '1992-10-25 02:00 AM'
OR @date BETWEEN '1993-04-04 02:00 AM' AND '1993-10-31 02:00 AM'
OR @date BETWEEN '1994-04-03 02:00 AM' AND '1994-10-30 02:00 AM'
OR @date BETWEEN '1995-04-02 02:00 AM' AND '1995-10-29 02:00 AM'
OR @date BETWEEN '1996-04-07 02:00 AM' AND '1996-10-27 02:00 AM'
OR @date BETWEEN '1997-04-06 02:00 AM' AND '1997-10-26 02:00 AM'
OR @date BETWEEN '1998-04-05 02:00 AM' AND '1998-10-25 02:00 AM'
OR @date BETWEEN '1999-04-04 02:00 AM' AND '1999-10-31 02:00 AM'
OR @date BETWEEN '2000-04-02 02:00 AM' AND '2000-10-29 02:00 AM'
OR @date BETWEEN '2001-04-01 02:00 AM' AND '2001-10-28 02:00 AM'
OR @date BETWEEN '2002-04-07 02:00 AM' AND '2002-10-27 02:00 AM'
OR @date BETWEEN '2003-04-06 02:00 AM' AND '2003-10-26 02:00 AM'
OR @date BETWEEN '2004-04-04 02:00 AM' AND '2004-10-31 02:00 AM'
OR @date BETWEEN '2005-04-03 02:00 AM' AND '2005-10-30 02:00 AM'
OR @date BETWEEN '2006-04-02 02:00 AM' AND '2006-10-29 02:00 AM'
OR @date BETWEEN '2007-03-11 02:00 AM' AND '2007-11-04 02:00 AM'
OR @date BETWEEN '2008-03-09 02:00 AM' AND '2008-11-02 02:00 AM'
OR @date BETWEEN '2009-03-08 02:00 AM' AND '2009-11-01 02:00 AM'
OR @date BETWEEN '2010-03-14 02:00 AM' AND '2010-11-07 02:00 AM'
OR @date BETWEEN '2011-03-13 02:00 AM' AND '2011-11-06 02:00 AM'
OR @date BETWEEN '2012-03-11 02:00 AM' AND '2012-11-04 02:00 AM'
OR @date BETWEEN '2013-03-10 02:00 AM' AND '2013-11-03 02:00 AM'
OR @date BETWEEN '2014-03-09 02:00 AM' AND '2014-11-02 02:00 AM'
OR @date BETWEEN '2015-03-08 02:00 AM' AND '2015-11-01 02:00 AM'
OR @date BETWEEN '2016-03-13 02:00 AM' AND '2016-11-06 02:00 AM'
OR @date BETWEEN '2017-03-12 02:00 AM' AND '2017-11-05 02:00 AM'
OR @date BETWEEN '2018-03-11 02:00 AM' AND '2018-11-04 02:00 AM'
OR @date BETWEEN '2019-03-10 02:00 AM' AND '2019-11-03 02:00 AM'
OR @date BETWEEN '2020-03-08 02:00 AM' AND '2020-11-01 02:00 AM'
OR @date BETWEEN '2021-03-14 02:00 AM' AND '2021-11-07 02:00 AM'
THEN 4
ELSE 5 END);
SELECT @utcDate = DATEADD(hh, @offset, @date)
RETURN @utcDate;
END
À moins que quelque chose ne se passe au-dessus (possible), toutes les méthodes ci-dessus sont faussées en ce qu'elles ne prennent pas en compte le chevauchement lors du passage de l'heure d'été (par exemple, l'heure avancée du centre) à l'heure standard (par exemple, l'heure d'été). Un exemple (très verbeux):
[1] EDT 2016-11-06 00:59 - UTC 2016-11-06 04:59
[2] EDT 2016-11-06 01:00 - UTC 2016-11-06 05:00
[3] EDT 2016-11-06 01:30 - UTC 2016-11-06 05:30
[4] EDT 2016-11-06 01:59 - UTC 2016-11-06 05:59
[5] EST 2016-11-06 01:00 - UTC 2016-11-06 06:00
[6] EST 2016-11-06 01:30 - UTC 2016-11-06 06:30
[7] EST 2016-11-06 01:59 - UTC 2016-11-06 06:59
[8] EST 2016-11-06 02:00 - UTC 2016-11-06 07:00
De simples décalages horaires basés sur la date et l'heure ne vous permettront pas de gagner du temps. Si vous ne savez pas si l'heure locale a été enregistrée en HAE ou en HNE entre 01h00 et 01h59, vous n'en saurez plus rien! Prenons par exemple 01:30: si vous trouvez des temps ultérieurs compris entre 01:31 et 01:59 AVANT, vous ne saurez pas si le 01:30 que vous regardez est [3 ou [6. Dans ce cas, vous pouvez obtenir l'heure UTC correcte avec un peu de code en regardant les entrées précédentes (ce qui n'est pas amusant en SQL), et c'est le MEILLEUR cas ...
Supposons que les heures locales suivantes soient enregistrées et que vous n’ayez pas consacré un peu de temps à indiquer l’EST ou l’EST:
UTC time UTC time UTC time
if [2] and [3] if [2] and [3] if [2] before
local time before switch after switch and [3] after
[1] 2016-11-06 00:43 04:43 04:43 04:43
[2] 2016-11-06 01:15 05:15 06:15 05:15
[3] 2016-11-06 01:45 05:45 06:45 06:45
[4] 2016-11-06 03:25 07:25 07:25 07:25
Les heures [2] et [3] peuvent se situer à 5 heures du matin, 6 heures du matin ou une heure à 5 heures du matin et l’autre à 6 heures du matin. . . En d'autres termes: vous êtes arrosé et vous devez rejeter toutes les lectures entre 01:00:00 et 01:59:59. Dans ce cas, il n’ya absolument aucun moyen de résoudre le temps UTC réel!
Ce qui suit devrait fonctionner, car il calcule la différence entre DATE et UTCDATE pour le serveur que vous exécutez et utilise cette compensation pour calculer l’équivalent UTC de toute date que vous lui transmettez. Dans mon exemple, j'essaie de convertir l'équivalent UTC pour "1 nov. 2012 06:00" à Adélaïde, en Australie, où l'offset UTC est égal à -630 minutes. Lorsqu'il est ajouté à n'importe quelle date, il correspond à l'équivalent UTC de toute date locale.
sélectionnez DATEADD (MINUTE, DATEDIFF (MINUTE, GETDATE (), GETUTCDATE ()), '1-nov-2012 06:00')
Nous pouvons convertir ServerZone DateTime
en UTC et UTC en ServerZone DateTime
Exécutez simplement les scripts suivants pour comprendre la conversion puis modifiez-le selon vos besoins.
--Get Server's TimeZone
DECLARE @ServerTimeZone VARCHAR(50)
EXEC MASTER.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
'TimeZoneKeyName',@ServerTimeZone OUT
-- ServerZone to UTC DATETIME
DECLARE @CurrentServerZoneDateTime DATETIME = GETDATE()
DECLARE @UTCDateTime DATETIME = @CurrentServerZoneDateTime AT TIME ZONE @ServerTimeZone AT TIME ZONE 'UTC'
--(OR)
--DECLARE @UTCDateTime DATETIME = GETUTCDATE()
SELECT @CurrentServerZoneDateTime AS CURRENTZONEDATE,@UTCDateTime AS UTCDATE
-- UTC to ServerZone DATETIME
SET @CurrentServerZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE @ServerTimeZone
SELECT @UTCDateTime AS UTCDATE,@CurrentServerZoneDateTime AS CURRENTZONEDATE
Note: Ceci (AT TIME ZONE
) fonctionne uniquement avec SQL Server 2016+ et cet avantage est automatiquement en considérant Daylight lors de la conversion en fuseau horaire
En fonction de la distance qui vous sépare, vous pouvez créer un tableau des heures d'été, puis rejoindre le tableau et effectuer une conversion sensible à DST. Celui-ci convertit d'EST en GMT (c'est-à-dire utilise des décalages de 5 et 4).
select createdon, dateadd(hour, case when dstlow is null then 5 else 4 end, createdon) as gmt
from photos
left outer join (
SELECT {ts '2009-03-08 02:00:00'} as dstlow, {ts '2009-11-01 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2010-03-14 02:00:00'} as dstlow, {ts '2010-11-07 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2011-03-13 02:00:00'} as dstlow, {ts '2011-11-06 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2012-03-11 02:00:00'} as dstlow, {ts '2012-11-04 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2013-03-10 02:00:00'} as dstlow, {ts '2013-11-03 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2014-03-09 02:00:00'} as dstlow, {ts '2014-11-02 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2015-03-08 02:00:00'} as dstlow, {ts '2015-11-01 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2016-03-13 02:00:00'} as dstlow, {ts '2016-11-06 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2017-03-12 02:00:00'} as dstlow, {ts '2017-11-05 02:00:00'} as dsthigh
UNION ALL SELECT {ts '2018-03-11 02:00:00'} as dstlow, {ts '2018-11-04 02:00:00'} as dsthigh
) dst
on createdon >= dstlow and createdon < dsthigh