Getting timezone based on the region - sql

I have my timezone in Pacific time.I want to convert it to the local time based on the region .Below is the example
time region
2017-05-23 14:00:00 Central
2017-05-23 14:00:00 Eastern
2017-05-23 14:00:00 Mountain
What i am looking for
time region time_local
2017-05-23 14:00:00 Central 2017-05-23 16:00:00
2017-05-23 14:00:00 Eastern 2017-05-23 17:00:00
2017-05-23 14:05:00 Mountain 2017-05-23 15:05:00

You can join to the timezone system view to adjust the timestamps:
select
time,
region,
time - tz1.utc_offset + tz2.utc_offset
from Example ex
JOIN pg_timezone_names tz1
on tz1.name = 'US/Pacific'
JOIN pg_timezone_names tz2
on tz2.name = 'US/' || ex.region

Assuming your "time" column type is a correct "timestamp with time zone" (also called or "timestamptz" for short):
create table times (time timestamp with time zone not null, region text not null);
set timezone='US/Pacific';
insert into times (time, region) values
('2017-05-23 14:00:00','US/Central'),
('2017-05-23 14:00:00','US/Eastern'),
('2017-05-23 14:00:00','US/Mountain');
select *, time at time zone region as time_local from times;
time | region | time_local
------------------------+-------------+---------------------
2017-05-23 14:00:00-07 | US/Central | 2017-05-23 16:00:00
2017-05-23 14:00:00-07 | US/Eastern | 2017-05-23 17:00:00
2017-05-23 14:00:00-07 | US/Mountain | 2017-05-23 15:00:00
If your time columns is an ordinary timestamp instead then you need to think about changing it before you'll get insane. The timestamp type does not mean timestamp at all - it means what some clock somewhere will show, which means different thing depending on where you are and which date it is and in which country you check and your database client settings and current environment and after which changes to the clock will politicians do in the future. Do not go this path.

You may use such a conversion as below :
SET TIMEZONE TO 'US/Central';
SELECT now()::timestamp;
current_time
2018-07-25T14:01:50.608042Z
SET TIMEZONE TO 'US/Eastern';
SELECT now()::timestamp;
current_time
2018-07-25T15:01:50.608042Z
SET TIMEZONE TO 'US/Mountain';
SELECT now()::timestamp;
current_time
2018-07-25T13:01:50.608042Z
SQL Fiddle Demo 1
OR alternatively Use :
SET TIMEZONE TO 'US/Central';
SELECT concat(current_date,' ',localtime) as current_time;
current_time
2018-07-25 14:10:57.962193
SET TIMEZONE TO 'US/Eastern';
SELECT concat(current_date,' ',localtime) as current_time;
current_time
2018-07-25 15:10:57.962193
SET TIMEZONE TO 'US/Mountain';
SELECT concat(current_date,' ',localtime) as current_time;
current_time
2018-07-25 13:10:57.962193
SQL Fiddle Demo 2

Related

Add 1 day to timezone aware timestamp with regards to daylight savings

I am trying to add 1 day to a timezone aware timestamp.
In this example I expected + interval '1' day to add 23 hours because DST starts on 2021-03-28 02:00:00 in Europe/Berlin, but it behaves the same as + interval '24' hour:
select timestamp '2021-03-28 00:00:00 Europe/Berlin' as before_dst,
timestamp '2021-03-28 00:00:00 Europe/Berlin' + interval '1' day as plus_1_day,
timestamp '2021-03-28 00:00:00 Europe/Berlin' + interval '24' hour as plus_24_hour
from dual;
BEFORE_DST
PLUS_1_DAY
PLUS_24_HOUR
2021-03-28 00:00:00.000000000 +01:00
2021-03-29 01:00:00.000000000 +02:00
2021-03-29 01:00:00.000000000 +02:00
Is there a way to add a day to a timestamp so that the beginnings or ends of daylight saving times are respected? For the example above that means a way to have oracle automatically recognize that the day 2021-03-28 only has 23 hours in Europe/Berlin.
I attempted to solve this by converting the timestamp to a local timestamp using at local before adding a day, but that does not work because at local converts the timestamp to the local time zone and not to something like a LocalDateTime in java, resulting in the exact same outcome: + interval '1' day always adding exactly 24 hours.
You could cast the timestamp with time zone value to a plain timestamp, which discards the time zone information; then add the 1-day interval, and declare the result to be in the required time zone:
from_tz(cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) + interval '1' day, 'Europe/Berlin') as plus_1_day
or cast to a date (which could be implicit), add a day, and cast back:
from_tz(cast(cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as date) + 1 as timestamp), 'Europe/Berlin')
Adapting your example and showing the intermediate values:
select timestamp '2021-03-28 00:00:00 Europe/Berlin' as before_dst,
cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) as as_ts,
cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) + 1 as plus_1_day_ts,
from_tz(cast(timestamp '2021-03-28 00:00:00 Europe/Berlin' as timestamp) + interval '1' day, 'Europe/Berlin') as plus_1_day
from dual;
BEFORE_DST
AS_TS
PLUS_1_DAY_TS
PLUS_1_DAY
2021-03-28 00:00:00 +01:00
2021-03-28 00:00:00
2021-03-29 00:00:00
2021-03-29 00:00:00 +02:00
db<>fiddle
This assumes that you're always dealing with a fixed known time zone region; if you actually have a variable or column value with an unknown time zone then you can extract the region from that and use that as the from_tz() argument.
You should also be aware that this will work for your example at midnight, but won't work for all times. For example if your starting value was timestamp '2021-03-27 02:30:00 Europe/Berlin' then it would fail with "ORA-01878: specified field not found in datetime or interval", because it would end up try to declare 2021-03-28 02:30:00 to be in zone Europe/Berlin - and there is no such time, as that falls into the 'lost' hour of 02:00-03:00. Simply adding a day interval handles that - but then doesn't work as you expect in your example...
And this is because of this line in the documentation:
Oracle performs all timestamp arithmetic in UTC time.
2021-03-28 00:00:00 Europe/Berlin is 2021-03-27 23:00:00 UTC; adding a day to that is 2021-03-28 23:00:00 UTC; which is 2021-03-29 02:00:00 Europe/Berlin

Google Bigquery - Create time series of number of active records

I'm trying to create a timeseries in google bigquery SQL. My data is a series of time ranges covering the period of activity for that record. Here is an example:
Start End
2020-11-01 21:04:00 UTC 2020-11-02 07:15:00 UTC
2020-11-01 21:45:00 UTC 2020-11-02 04:00:00 UTC
2020-11-01 22:00:00 UTC 2020-11-02 09:48:00 UTC
2020-11-01 22:00:00 UTC 2020-11-02 06:00:00 UTC
I wish to create a new table to total the number of active records within a 15 minute block. "21:00:00" would for example be 21:00 to 21:14.59. My desired output for the above would be:
Period Active_Records
2020-11-01 21:00:00 1
2020-11-01 21:15:00 1
2020-11-01 21:30:00 1
2020-11-01 21:45:00 2
2020-11-01 22:00:00 4
2020-11-01 22:15:00 4
etc until the end of the last active range.
I would also like to be able to generate this on the fly by querying a date range and having it return every 15 minute block in the range and how many active records there was in that period.
Any assistance would be greatly appreciated.
Below is for BigQuery Standard SQL
#standardSQL
select ts as period, count(1) as Active_Records
from unnest((
select generate_timestamp_array(timestamp_trunc(min(start), hour), max(`end`), interval 15 minute)
from `project.dataset.table`
)) ts
join `project.dataset.table`
on not (`end` < ts or start > timestamp_add(ts, interval 15 * 60 - 1 second))
group by ts
if to apply to sample data from your question - output is

Time offset (Paris-Kuwait) incorrect timezone difference SQL Server

I use a query to filter records from Friday midnight until Saturday midnight based on kuwait time zone.
First when I check my server timezone I can see:
select CURRENT_TIMEZONE();
(UTC+01:00) Brussels, Copenhagen, Madrid, Paris
So I assume that the date column I need to filter is based on this timezone since the records where created and stored on this server, right?
Then I have my query:
SELECT
ID,
DATETIME,
DATETIME AT TIME ZONE 'Arab Standard Time' AS kuwait_time
FROM
admin_all.ACCOUNT_TRAN_ALL
WHERE
DATETIME BETWEEN (DATEADD(wk, DATEDIFF(wk, 6, GETDATE()), 5) AT TIME ZONE 'Arab Standard Time')
AND (DATEADD(wk, DATEDIFF(wk, 6, GETDATE()), 6) AT TIME ZONE 'Arab Standard Time')
Which returns this output:
+---------+-------------------------+--------------------------------+
| ID | DATETIME | kuwait_time |
+---------+-------------------------+--------------------------------+
| 1050554 | 2019-12-27 21:05:28.073 | 2019-12-27 21:05:28.073 +03:00 |
| 1050555 | 2019-12-27 21:05:42.586 | 2019-12-27 21:05:42.587 +03:00 |
| 1050556 | 2019-12-27 21:06:58.920 | 2019-12-27 21:06:58.920 +03:00 |
| 1050557 | 2019-12-27 21:07:12.906 | 2019-12-27 21:07:12.907 +03:00 |
| 1050558 | 2019-12-27 21:16:56.436 | 2019-12-27 21:16:56.437 +03:00 |
| 1050559 | 2019-12-27 21:17:10.533 | 2019-12-27 21:17:10.533 +03:00 |
| 1050560 | 2019-12-27 21:17:37.913 | 2019-12-27 21:17:37.913 +03:00 |
| 1050561 | 2019-12-27 21:17:37.986 | 2019-12-27 21:17:37.987 +03:00 |
+---------+-------------------------+--------------------------------+
Now my issue. Since I want this to start from last Friday at midnight and finish on last Saturday at midnight and since my local DATETIME field is UTC+1, I should have my first record starting at 22:05 to get this starting at midnight Kuwait time Since Kuwait is 2 hours ahead of me (I am UTC+ 1 and Kuwait is UTC+3).
Why I am then filtering first records at 21:05?
UPDATE: following #Matt's kind support I came up with this query that takes some of his hints, but also include the last friday and Saturday search in the where parameter that was skipped in the answer.
Now query is very slow, there must be a way to speed it up. It gives a correct results:
SELECT id,
datetime at time zone 'Romance Standard Time' AS local_time,
datetime at time zone 'Romance Standard Time' at time zone 'Arab Standard Time' AS kuwait_date
FROM admin_all.account_tran_all
WHERE datetime at time zone 'Romance Standard Time' at time zone 'Arab Standard Time' >=
(
SELECT CONVERT(datetime,
CASE
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Sunday' THEN dateadd(day,-2,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Monday' THEN dateadd(day,-3,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Tuesday' THEN dateadd(day,-4,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Wednesday' THEN dateadd(day,-5,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Thursday' THEN dateadd(day,-6,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Friday' THEN dateadd(day,-7,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Saturday' THEN dateadd(day,-8,cast(CURRENT_TIMESTAMP AS date))
END ) at time zone 'Arab Standard Time')
AND datetime at time zone 'Romance Standard Time' at time zone 'Arab Standard Time' <
(
SELECT CONVERT(datetime,
CASE
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Sunday' THEN dateadd(day,-1,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Monday' THEN dateadd(day,-2,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Tuesday' THEN dateadd(day,-3,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Wednesday' THEN dateadd(day,-4,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Thursday' THEN dateadd(day,-5,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Friday' THEN dateadd(day,-6,cast(CURRENT_TIMESTAMP AS date))
WHEN datename(weekday,cast(CURRENT_TIMESTAMP AS date)) = 'Saturday' THEN dateadd(day,-7,cast(CURRENT_TIMESTAMP AS date))
END) at time zone 'Arab Standard Time' )
the result:
| ID | Original_DATETIME | Local_time | kuwait_date |
|-------------|-------------------------|--------------------------------|--------------------------------|
| 1046053 | 2019-12-26 22:00:01.836 | 2019-12-26 22:00:01.837 +01:00 | 2019-12-27 00:00:01.837 +03:00 |
| 1046054 | 2019-12-26 22:00:01.940 | 2019-12-26 22:00:01.940 +01:00 | 2019-12-27 00:00:01.940 +03:00 |
| 1046055 | 2019-12-26 22:00:17.040 | 2019-12-26 22:00:17.040 +01:00 | 2019-12-27 00:00:17.040 +03:00 |
| 1046056 | 2019-12-26 22:00:19.046 | 2019-12-26 22:00:19.047 +01:00 | 2019-12-27 00:00:19.047 +03:00 |
| 1046057 | 2019-12-26 22:00:19.156 | 2019-12-26 22:00:19.157 +01:00 | 2019-12-27 00:00:19.157 +03:00 |
| 1046058 | 2019-12-26 22:00:44.646 | 2019-12-26 22:00:44.647 +01:00 | 2019-12-27 00:00:44.647 +03:00 |
| 1046059 | 2019-12-26 22:00:44.713 | 2019-12-26 22:00:44.713 +01:00 | 2019-12-27 00:00:44.713 +03:00 |
| 1046060 | 2019-12-26 22:00:47.483 | 2019-12-26 22:00:47.483 +01:00 | 2019-12-27 00:00:47.483 +03:00 |
So i assume that the Date field I need to filter is based on this timezone since the records where created and stored on this server, right?
Sorry, but no - that is not right. There is no implicit binding between the fields stored in your database and the server's time zone. The server's time zone is used by the GETDATE() function to determine the local time, but it is subsequently discarded.
For DATETIME and DATETIME2 fields, you must design your database such that you know what the time zone reference is. For example, often UTC is the reference. If Paris time is your reference, then you'd have to know that when you do your conversions.
Here's how AT TIME ZONE works:
With DATETIME and DATETIME2 types, the AT TIME ZONE statement simply asserts the intended time zone. The result is a DATETIMEOFFSET type with the same local date and time as the original value, but with the correct offset for that time zone applied.
With DATETIMEOFFSET types, the AT TIME ZONE statement converts from the value given to the intended time zone. A DATETIMEOFFSET with the same point in Universal Time is returned, but the date, time, and offset will change to reflect the new time zone.
Thus, if you are starting with a DATETIME or DATETIME2 field, you will need two AT TIME ZONE statements to convert from one time zone to another.
SELECT
DATETIME AT TIME ZONE 'Romance Standard Time' as paris_time,
DATETIME AT TIME ZONE 'Romance Standard Time' AT TIME ZONE 'Arab Standard Time' AS kuwait_time
...
The simpler solution is to use DATETIMEOFFSET fields to begin with. Then you won't have to have any implicit knowledge about the time zone reference.
I also suggest declaring local variables for your start/end points. That will make your code much more readable.
DECLARE #NowInKuwait DATETIMEOFFSET = SYSDATETIMEOFFSET() AT TIME ZONE 'Arab Standard Time';
DECLARE #Start DATETIMEOFFSET = DATEADD(wk, 5, #NowInKuwait) AT TIME ZONE 'Arab Standard Time';
DECLARE #End DATETIMEOFFSET = DATEADD(wk, 6, #NowInKuwait) AT TIME ZONE 'Arab Standard Time';
SELECT ...
FROM ...
WHERE DATETIME >= #Start AND DATETIME < #End
I'm guessing a bit on your desired start and end times, as the DATEADD and DATEDIFF statements in your question do not have the correct parameters.
Also note that I call AT TIME ZONE one more time in the declarations for #Start and #End. That is in case the offset has changed between "now" and the resulting date. If you're only using this for Kuwait, then you can omit that since Kuwait is fixed to UTC+3. However many time zones use daylight saving time, or have had changes to their standard time offset, and thus one can't always assume the offsets are consistent.

Reset the date portion to the last of the month while preserving the time

Is there any way to reset the date portion to the last day of the month while preserving the time? For example:
2018-01-02 23:00:00 -> 2018-01-31 23:00:00
2018-04-04 10:00:00 -> 2018-04-30 10:00:00
The Oracle function last_day() does exactly this. Try:
select last_day(sysdate), sysdate
from dual
to see how it works.
Ironically, I usually find the preservation of the date to be counterintuitive, so my usual usage is more like:
select last_day(trunc(sysdate))
from dual

Add nchar field to DateTime in Informix

I have a datetime field that stores times in UTC format. There's another nchar field that stores the time zone difference based on a location. I'm trying to combine the two for a report so that the time displayed matches the appropriate time zone.
time_stamp | time_zone
---------------------------------
2015-11-24 21:00:00 | -0500
2015-11-23 15:00:00 | -0600
Expected output:
2015-11-24 16:00:00
2015-11-23 09:00:00
I was able to get this to work by using:
extend(time_stamp, year to minute) + (CAST(LEFT(time_zone,3) as int)) units hour
While this technically works for the current situation, I really don't like using the CAST and LEFT functions on the time_zone field since it breaks if the value is not negative. Seems like there's a much better solution, possible something with TO_CHAR. In an informix database, what is the proper way to combine the dateime and nchar fields so that the output time is correct? Ideally I would like to output in non 24 hr format (4:00 PM, etc...) but at this point I'm mainly focused on getting the correct time.
Ideally, your time zone column would be an INTERVAL HOUR TO MINUTE type; you'd then simply add the two columns to get the desired result. Since it is a character type, substringing in some form will be necessary. Using LEFT is one option; SUBSTRING is another; using the Informix subscripting notation is another. The CAST isn't crucial; Informix is pretty good about coercing things.
Unless you actually want only hours and minutes in the result (which is a legitimate choice), your EXTEND operation is unnecessary and undesirable; it means your result won't include the seconds value from your data.
Note that some time zones include minutes values. Newfoundland is on UTC-04:30; India is on UTC+05:30; Nepal is on UTC+05:45. (See World Time Zone for more information.) Getting the minutes accurate is harder because the sign has to be carried through.
As to formatting in AM/PM notation, apart from the question 'why', the answer is to use the TO_CHAR() function and a ghastligram expressing the time format that you want.
TO_CHAR()
GL_DATETIME
GL_DATE
Demonstration:
create table zone_char(time_stamp datetime year to second, time_zone nchar(5));
insert into zone_char values('2015-11-24 21:00:00', '-0500');
insert into zone_char values('2015-11-23 15:00:00', '-0600');
insert into zone_char values('2015-11-22 17:19:21', '+0515');
insert into zone_char values('2015-11-21 02:56:31', '-0430');
Various ways to select the data:
select extend(time_stamp, year to minute) + LEFT(time_zone,3) units hour,
time_stamp + LEFT(time_zone,3) units hour,
time_stamp + time_zone[1,3] units hour,
time_stamp + time_zone[1,3] units hour + (time_zone[1] || time_zone[4,5]) units minute,
TO_CHAR(time_stamp + time_zone[1,3] units hour + (time_zone[1] || time_zone[4,5]) units minute,
'%A %e %B %Y %I.%M.%S %p')
from zone_char;
Sample output:
2015-11-24 16:00 2015-11-24 16:00:00 2015-11-24 16:00:00 2015-11-24 16:00:00 Tuesday 24 November 2015 04.00.00 PM
2015-11-23 09:00 2015-11-23 09:00:00 2015-11-23 09:00:00 2015-11-23 09:00:00 Monday 23 November 2015 09.00.00 AM
2015-11-22 22:19 2015-11-22 22:19:21 2015-11-22 22:19:21 2015-11-22 22:34:21 Sunday 22 November 2015 10.34.21 PM
2015-11-20 22:56 2015-11-20 22:56:31 2015-11-20 22:56:31 2015-11-20 22:26:31 Friday 20 November 2015 10.26.31 PM
And note how much easier it is when the time zone is represented as an INTERVAL HOUR TO MINUTE:
alter table zone_char add hhmm interval hour to minute;
update zone_char set hhmm = time_zone[1,3] || ':' || time_zone[4,5];
SELECT:
select time_stamp, hhmm, extend(time_stamp + hhmm, year to minute),
time_stamp + hhmm,
TO_CHAR(time_stamp + hhmm, '%A %e %B %Y %I.%M.%S %p')
from zone_char;
Result:
2015-11-24 21:00:00 -5:00 2015-11-24 16:00 2015-11-24 16:00:00 Tuesday 24 November 2015 04.00.00 PM
2015-11-23 15:00:00 -6:00 2015-11-23 09:00 2015-11-23 09:00:00 Monday 23 November 2015 09.00.00 AM
2015-11-22 17:19:21 5:15 2015-11-22 22:34 2015-11-22 22:34:21 Sunday 22 November 2015 10.34.21 PM
2015-11-21 02:56:31 -4:30 2015-11-20 22:26 2015-11-20 22:26:31 Friday 20 November 2015 10.26.31 PM