Consider this. On my server, I convert a (UTC/GMT) timestring like this:
strtotime('Fri Feb 18 21:08:38 +0000 2011')
My server returns
1298063318
This is correct, since all unix timestamp converters that I tested, return the same. And vice versa, if I insert not the date but the timestamp, I get returned the timestring as given above. But if I convert the timestamp on my server:
date("Y-m-d H:i:s", 1298063318);
I get back a different date than expected (being 'Fri Feb 18 21:08:38 +0000 2011'):
2011-02-18 22:08:38
So it's off an hour. This is probably because my servers timezone is set at Europe/Paris, and it thus translates the timestamp into UTC/GTM + 1. But MySQL, ran on the same server and having the same timezone, gives me back another result:
SELECT FROM_UNIXTIME(1298063318) = 2011-02-18 22:08:15
In other words, it is off 18 seconds. Can somebody explain why?
For MySQL, a leap second correction is used for the date functions (MySQL Documentation). For PHP date functions, leap seconds are not taken into account. For that reason, you get now a 24 seconds difference.
You can try the following to solve your problem: http://pumka.net/2010/10/24/why-mysql-timestamp-is-24-seconds-different-from-php/
PS. 38 - 15 = 23
Related
There's lots of stuff on the internet about converting MySQL timestamps, how it works etc. But what I need is the opposite: knowing that MySQL stores every datetime data as UTC in TIMESTAMP fields, all I want is to direclty retrieve the stored UTC value without MySQL messing around the datetime with system/server/connection timezones.
You see, we've built a simple node.js feeder which reads from several third-part MySQL databases (so I can't change their timezone settings) and save the gathered data to a Elasticsearch, as a "denormalization process". As the original data comes from different timezones, I need to store them in UTC, so I can easily coordinate further GETs and aggregations.
I know I can set the connection timezone on the fly and I know I can change every timestamp field fetched in my node application, but since MySQL engine already stores timestamps in UTC, why should I add any other step if I could simply get it directly, without converting functions or costly data processings?
In a nutshell, I'd like to know: is there a way to bypass MySQL automatic GMT conversion?
MySQL provides a UNIX_TIMESTAMP function which returns a raw integer value. And that isn't subject to timezone conversions at all.
SELECT UNIX_TIMESTAMP( timestamp_col ) ...
But that returns a raw integer, not a datetime. The client would need to do the conversion into a "datetime" type object, if that's needed.
Another option would be to use the CONVERT_TZ function to convert to UTC from the session time_zone setting.
SELECT CONVERT_TZ( timestamp_col, ##session.time_zone,'+00:00')
But, that doesn't really "bypass" timezone conversion. One downside of this approach is if the session time_zone is affected by daylight saving time changes, there's ambiguity with a one hour period each year when the clock "falls back" one hour. e.g. Sunday, Nov 1 2015 2AM CDT transition to Sunday Nov 1 2015 1AM CST. (Converting back from UTC, if we get 1:30 AM in the session time_zone, we don't "know" if that's CDT or CST. And the conversion back to UTC doesn't know which it is either.)
Another option (which I think you already mentioned) is changing the session time_zone to UTC. Then you could just return the timestamp_col value as UTC. You could save the current time_zone setting, and set it back when you are done, e.g.
SET #save_session_time_zone := ##session.time_zone ;
SET time_zone = '+00:00' ;
SELECT timestamp_col ...
SET time_zone = #save_session_time_zone ;
But your client Connector might do some not-so-helpful conversions when the time_zone of the MySQL database session doesn't match the time_zone of the client, like the funky shenanigans the JDBC driver (MySQL Connector/J) does. (That concern isn't limited to returning UTC; that's a concern whenever the time_zone of the client doesn't match the time_zone of the database session.)
It looks like there's no way to get the original UTC value from a MySQL field; every single function uses the timezone setting, be that SYSTEM or any other you configure.
The way MySQL forces you to use a date conversion is, at least, very constraining. For example, say you have a MySQL server set to a timezone with GMT -03:00 and GMT/DST -02:00 and you store a datetime like '2016-07-01 10:00:00'. If you select this value after the DST has ended, you'll get '2016-07-01 09:00:00'.
You can't tell what time it is for sure unless you store the GMT offset separately or you previously know what timezone the server was when it was stored.
We used the second approach. We saved the server timezone and used it to calculate the offset and return an ISO datetime, so future calculations can be made easily.
DROP FUNCTION IF EXISTS `iso_datetime`;;
CREATE FUNCTION `iso_datetime` (
p_datetime TIMESTAMP
) RETURNS VARCHAR(25)
READS SQL DATA
BEGIN
DECLARE _timezone VARCHAR(255) DEFAULT NULL;
DECLARE _offset VARCHAR(6) DEFAULT NULL;
SET _timezone = (SELECT timezone FROM network);
SET _offset = (SELECT SUBSTRING(TIMEDIFF(p_datetime,CONVERT_TZ(p_datetime, _timezone,'UTC')), 1,6));
RETURN CONCAT(DATE_FORMAT(p_datetime, '%Y-%m-%dT%H:%i:%S'), _offset);
END;
In order to do so, you have to load timezone info into MySQL, so the server can calculate the tz offset of the date for you.
Development is localhost running version 5.6.16, production is 5.1.73-cll
The DATE_ADD of this query returns NULL on production, but in development is does exactly what I want it to(adds 90 minutes to the game_time column), The game_time column is a string that contains time in the following format: '21:00'.
This is the query:
SELECT TIME(game_time),
DATE_ADD(TIME(game_time),
INTERVAL 90 MINUTE),
TIME(NOW())
FROM games
What is going on? What am i doing wrong?
I know time should be in a TIMESTAMP, or TIME, but I'm working on someone elses code, I didn't start this from scratch myself.
I've also just noticed that TIME() returns different things, in development, TIME('21:00') returns 21:00:00.000000, in production I only get 21:00:00
Managed to get around, not pretty, but it works.
SEC_TO_TIME(TIME_TO_SEC(TIME(game_time))+5400)
You better develop with the same version as the production server:
Your old version will convert your TIME value to a date and because it's an invalid date, it will get NULL, see manual chapter Conversion Between Date and Time Types
Here's the relevant part:
Before 5.6.4, MySQL converts a time value to a date or date-and-time
value by parsing the string value of the time as a date or
date-and-time. This is unlikely to be useful. For example, '23:12:31'
interpreted as a date becomes '2023-12-31'. Time values not valid as
dates become '0000-00-00' or NULL.
Edit:
To get a TIME value with the desired result, you could use ADDTIME.
This could be working:
SELECT TIME(game_time),
ADDTIME (TIME(CONCAT(CURDATE(), ' ', game_time))),
'01:30:00'),
TIME(NOW())
FROM games
untested, because I have no such old MySQL version anymore.
Try moving the conversion to time outside the DATE_ADD:-
SELECT TIME(game_time), TIME(DATE_ADD(game_time, INTERVAL 90 MINUTE)), TIME(NOW())
FROM games
DATE_ADD works on a DATE or DATETIME field, and as it is you are passing it a TIME field.
I'm using JDBC to add some rows to my DB and they have a date aspect to it.
The GMT String for the date I'm adding in the commit is
11 Jan 2014 05:00:00 GMT
but when I get it back I'm getting this:
11 Jan 2014 04:00:00 GMT
I'm 1 hour ahead of my server and this might be part of the problem. I tried using datetime and timestamp but none of these solved my problem.
my JDBC query looks like this: (it's in Scala)
val statement = connection.prepareStatement("INSERT INTO `listings`(`track_id`, `date`, `position`) VALUES (?,?,?)");
statement.setInt(1, trackId);
statement.setDate(2,trackListing.date);
statement.setInt(3, trackListing.position);
statement.execute();
Is there any way to avoid this problem?
Turns out that by changing setDate and getDate to setTimestamp and getTimestamp I could fix it. I ended up just wrapping the timestamps to dates to maintain compatiblity, using:
new java.sql.Date(rs.getTimestamp("date").getTime());
Hope this helps someone else with the same problem in the future.
I experienced a problem importing dates into MySQL. I boiled it down to this ...
select from_unixtime(unix_timestamp(str_to_date('201201', '%Y%m')))
It reports...
2011-12-31 00:00:00
To make it return the original date, is there something I need to set up with MYSQL, or do I just fiddle it and add on one day or something?
I'm in the GMT time zone.
A search returned some very old bugs about this and other posts says it was how it is supposed to happen, but I didnt understand what you are supposed to do about it
On 5.5.21 (OS X) i get 2012-01-01 00:00:00.
Try upgrading your server.
When I run it, SELECT STR_TO_DATE('201201', '%Y%m') returns the invalid date 2012-01-00 (January 0th?!), so I'm not altogether surprised that round-tripping that through UNIX_TIMESTAMP() and FROM_UNIXTIME() ends up messing it up. Try adding a day to make it a real date (2012-01-01) first.
My server default timezone is +0400 (Moscow). All instance recording to DB in UTC format. IE: it user create item at 04-00 this is recorded as 00-00. When I'm trying to get
Item.last
I see the UTC raw time. When I'm asking
Item.last.created_at
I've get +0400 time. BUT! When I'm using std time functions like
Item.where('created_at > ?', Time.now.beginning_of_day)
it is making sql query
SELECT `items`.* FROM `items` WHERE (items.created_at > '2012-11-30 00:00:00')
instead of
SELECT `items`.* FROM `items` WHERE (items.created_at > '2012-11-29 20:00:00')
So i'm losing 4 hours. The little crunch is using in_time_zone:
Time.now.beginning_of_day.in_time_zone(-4)
But I's not a jedi way :D Any ideas?
I hope your users have some settings related to their time zones in their profiles. In this case you can do the following:
Time.use_zone(zone_of_current_user) do # like 'America/New_York'
#products = Product.where('created_at > ?', Time.zone.now.beginning_of_day)
end
This way you'll set the zone for the block.
Note the using of zone method of Time.
I think i've solved it:
Time.now.beginning_of_day.in_time_zone('UTC')
=> Thu, 29 Nov 2012 20:00:00 UTC +00:00
So this is relation what time in Moscow when day beginnings in UTC timezone :)