MySQL: DATE_SUB/DATE_ADD that accounts for DST? - mysql

This returns 1 (aka TRUE)
SELECT DATE_SUB(NOW(), INTERVAL 24*100 HOUR) = DATE_SUB(NOW(), INTERVAL 100 DAY);
100 days ago, the hour of day does not change. But due to Daylight Savings Time (US), 100 twenty-four hour periods ago is actually one hour earlier than if you counted by days. If the above statement accounted for DST, it would return 0 or FALSE.
Is there a way I can say to account for DST for a given statement or session? I would prefer not to use UNIX_TIMESTAMP since it cuts off anything past 2038.

You'll need to create a custom function, something like this.
DELIMITER $$
CREATE FUNCTION DST(ADatetime DATETIME) RETURNS DATETIME
BEGIN
DECLARE result DATETIME;
SET Result = ADatetime;
IF ADatetime >= startDST AND ADateTime <= endDST THEN
result = DATE_SUB(ADatetime, INTERVAL 1 HOUR);
END IF;
RETURN result;
END $$
DELIMITER ;

This is really just a matter of converting to UTC and back:
CONVERT_TZ(DATE_SUB(CONVERT_TZ(NOW(),##session.time_zone,'UTC'), INTERVAL 24*100 HOUR),'UTC',##session.time_zone);
This assumes you have the timezone tables set up to use named time zones. If not, you can use '+0:00' instead of 'UTC'

How would cutting off anything past 2038 be a real problem when you can be sure that 64bit integer timestamps will be immplemented everywhere 20 years before that at least ?
Seriously, there are so many issues with the datetime / timestamp types in MySQL that you should try and avoid them when possible.
Do you store many dates beyond 2038 ?
And, why not try using PostgreSQL which has much more advanced type support ?

Related

Compare sysdate with iso 8601 using oracle

The problem is that I am currently trying to resolve is to select a date time value (iso 8601) and compare it with the sysdate and time, and the sysdate/time has to be 20 min in the past.
I have tried using TO_TIMESTAMP and tried to convert the SYSDATE but the problem keeps returning.
select * from table
where timestamp_from_the_table > to_date(systimestamp, 'YYYY-MM-DD"T"hh24:mi:ss.ff3TZR') -INTERVAL '30' MINUTE
The result I would like is to have a list with the timestamps from table but filtered with the time only from 20 min in the past not longer.
Hope someone can help me. Thanks in advance.
Never store date values as string, i.e. VARCHAR2!
In your case you can run
WHERE TO_TIMESTAMP_TZ(timestamp_from_the_table, 'YYYY-MM-DD"T"hh24:mi:ss.ff3TZR')
> systimestamp - INTERVAL '30' MINUTE
It might be an option to create a virtual column, i.e.
ALTER TABLE your_table ADD (real_timestamp TIMESTAMP(3) WITH TIME ZONE (TO_TIMESTAMP_TZ(timestamp_from_the_table, 'YYYY-MM-DD"T"hh24:mi:ss.ff3TZR') ));
Then you could simply run
WHERE real_timestamp > systimestamp - INTERVAL '30' MINUTE

UPDATE column where timediff is greater than 5 minutes

I have a table (sessions) which has 2 columns that I have to use for this query.
Session_Active (which is a tinyInt) and Last_active(which is a datetime).
I want to create a query that calculates the time difference between now and 'Last_active' for all tables WHERE 'Session_Active' is true, and if its greater than 5 minutes it should change 'Session_Active'.
This is the part that I have which works:
SELECT timediff(now(), `Last_Active`) from sessions WHERE `Session_Active` = true;
I have no clue at all how I can check if the difference is greater than 5 minutes, neither do I know where/how to put the UPDATE Session_Active = false (If the difference is 5 minutes or more)
Thanks in advance! (:
You can use the following solution using DATE_SUB:
UPDATE sessions SET `Session_Active` = 0
WHERE `Last_Active` <= DATE_SUB(NOW(), INTERVAL 5 MINUTE)
AND `Session_Active` = 1
You want to use a timestamp solution?
You can use TIMESTAMPDIFF:
UPDATE sessions SET `Session_Active` = 0
WHERE TIMESTAMPDIFF(MINUTE, `Last_Active`, NOW()) >= 5
AND `Session_Active` = 1
Note: You should be careful with using TIMESTAMP! Some information why you shouldn't use TIMESTAMP: https://stackoverflow.com/a/35469149/3840840. On this answer there is a reference to this article describing the performance of DATETIME, TIMESTAMP and INT.
The TIMESTAMP solution is only working until 2038. This will be caused by the Year 2038 problem.
A very good explanation of this problem and what is happening in 2038: https://stackoverflow.com/a/2012620/3840840
You can use UNIX_TIMESTAMP(date)
When UNIX_TIMESTAMP() is called with a date argument, it returns the value of the argument as seconds since '1970-01-01 00:00:00' UTC. The date argument may be a DATE, DATETIME, or TIMESTAMP string, or a number in YYMMDD, YYMMDDHHMMSS, YYYYMMDD, or YYYYMMDDHHMMSS format. The server interprets date as a value in the current time zone and converts it to an internal value in UTC. This is faster then DATE_SUB on large table set.
UPDATE sessions
SET `Session_Active` = 0
WHERE UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(`Last_Active`) > 300
AND `Session_Active` = 1

Mysql Convert_tz

I'm trying to convert a datetime from Asia/Manila to EST timezone
without declaring the exact interval like
date_sub(), subdate(), date_add(), adddate()
i find it easy to use
SELECT DATE_SUB('2016-04-04 13:00:00', INTERVAL 12 HOUR);
the result will be2016-04-04 01:00:00
But Im trying to create a dynamic script where i don't need to look how many hours is the difference between two timezone
and i find Convert_TZ() to do job
SELECT CONVERT_TZ('2016-04-04 13:00:00', 'Asia/Manila', 'EST');
but the result of this query is 2016-04-04 00:00:00
Maybe this native function is not including the "Daylight saving time(DST)"
Does anyone know how to do the trick?
where i can easily convert the time including the DST
to any timezone without hard coding the interval hour between the two timezone?
Thanks
Okay, my problem is solved, i use two option
First :
I simply use 'US/Eastern' not 'EST' to include the daylight in conversion.
Second:
Because I didn't know the first option earlier i do this to solve my problem at first.
I create a table that compose of the date where it is DST
which i found in some site online..
Then
I create a mysql function where its lookup to the table above
which if the specified date is between that DST Start and DST End it will automatically add 1 hour,
My function is like this,
CREATE FUNCTION usp_Convert(specified_date DATETIME, From_Timezone VARCHAR(20), To_Timezone VARCHAR(20), is_DST INT(1)) RETURNS datetime
DECLARE theDate DATETIME;
SET theDate = CONVERT_TZ(specified_date, From_Timezone, To_Timezone);
IF is_DST = 1 AND To_Timezone= 'EST' THEN
SET theDate = ADDDATE(theDate, INTERVAL 1 HOUR); END IF;
RETURN theDate;
This might not be the best answer but this totally solved my problem
Thanks.

What's the difference between a TimeStamp field and mysql curdate

I was working and my friend told me to use curdate() on mysql query to get the current date of the server... And I told him that I was using Time_Stamp field for date/time.
Now I start to think, is there a huge difference between this two ways ? One is better than the other? Or there is something that makes it a not good practice ? Also there is a now() that can be used too. I just wanted to understand how does it work or wich one is the best and why.
Short version:
NOW() = CONCAT(CURDATE(), ' ', CURTIME());
CURDATE() = DATE(NOW());
Some explanation:
NOW() gets both date (CURDATE()) and time (CURTIME()).
So if we do it the other way round, CURDATE() = DATE(NOW()).
Regarding timestamp, in MySQL Data Types we can see timestampis 3 bytes, while datetime is 8 bytes.

MySQL Select rows <= now(), using separated time fields

I have a table 't' with date(yyyy-mm-dd), hour(1-12), minute(00-59), ampm(a/p), and timezone(pst/est) fields.
How can I select the rows that are <= now()? (ie. already happened)
Thank you for your suggestions!
edit: this does it without attention to the hour/minute/ap/tz fields:
SELECT * FROM t.date WHERE date <= now()
Here's one way to do it - combine all your seconds, minutes, etc into a date and compare to NOW(), making sure you do the comparison in the same time-zone. (Untested):
SELECT *
FROM t
LEFT JOIN y ON t.constant=y.constant
WHERE CONVERT_TZ(STR_TO_DATE(CONCAT(date,' ',hour,':',minute,' 'ampm),
'%Y-%m-%d %l:%i %p' ),
timezone,"SYSTEM") < NOW();
If your hour is 01 - 12 not 1-12 then use %h instead of %l in the STR_TO_DATE.
The STR_TO_DATE tries to stick your date and time columns together and convert them into a date.
The CONVERT_TZ(...,timezone,"SYSTEM") converts this date from whatever timezone is specified in the timezone column to system time.
This is then compared to NOW(), which is always in system time.
As an aside, perhaps you should make a single column date using MySQL's date datatype, as it's a lot easier to do arithmetic on that!
For reference, here is a summary of very useful mysql date functions where you can read up on those featuring in this answer.
Good luck!
SELECT * FROM t
WHERE `date`<=DATE_SUB(curdate(), INTERVAL 1 DAY)
OR (
`date`<=DATE_ADD(curdate(), INTERVAL 1 DAY)
AND
CONVERT_TZ(CAST(CONCAT(`date`,' ',IF(`hour`=12 AND ampm='a',0,if(ampm='a',`hour`,`hour`+12)),':',`minute`,':00') AS DATETIME),'GMT',`timezone`)<=NOW()
)
Rationale for date<=DATE_[ADD|SUB](curdate(), INTERVAL 1 DAY):
The fancy conversion is quite an expensive operation, so we don't want it to run on the complete table. This is why we pre-select against an UNCHANGED date field (possibly using an index). In no timezone can an event being more than a day in current timezone's past be in the future, and in no timezone can an event more than a day in the curent timezone's future be in the past.