Add nchar field to DateTime in Informix - sql

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

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

Postgres to_timestamp function

I have a python script that insert hourly readings into a postgres db. It is failing in 2010-03-28. How is postgres interpreting both 01:00:00 and 02:00:00 as 02:00:00. what am I doing wrong (ps: works for other dates prior to this)
select to_timestamp('28/03/2010 01:00:00','DD/MM/YYYY HH24:MI:SS');
to_timestamp
------------------------
2010-03-28 02:00:00+01
(1 row)
select to_timestamp('28/03/2010 02:00:00','DD/MM/YYYY HH24:MI:SS');
to_timestamp
------------------------
2010-03-28 02:00:00+01
(1 row)
what am I doing wrong?
Nothing wrong.
As commented by a_horse_with_no_name, what you are seeing is the effect of Daylight Saving Time. On 2 AM CET on March 28th, the clock skips one hour and goes directly to 3 AM. I assume that your timezone is UTC+01, so you are seeing this on 1 AM > 2 AM.
That particular day only has 23 hours: both dates do represent the same point in time, which is what you are seeing in the results generated by to_timestamp().
Extracting the corresponding epochs, you can also see that the results are identical:
select
to_timestamp('28/03/2010 01:00:00','DD/MM/YYYY HH24:MI:SS') "1 AM",
to_timestamp('28/03/2010 02:00:00','DD/MM/YYYY HH24:MI:SS') "2 AM",
extract(epoch from to_timestamp('28/03/2010 01:00:00','DD/MM/YYYY HH24:MI:SS')) "1 AM epoch",
extract(epoch from to_timestamp('28/03/2010 02:00:00','DD/MM/YYYY HH24:MI:SS')) "2 AM epoch"
1 AM | 2 AM | 1 AM epoch | 2 AM epoch
:--------------------- | :--------------------- | :--------- | :---------
2010-03-28 02:00:00+01 | 2010-03-28 02:00:00+01 | 1269738000 | 1269738000
This is just one of the many tricks involved when working with dates. There is not much you can do about that from Postgres' perspective: it is on your application to handle that.
I'm afraid that I disagree with the statement that you're doing nothing wrong.
If your database timezone is set to a timezone thats changes to daylight savings at 01:00 on 2010-03-28, e.g., 'Europe/London', then you shouldn't be attempting to create records for both 01:00:00 and 02:00:00 on 2010-03-28, as 01:00:00 does not exist (the time jumps from 00:59:59 to 02:00:00).
If you're not meant to be following daylight savings, then you need to change the DB server timezone to one that doesn't, e.g., 'GMT' or 'UTC'.

sysdate format not working in where clause with time

I am trying to run following query on oracle at PL/SQL developer to select a list of time slots between current time and end of the day:
SELECT T.VISIT_DATE
FROM REGISTRATION.VU_SCHEDULE T
WHERE T.VISIT_DATE BETWEEN TO_DATE(SYSDATE, 'DD-MON-YYYY HH24:MI:SS')
AND TO_DATE('27-MARCH-2020 23:59:59', 'DD-MON-YYYY HH24:MI:SS')
ORDER BY VISIT_DATE
but it gives me result of whole day instead of current time of day
VISIT_DATE
1 3/27/2020 9:00:00 AM
2 3/27/2020 9:15:00 AM
3 3/27/2020 9:30:00 AM
4 3/27/2020 9:45:00 AM
5 3/27/2020 10:00:00 AM
6 3/27/2020 10:15:00 AM
7 3/27/2020 10:30:00 AM
8 3/27/2020 10:45:00 AM
9 3/27/2020 11:00:00 AM
10 3/27/2020 11:15:00 AM
11 3/27/2020 11:30:00 AM
12 3/27/2020 11:45:00 AM
e.g if current time is 11:00 AM then it should give result from current time.
I've tried trunc(sysdate) but it doesn't work
NOTE:
The condition must have date and time from now to the end of the day with format.
must have date and time from now to the end of the day with format.
You could do:
where t.visit_date >= sysdate and t.visit_date < trunc(sysdate) + 1
Rationale:
sysdate gives you the current date/time, that represents the lower bound of the interval
trunc(sysdate) is the beginninig of the current day (today at midnight), to which you can add 1 to get the beginning of the next day; this is the (exclusive) upper bound of the range
Note that there is no point applying to_date() to function sysdate, that produces a date alreay.

Get the total sum hours in a column SQL SERVER

Sql Fiddle Example
I have this result table
Id Hours
----- -----
1 09:00
2 09:30
3 10:00
4 10:30
5 11:00
6 11:30
7 12:00
8 12:30
9 13:00
10 13:30
11 14:00
12 14:30
13 15:00
14 15:30
15 16:00
16 16:30
17 17:00
18 17:30
19 18:00
I need to get the total sum hours, for example from 09:00 to 18:00 there is a total of :
9
hours, I need to get this sum of hours
Your table schema hour is varchar, you need to cast as time, then do the calculation
SELECT datediff(hour,min(cast(hour as time)),max(cast(hour as time)))
FROM Timetable
sqlfiddle
NOTE
I would suggest your hour column as datetime or time instead of varchar. because hour column intention is time.
EDIT
If your time is 9:00 to 17:30, you can try to use datediff minute to get the total diff minutes then divide 60 to get hours.
SELECT datediff(minute,min(cast(hour as time)),max(cast(hour as time))) / CAST(60 as float)
FROM Timetable
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=6e005cdfad4eca3ff7c4c92ef14cc9c7
use datediff function
select datediff(hour,min(h),max(h)) from
(
select CAST(hour AS TIME) as h from Timetable
) as t
strongly disagreed to put time value in varchar ,so it is better change your data type from varchar to time
declare #a time = '13:00',#b time = '17:30' --- Here you can give time, what you need.
select distinct convert(varchar(20)
, datediff(MINUTE,#a,#b) / 60)
+ ':' +
convert(varchar(20), datediff(MINUTE,#a,#b) % 60)
from #Timetable
where hour in (#a,#b)
For your SQL Fiddle Sample Data.
Obviously, you need to use datediff(). However, you should be doing the datediff() in minutes or seconds and then converting to hours:
SELECT datediff(minute, min(cast(hour as time)), max(cast(hour as time))) / 60.0
FROM Timetable;
This will handle the case where the number of hours is not an exact number of hours.