Netezza Convert UTC/GMT to Central with Daylight Savings Time - netezza

I am working in a Netezza database that stores time as GMT (or so I am told by our data engineers). I need to be able to convert this to Central Standard Time (CST) but accounting for daylight savings time. I found that I could use something like:
SELECT CURRENT_TIMESTAMP, CURRENT_TIMESTAMP AT TIME ZONE 'CST' AT TIME ZONE 'GMT'
However, when I run this SELECT (keep in mind, today is March 30, 2021 - CST should only be 5 hours different from GTM), I get a 6 hour difference.... I looked up a reference to see what time zones are available in Netezza and I see a "CDT" which is 5 hours, and that works for the 5 hour difference, but this means in my query I would need to either change this each time DST switches over or do some sort of elaborate case statement to know which one to use depending on the date/time of year.
Is there an easy automated way to convert a GTM time to Central Standard Time accounting for daylight savings time? Thanks so much!!!

The question can be interpreted one of two ways. In both cases, the solution is to determine the timezone to convert to, based on whether the timestamp is between 2 AM 2nd Sunday of March and 2 AM on 1st Sunday of Nov (for US Central timezone)
The timestamps in your table, need to be converted to CST or CDT based on the current time (when the query is being run)
this means if the same query was run in Feb, the results would be different than if its run now
also it would be different based on what the timezone of the netezza system is set to
Eg
select
t as original,
-- extract year from current date and 2nd Sunday of March
-- use last_day to make sure we account for March 1 being a Sunday
(next_day(next_day(
last_day((date_part('years', current_date) || '-02-01'):: date),
'sun'),
'sun')|| ' 02:00:00'):: timestamp as dstart,
-- extract year from current date and 1st Sunday of Nov
-- use last_day to make sure we account for Nov 1 being a Sunday
(next_day(last_day(
(date_part('years', current_date) || '-10-01')::date),
'sun')|| ' 02:00:00'):: timestamp as dend,
case when current_timestamp between dstart
and dend then 'CDT' else 'CST' end as tz,
t at time zone tz as converted
from
tdata;
will produce
ORIGINAL | DSTART | DEND | TZ | CONVERTED
---------------------+---------------------+---------------------+-----+------------------------
2021-01-01 17:00:00 | 2021-03-14 02:00:00 | 2021-11-07 02:00:00 | CDT | 2021-01-01 12:00:00-05
2021-04-01 17:00:00 | 2021-03-14 02:00:00 | 2021-11-07 02:00:00 | CDT | 2021-04-01 12:00:00-05
2020-04-01 17:00:00 | 2021-03-14 02:00:00 | 2021-11-07 02:00:00 | CDT | 2020-04-01 12:00:00-05
2020-12-01 17:00:00 | 2021-03-14 02:00:00 | 2021-11-07 02:00:00 | CDT | 2020-12-01 12:00:00-05
(4 rows)
OR
The timestamps in your table need to be converted to CST or CDT depending on when the daylight savings started/ended in the respective year as defined in the time stamp.
this is more deterministic
select
t as original,
-- extract year from this timestamp and 2nd Sunday of March
-- use last_day to make sure we account for March 1 being a Sunday
(next_day(next_day(
last_day((date_part('years', t) || '-02-01'):: date), 'sun'),
'sun')|| ' 02:00:00'):: timestamp as dstart,
-- extract year from this timestamp and 1st Sunday of Nov
-- use last_day to make sure we account for Nov 1 being a Sunday
(next_day(last_day((date_part('years', t) || '-10-01')::date),
'sun')|| ' 02:00:00'):: timestamp as dend,
case when current_timestamp between dstart
and dend then 'CDT' else 'CST' end as tz,
t at time zone tz as converted
from
tdata;
This will produce (tdata is a sample table w/ 4 timestamps)
ORIGINAL | DSTART | DEND | TZ | CONVERTED
---------------------+---------------------+---------------------+-----+------------------------
2021-01-01 17:00:00 | 2021-03-14 02:00:00 | 2021-11-07 02:00:00 | CST | 2021-01-01 11:00:00-06
2021-04-01 17:00:00 | 2021-03-14 02:00:00 | 2021-11-07 02:00:00 | CDT | 2021-04-01 12:00:00-05
2020-04-01 17:00:00 | 2020-03-08 02:00:00 | 2020-11-01 02:00:00 | CDT | 2020-04-01 12:00:00-05
2020-12-01 17:00:00 | 2020-03-08 02:00:00 | 2020-11-01 02:00:00 | CST | 2020-12-01 11:00:00-06
(4 rows)

system.admin(admin)=> select '2021-04-07 11:00:00' as gmt, timezone('2021-04-07 11:00:00' , 'GMT', 'America/New_York') as eastern, timezone('2021-04-07 11:00:00', 'GMT', 'America/Chicago') as central, timezone('2021-04-07 11:00:00', 'GMT', 'America/Los_Angeles') as pacific;
gmt | eastern | central | pacific
---------------------+---------------------+---------------------+---------------------
2021-04-07 11:00:00 | 2021-04-07 07:00:00 | 2021-04-07 06:00:00 | 2021-04-07 04:00:00
(1 row)
system.admin(admin)=> select '2021-03-07 11:00:00' as gmt, timezone('2021-03-07 11:00:00' , 'GMT', 'America/New_York') as eastern, timezone('2021-03-07 11:00:00', 'GMT', 'America/Chicago') as central, timezone('2021-03-07 11:00:00', 'GMT', 'America/Los_Angeles') as pacific;
gmt | eastern | central | pacific
---------------------+---------------------+---------------------+---------------------
2021-03-07 11:00:00 | 2021-03-07 06:00:00 | 2021-03-07 05:00:00 | 2021-03-07 03:00:00
(1 row)
Instead of CDT and CST if we use 'America/Chicago' as shown above it takes care of daylight savings.

Related

Force UTC time in Snowflake JOIN condition

I have a setup where we have
A landing table with a column of type TIMESTAMP_LTZ (consumption_date). Includes the timezone of +02:00
A view (landing_view) that reads from the landing table
A view (raw_data) that reads from a table that has a field of type TIMESTAMP_NTZ (SOURCE_TIMESTAMP), but the value itself is in UTC time.
I have to join the data from landing_view to the data from raw_data using the consumption_date and SOURCE_TIMESTAMP.
SELECT l.ID, l.consumption_date, l.RUN_TIME, r.DISPLAY_NAME, r.source_timestamp, r.value_as_double
FROM "raw_data" r
JOIN "landing_view" l
ON r.SOURCE_TIMESTAMP >= DATEADD(second,120, convert_timezone('UTC',l.consumption_date))
and r.SOURCE_TIMESTAMP < DATEADD(second,1000, convert_timezone('UTC',l.consumption_date))
My problem is that the convert_timezone command does not seem to affect the join clause at all, insted the join is made using the local time included in the LTZ type (+02:00).
If I use the convert_timezone is a select, if works just fine, but for the JOIN it does not.
Is there a way I can tell snowflake to use UTC in the join?
This will depend on what your TIMEZONE setting is. See example below.
If TIMEZONE is UTC:
alter session set TIMEZONE = 'UTC';
select
-- 2AM UTC
'2021-01-02 02:00:00'::timestamp_ntz as SOURCE_TIMESTAMP,
-- 1AM UTC / 12PM Australia/Melbourne time / 1 hour before SOURCE_TIMESTAMP
'2021-01-02 12:00:00 +1100'::timestamp_ltz as CONSUMPTION_DATE,
-- so add one hour to CONSUMPTION_DATE should equal to SOURCE_TIMESTAMP
SOURCE_TIMESTAMP = DATEADD(hour, 1, convert_timezone('UTC', CONSUMPTION_DATE)) as is_equal
;
+-------------------------------+-------------------------------+----------+
| SOURCE_TIMESTAMP | CONSUMPTION_DATE | IS_EQUAL |
|-------------------------------+-------------------------------+----------|
| 2021-01-02 02:00:00.000000000 | 2021-01-02 01:00:00.000 +0000 | True |
+-------------------------------+-------------------------------+----------+
However, if you change your TIMEZONE setting to another timezone, the result will be different:
alter session set TIMEZONE = 'Australia/Melbourne';
select
-- 2AM UTC
'2021-01-02 02:00:00'::timestamp_ntz as SOURCE_TIMESTAMP,
-- 1AM UTC / 12PM Australia/Melbourne time / 1 hour before SOURCE_TIMESTAMP
'2021-01-02 12:00:00 +1100'::timestamp_ltz as CONSUMPTION_DATE,
-- so add one hour to CONSUMPTION_DATE should equal to SOURCE_TIMESTAMP
SOURCE_TIMESTAMP = DATEADD(hour, 1, convert_timezone('UTC', CONSUMPTION_DATE)) as is_equal
;
+-------------------------------+-------------------------------+----------+
| SOURCE_TIMESTAMP | CONSUMPTION_DATE | IS_EQUAL |
|-------------------------------+-------------------------------+----------|
| 2021-01-02 02:00:00.000000000 | 2021-01-02 12:00:00.000 +1100 | False |
+-------------------------------+-------------------------------+----------+
Since you SOURCE_TIMESTAMP stores UTC value, you should change your TIMEZONE setting to match it.
By the way, having CONVERT_TIMEZONE in the DATEADD is redundant, as it only adds extra operation, but not having any effects. See below example:
select
-- 1AM UTC / 9AM Australia/Perth time / 1 hour before SOURCE_TIMESTAMP
'2021-01-02 09:00:00 +0800'::timestamp_ltz as CONSUMPTION_DATE,
DATEADD(hour, 1, CONSUMPTION_DATE) as no_convert_tz,
DATEADD(hour, 1, convert_timezone('UTC', CONSUMPTION_DATE)) as convert_tz,
no_convert_tz = convert_tz
;
+-------------------------------+-------------------------------+-------------------------------+----------------------------+
| CONSUMPTION_DATE | NO_CONVERT_TZ | CONVERT_TZ | NO_CONVERT_TZ = CONVERT_TZ |
|-------------------------------+-------------------------------+-------------------------------+----------------------------|
| 2021-01-02 12:00:00.000 +1100 | 2021-01-02 13:00:00.000 +1100 | 2021-01-02 02:00:00.000 +0000 | True |
+-------------------------------+-------------------------------+-------------------------------+----------------------------+
You can see that the last column returns True.

Get results from past year if 1st of january

I have a scheduled job that runs daily ( in the morning or evening, or the next day in the morning) that sends some reports to an FTP.
I want to include in the report only records from the current year.
And if it is 1st of january of the next year, in the morning, I want to show all the records from the past year.
If it is 1st of january in the evening, and I have records from the new year, I want to include only those records from the new year
Example:
transfer date | job run date | Records with year
-----------------|------------------|------------------
30.12.2018 08:00 | 30.12.2018 19:00 | 2018
30.12.2018 08:00 | 31.12.2018 02:00 | 2018
31:12.2018 08:00 | 31.12.2018 19:00 | 2018
31.12.2018 08:00 | 01.01.2019 01:00 | 2018
01.01.2019 08:00 | 01.01.2019 19:00 | 2019
01.01.2019 08:00 | 02.01.2019 01:00 | 2019
In the last column is the record's year that i want
I would like a simple solution, so that the original query is not transformed into a function or stored procedure etc.
What have I tried:
SELECT
DateReceived ,
UniqueNumber ,
TransferDate ,
CompanyCode
FROM
ExportData
where
YEAR(convert(datetime,TransferDate)) =
case
when year(convert(datetime,Transferdate))=year(getdate())
and month(getdate()) =1
and day(getdate()) =1
then year(getdate())
when Year(convert(datetime,transferdate))=year(getdate())-1
then year(getdate())-1
end
order by
TransferDate DESC
Can you please check this simple adjustment in WHERE condition works for you or not. This will only return records for the Maximum YEAR available in the table.
SELECT
DateReceived ,
UniqueNumber ,
TransferDate ,
CompanyCode
FROM
ExportData
WHERE YEAR(CONVERT(DATETIME, TransferDate)) =
(
SELECT YEAR(MAX(CONVERT(DATETIME, TransferDate)))
FROM TransferDate
)
ORDER BY TransferDate DESC;

SQL Calculate a Total Time in a specific State

I have these datas in a Table
_____DateTime____|Variable__|Value
2017/03/29 23:00:00 | Variable1 | 1
2017/03/31 01:00:00 | Variable1 | 0
2017/03/31 02:00:00 | Variable1 | 1
2017/03/31 03:00:00 | Variable1 | 0
2017/03/31 04:00:00 | Variable2 | 1
2017/03/31 23:00:00 | Variable1 | 1
2017/04/01 01:00:00 | Variable1 | 0
And I would like to calculate the total duration where each variable was in state 1 between two date
For example between for Var1 2017/03/31 00:00:00 and 2017/04/01 00:00:00
The result is :
1 hour between 2017/03/31 00:00:00 and 2017/03/31 01:00:00
1 hour between 2017/03/31 02:00:00 and 2017/03/31 03:00:00
1 hour between 2017/03/31 23:00:00 and 2017/04/01 00:00:00
So the result I want for Var1 should be 3 hours
For example between for Var2 2017/03/31 00:00:00 and 2017/04/01 00:00:00
The result is :
1 hour between 2017/03/31 04:00:00 and 2017/04/01 00:00:00 (no value before but because it change to 1 I suppose that it was 0 before)
So the result I want for Var2 should be 20 hours
Variable|__Time in Value (second)
Variable1 | 180
Variable2 | 1200
If someone can help me.
Thanks in advance
For SQL Server 2012+ (because of lead() and concat())
Using a stacked cte to generate an hours table to inner join a subquery that uses the lead() window function to get the next date for status change partitioned by Variable.
To adapt for prior versions, use an outer apply() to get the next dt for each variable instead of lead(); and regular string concatenation with proper conversions instead of concat().
declare #fromdate datetime = '20170331 00:00:00';
declare #thrudate datetime = '20170401 00:00:00';
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, hours as (
select top ((datediff(hour, #fromdate, #thrudate)+1))
[DateHour]=dateadd(hour,(row_number() over (order by (select 1)) -1),#fromdate)
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by 1
)
select variable, value
, hours = count(h.datehour)
, start_dt = convert(varchar(20),min(h.datehour),120)
, end_dt = convert(varchar(20),end_dt,120)
, txt = concat(
count(h.datehour),' '
, case when count(h.datehour) < 2 then 'hour' else 'hours' end
, ' between '
, convert(varchar(20),min(h.datehour),120)
, ' and '
, convert(varchar(20),end_dt,120)
)
from hours h
inner join (
select
variable
, value
, start_dt = dt
, end_dt = case when coalesce(lead(dt) over (partition by variable order by dt),#thrudate) > #thrudate
then #thrudate
else coalesce(lead(dt) over (partition by variable order by dt),#thrudate)
end
from t
) s
on h.datehour >= s.start_dt
and h.datehour < s.end_dt
where h.datehour >= #fromdate
and h.datehour < #thrudate
and s.value = 1
group by variable, value, start_dt, end_dt
rextester demo: http://rextester.com/ZBWP22523
returns:
+-----------+-------+-------+---------------------+---------------------+--------------------------------------------------------------+
| variable | value | hours | start_dt | end_dt | txt |
+-----------+-------+-------+---------------------+---------------------+--------------------------------------------------------------+
| Variable1 | 1 | 1 | 2017-03-31 00:00:00 | 2017-03-31 01:00:00 | 1 hour between 2017-03-31 00:00:00 and 2017-03-31 01:00:00 |
| Variable1 | 1 | 1 | 2017-03-31 02:00:00 | 2017-03-31 03:00:00 | 1 hour between 2017-03-31 02:00:00 and 2017-03-31 03:00:00 |
| Variable1 | 1 | 1 | 2017-03-31 23:00:00 | 2017-04-01 01:00:00 | 1 hour between 2017-03-31 23:00:00 and 2017-04-01 01:00:00 |
| Variable2 | 1 | 20 | 2017-03-31 04:00:00 | 2017-04-01 00:00:00 | 20 hours between 2017-03-31 04:00:00 and 2017-04-01 00:00:00 |
+-----------+-------+-------+---------------------+---------------------+--------------------------------------------------------------+
If you need to do this often, you might consider creating an actual table for hours. Otherwise, using the stacked cte is as fast as most other options, and is much faster than a recursive cte as the number of values generated increases.
Number and Calendar table reference:
Generate a set or sequence without loops - 1 - Aaron Bertrand
Generate a set or sequence without loops - 2 - Aaron Bertrand
Generate a set or sequence without loops - 3 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in SQL Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in SQL Server - Aaron Bertrand
TSQL Function to Determine Holidays in SQL Server - Tim Cullen
F_TABLE_DATE - Michael Valentine Jones

finding date in sql

i need a query to find the date based on year, month, day of the week and weekday number. Say for example, if the question is to find the date of 2nd Sunday of January 2010, the answer should be '2010-01-10'.
Inputs are
Yr | Mon | Dy | Dyno
-----------------------
2010 | Jan | Sun | 2
2005 | Jan | Mon | 3
1995 | Feb | Sun | 1
2000 | Feb | Wed | 4
1982 | Mar | Tue | 2
2010 | Mar | Tue | 8
Dyno states dayno
The easiest answer to many date-related questions in SQL is to create a calendar table. In your case, if you create a table with the columns you've already shown, and an extra one with the DATETIME value that you want (call it BaseDate), you can get the value you need with a simple query:
select BaseDate
from dbo.Calendar
where Yr = 2010 and Mon = 'Jan' and Dy = 'Sunday' and Dyno = 2
Of course, your calendar table can have 10, 20 or more columns, depending on what values you find useful for your queries.

TSQL Finding Overlapping Hours

When two tables are given
Employee Table
EmpID Name
1 Jon
2 Smith
3 Dana
4 Nancy
Lab Table
EmpID StartTime EndTime Date LabID
1 10:00 AM 12:15 PM 01/JAN/2000 Lab I
1 11:00 AM 14:15 PM 01/JAN/2000 Lab II
1 16:30 PM 18:30 PM 01/JAN/2000 Lab I
2 10:00 AM 12:10 PM 01/JAN/2000 Lab I
From the given details ,I have to find out the overlapping hours,and non overlapping hours of each employee on each date. (StartTime and EndTime are of type varchar).
The expected output is
-------------------------------------------------------------------------------
EmpID| Name| Overlapping | Non-Overlapping | Date
Period Period
-------------------------------------------------------------------------------
1 Jon | 10:00 AM to 12:15 PM |16:30 PM to 18:30 PM | 01/JAN/2000
| AND | |
| 11:00 AM to 14:15 PM | |
| AND ...(If any) | |
--------------------------------------------------------------------------------
2 Smith| NULL | 10:00 AM to 12:10 PM |01/JAN/2000
--------------------------------------------------------------------------------
Please help me to bring such output using TSQL(SQL Server 2005/2008).
First, you should probably consider using a DateTime field to store the StartTime and EndTime, and thus make calculations easier, and remove the need for the Date field.
SELECT t1.EmpID,
t1.StartTime,
t1.EndTime,
t2.StartTime
t2.EndTime,
FROM lab t1
LEFT OUTER JOIN lab t2
ON t2.StartTime BETWEEN t1.StartTime AND t1.EndTime
AND t2.EmpID = t1.EmpID
ORDER BY t1.EmpID,
t1.StartTime,
t2.StartTime
That won't get you the EXACT format you have listed, but it's close. You should end up with:
| EmpID| Name| Normal Period | Overlapping Period |
------------------------------------------------------------
| 1 | Jon | 10:00 AM | 12:15 PM | 11:00 AM | 02:15 PM |
------------------------------------------------------------
| 2 | Smith | 10:00 AM | 12:10 PM | NULL | NULL |
------------------------------------------------------------
Each overlapped period within a normal period would show up in a new row, but any period with no overlaps would have only one row. You could easily concatenate the fields if you wanted specifically the "xx:xx xx to xx:xx xx" format. Hope this helps you some.

Resources