I have stored time in DB as time without time zone and I want cast this time to time with timezone and then compare it with current time.
WHERE (my_time::time AT TIME ZONE country_timezone)
BETWEEN (current_time::time) - 60 * INTERVAL '1 minute'
AND (current_time::time) + 60 * INTERVAL '1 minute'
But the problem is that It is always compared against the time without time zone even after I casted It to time with timezone.
So for example
'my_time' => string '18:00:00+09' (length=11)
'current_time_plus' => string '10:51:47.997405'
'current_time_minus' => string '09:51:47.997405'
This should not match, but since my_time = 10:00:00 wihtout time zone then It is matching. How can I match the time as time with timezone ?
I would suggest not mixing times with time zone and without. I can't check myself but I assume
WHERE (my_time::time AT TIME ZONE country_timezone)
BETWEEN (current_time::timetz) - 60 * INTERVAL '1 minute'
AND (current_time::timetz) + 60 * INTERVAL '1 minute'
should work. because after you ::time AT TIME ZONE it becomes timetz and you compare it againt timestamptz::time, which gives you unexpected result
Related
I want to query rows for a given time range and also filter between given times of day.
Example I want to filter for times of day between '9.00 AM' and '10.00 PM' of every date within a given time range.
My table looks like this.
This is my sample code:
SELECT *
FROM public.energy
WHERE time >= date_trunc('month', NOW() - INTERVAL '1 MONTH') AT TIME ZONE 'Asia/Bangkok'
AND time < date_trunc('MINUTE', NOW()- INTERVAL '1 MONTH') AT TIME ZONE 'Asia/Bangkok'
AND name = 'SWU0001'
ORDER BY id DESC;
I already select data between dates that I want, but I want to filter for specific times.
SELECT *
FROM public.energy
WHERE name = 'SWU0001'
AND time >= date_trunc('month' , now() AT TIME ZONE 'Asia/Bangkok' - interval '1 month') AT TIME ZONE 'Asia/Bangkok' -- !
AND time < date_trunc('minute', now() AT TIME ZONE 'Asia/Bangkok' - interval '1 month') AT TIME ZONE 'Asia/Bangkok' -- !
AND (time AT TIME ZONE 'Asia/Bangkok')::time BETWEEN '09:00' AND '22:00' -- !!!
ORDER BY id DESC;
Don't call a timestamptz column "time". The name is misleading, and it's a basic type name.
Also, to work with local time of 'Asia/Bangkok' you need to get the local time for that time zone before applying date_trunc(), and cast the adjusted value back to timestamptz at the end. Basics:
Ignoring time zones altogether in Rails and PostgreSQL
Intuitively these two intervals represent the same amount of time. But the difference shows around daylight-saving time changes, in which case "1 day" can mean either "23 hours" in the spring or "25 hours" in the autumn.
I tested with PostgreSQL and there these two intervals don't mean the same amount of time:
set timezone TO 'CET';
SELECT timestamp with time zone'2020-03-29 0:00 Europe/Bratislava' + INTERVAL '1' DAY,
timestamp with time zone'2020-03-29 0:00 Europe/Bratislava' + INTERVAL '24' HOUR;
returns this:
2020-03-29T22:00:00.000Z 2020-03-29T23:00:00.000Z
The client's time zone was UTC, that's why the returned values are in UTC. But important is that they are not the same.
I also tried with MySQL but seems to me that it doesn't support time zones, only time zone offsets. And zone offsets don't have DST changes so a day is always 24 hours.
On the other hand Apache Calcite, that backs many SQL implementations such as Apache Drill, Apache Flink, Apache Beam and many more, represents interval literals as java's BigDecimal: the day-second interval is converted to milliseconds and day is assumed to always be 24 hours.
My question is: which approach is correct according to the SQL standard?
EDIT:
Checked more DBs:
Oracle: SELECT INTERVAL '1' DAY FROM DUAL returns +01 00:00:00. Adding either 1 day or 24 hours to 2020-03-29 0:00 CET provides the same result: 24 hours are added.
SQL Server and DB2: as far as I can tell, only time zone offsets are supported. So same case as MySql: they don't support time zones with DST changes.
Conclusion: PostgreSQL seems to be the only exception to have 1 day different from 24 hours.
I think your answer lies in this resource: https://www.postgresql.org/docs/9.1/functions-datetime.html
When adding an interval value to (or subtracting an interval value
from) a timestamp with time zone value, the days component advances
(or decrements) the date of the timestamp with time zone by the
indicated number of days. Across daylight saving time changes (with
the session time zone set to a time zone that recognizes DST), this
means interval '1 day' does not necessarily equal interval '24 hours'.
For example, with the session time zone set to CST7CDT, timestamp with
time zone '2005-04-02 12:00-07' + interval '1 day' will produce
timestamp with time zone '2005-04-03 12:00-06', while adding interval
'24 hours' to the same initial timestamp with time zone produces
timestamp with time zone '2005-04-03 13:00-06', as there is a change
in daylight saving time at 2005-04-03 02:00 in time zone CST7CDT.
I would like to return all the timestamps from a database as long as they are before 6 am on the day they were made. I tried to do this:
SELECT source_created_at AT TIME ZONE 'US/PACIFIC' FROM time_entry
WHERE source_created_at AT TIME ZONE 'US/PACIFIC' < '06:00:00'::time
but got this error:
ERROR: operator does not exist: timestamp without time zone < time without time zone
LINE 2: WHERE source_created_at AT TIME ZONE 'US/PACIFIC' < '06:00:0...
I also tried < '06:00:00' and TIME'06:00:00; but got the same error. I'm new to SQL and the documentation has not been helpful here.
You cannot compare a timestamp to a time. You need to cast the timestamp to time first:
WHERE (source_created_at AT TIME ZONE 'US/PACIFIC')::time < '06:00:00'::time
Edit from the comments
If you want all timestamps before tomorrow 6 AM in the same timezone:
WHERE source_created_at at time zone 'US/PACIFIC'
< current_date at time zone 'US/PACIFIC' + interval '1 day 6 hour'
The following query against Postgres database some how leaves few rows for the date: 2017-10-01. I have added time zone also. Is there a way to solve this issues?
select min(p.start_timestamp AT TIME ZONE p.timezone AT TIME ZONE 'America/Phoenix') as Date,
'America/Phoenix' AS Timezone, sum(GREATEST(0, p.value)) as Value, p.uom as UnitOfMeasurement
from main.production_ts_2017_10 p
where p.start_timestamp AT TIME ZONE p.timezone >= to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
and p.start_timestamp AT TIME ZONE p.timezone <= '2017-10-30'
and p.serial_number = '5T7842974Z'
group by date_trunc('hour', p.start_timestamp AT TIME ZONE p.timezone AT TIME ZONE 'America/Phoenix'), p.uom
order by Date
Let's simplify it down to just this part of the query:
p.start_timestamp AT TIME ZONE p.timezone >= to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
This is a conditional expression around the >= operator. It has a left-hand side (p.start_timestamp AT TIME ZONE p.timezone) and a right-hand side: (to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day') that are each evaluated separately, so the final conditional result (true/false) can be determined for each record.
Postgres database some how leaves few rows for the date: 2017-10-01
Look again at the right-hand side of the expression:
to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
That reduces down to this value:
'2017-10-02'
In other words, you're limited to records that come after '2017-10-01'. Do you really want that INTERVAL section in there? Did you want to subtract two days instead of add?
Let's assume two things:
You're asking "why does this query show a few rows for October 1st when it should show only rows for October 2nd or later"
p.start_timestamp is of type TIMESTAMP WITHOUT TIME ZONE (I feel safe in assuming this, because otherwise the double-timezone casting of it in other parts of the query makes no sense).
You have this WHERE clause:
where p.start_timestamp AT TIME ZONE p.timezone >= to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
Now, the left-hand side of the query is p.start_timestamp AT TIME ZONE p.timezone which will evaluate to a TIMESTAMP WITH TIME ZONE. The right-hand side of the query, however, is to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day' which evaluates to a TIMESTAMP WITHOUT TIMEZONE.
Whenever you compare a TIMESTAMP WITH TIMEZONE to a TIMESTAMP WITHOUT TIMEZONE, the results are going to be dependent on your current TimeZone setting in your psql session, because the TIMESTAMP-no-TZ will be evaluated as if it is in the psql TimeZone. For example, 12:31AM in 'America/Phoenix' on October 2nd can be 11:31AM in 'America/Los_Angeles', depending on the date (and BTW, you chose the worst possible time zone as your default, because America/Phoenix is just wierd). This would result in you seeing records from October 1st, depending on the value of p.timezone.
You're compounding the problem with this:
select min(p.start_timestamp AT TIME ZONE p.timezone AT TIME ZONE 'America/Phoenix') as Date
... so now you're displaying all timestamps as America/Phoenix timestamps regardless of what timezone they were originally in, or what timezone they were compared with. If your TimeZone setting is UTC, you'll see a LOT of October 1st records, because Oct 2nd in UTC overlaps with Oct 1st in America/Phoenix for six or seven hours depending on the date.
I suspect, given the rest of the query, what you want for that WHERE clause is:
where ( p.start_timestamp AT TIME ZONE p.timezone )
>=
( to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day' )
AT TIME ZONE 'America/Phoenix'
AND p.start_timestamp AT TIME ZONE p.timezone
<=
( TIMESTAMP '2017-10-30' AT TIME ZONE 'America/Phoenix' )
All of this is an illustrative lesson in why you should be storing all of your timestamp data as TIMESTAMP WITH TIME ZONE, instead of what you're doing here. As long as you're storing the timezone in a separate field an using AT TIME ZONE all the time, you're going to keep breaking queries. Also, with all of this casting time zones, any indexes you have on those time columns are unlikely to be used.
I have to compare the two dates to get the last 10 min records from database,I'm using postgresql,
I have write this query
select *
FROM x_table
WHERE x_time >= (NOW()::TIMESTAMP WITHOUT TIME ZONE - interval '10 minutes');
x_time is timestamp without time zone so that's why i'm converting the other to the same but it won't giving the result.
Query should written the last 1 min records but i think due to time zone issue it is not giving the result.
how can i resolve the issue?
If x_time is timestamp without time zone, you should specify proper time zone for NOW() function.
Try
select *FROM x_table WHERE x_time >= (NOW() at time zone '-04' - interval '10 minutes')