Everywhere I read about converting time to a user's timezone says that the best method is to store a date and time in UTC then just add the user's timezone offset to this time.
How can I store a date in UTC time? I use the MySQL DATETIME field.
When adding a new record to MySQL in my PHP code I would use now() to insert into MySQL DATETIME.
Would I need to use something different than now() to store UTC time?
MySQL: UTC_TIMESTAMP()
Returns the current UTC date and time
as a value in 'YYYY-MM-DD HH:MM:SS' or
YYYYMMDDHHMMSS.uuuuuu format,
depending on whether the function is
used in a string or numeric context
PHP: gmdate()
Also PHP date_default_timezone_set() is used in PHP to set the current time zone for the script. You can set it to the client time zone so all the formatting functions return the time in his local time.
In truth though I had a hard time getting this to work and always stumble into some gotcha. Eg. time information returned from MySQL is not formatted as 'UTC' so strtotime transforms it into a local time if you are not careful. I'm curious to hear if someone has a reliable solution for this problem, one that doesn't break when dates traverse media boundaries (HTTP->PHP->MySQL and MySQL->PHP->HTTP), also considering XML and RSS/Atom.
I would suggest inserting the date in UTC time zone. This will save you a lot of headaches in the future with daylight saving problems.
INSERT INTO abc_table (registrationtime) VALUES (UTC_TIMESTAMP())
When I query my data I use the following PHP script:
<?php
while($row = mysql_fetch_array($registration)) {
$dt_obj = new DateTime($row['message_sent_timestamp'] ." UTC");
$dt_obj->setTimezone(new DateTimeZone('Europe/Istanbul'));
echo $formatted_date_long=date_format($dt_obj, 'Y-m-d H:i:s');
}
?>
You can replace the DateTimeZone value with one of the available PHP timezones.
NOW() gives you the time (including the timezone offset) of the system running your database. To get UTC date/time you should use UTC_TIMESTAMP() as described in the MySQL Reference Manual.
https://dba.stackexchange.com/questions/20217/mysql-set-utc-time-as-default-timestamp
Quoting all the answer from above link in case of delete:
To go along with #ypercube's comment that CURRENT_TIMESTAMP is stored as UTC but retrieved as the current timezone, you can affect your server's timezone setting with the --default_time_zone option for retrieval. This allows your retrieval to always be in UTC.
By default, the option is 'SYSTEM' which is how your system time zone is set (which may or may not be UTC!):
mysql> SELECT ##global.time_zone, ##session.time_zone;
+--------------------+---------------------+
| ##global.time_zone | ##session.time_zone |
+--------------------+---------------------+
| SYSTEM | SYSTEM |
+--------------------+---------------------+
1 row in set (0.00 sec)
mysql> SELECT CURRENT_TIMESTAMP();
+---------------------+
| CURRENT_TIMESTAMP() |
+---------------------+
| 2012-09-25 16:28:45 |
+---------------------+
1 row in set (0.00 sec)
You can set this dynamically:
mysql> SET ##session.time_zone='+00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT ##global.time_zone, ##session.time_zone;
+--------------------+---------------------+
| ##global.time_zone | ##session.time_zone |
+--------------------+---------------------+
| SYSTEM | +00:00 |
+--------------------+---------------------+
1 row in set (0.00 sec)
Or permanently in your my.cnf:
[mysqld]
**other variables**
default_time_zone='+00:00'
Restart your server, and you will see the change:
mysql> SELECT ##global.time_zone, ##session.time_zone;
+--------------------+---------------------+
| ##global.time_zone | ##session.time_zone |
+--------------------+---------------------+
| +00:00 | +00:00 |
+--------------------+---------------------+
1 row in set (0.00 sec)
mysql> SELECT CURRENT_TIMESTAMP();
+---------------------+
| CURRENT_TIMESTAMP() |
+---------------------+
| 2012-09-25 20:27:50 |
+---------------------+
1 row in set (0.01 sec)
As #Haluk suggests, you can store the date as a UTC datetime .
I'm complementing his answer for situations when the date you want to insert comes from PHP code, and adding some details about how it works :
$pdo = new PDO('mysql:host=mydbname.mysql.db;dbname=mydbname', 'myusername', 'mypassword');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$insertStmt = $pdo->prepare("insert into test (last_modified)"
." values(:last_modified)");
$insertStmt->bindParam(':last_modified', $lastModified, PDO::PARAM_STR);
$lastModified = gmdate("Y-m-d H:i:s");
$insertStmt->execute();
Actually datetime in PHP doesn't have a timezone associated to it. What is passed to PDO is just a string, in one of the formats recognized by MySQL, representing the date in UTC timezone. For example if the date is 2015-10-21 19:32:33 UTC+2:00, the code above just tells MySQL to insert 2015-10-21 17:32:33. gmtdate("Y-m-d H:i:s") gives you the current date in such a format. In any case, all you need to do is build the string representing the date you want to insert in UTC time.
Then, when you read the date, you have to tell PHP that the timezone of the date you just retrieved is UTC. Use the code in Haluk's answer for this.
Random: UTC_TIMESTAMP(6) for time with 6 digits of microseconds.
MySQL 5.7+
As per (https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html) set the timezone upon connection:
SET TIME_ZONE = 'UTC';
After that all datetime functions should be in UTC.
Refrain from using TIMESTAMP() it is not Y2038 save.
Related
I'm in the early stage of building a mysql table that will hold data from two timezones. Both my machine and the server are in the America/Los_Angeles time zone. The timezone table is not loaded in the mysql server, but elsewhere on this site, I've read this is not necessary as long as php handles the queries - php will write the UTC offset to mysql. Mysql datatype is TIMESTAMP. Sample data has been inserted with a php script that includes the following statement:
if($company == 'HOS_CIN' or $company == 'HOS_FER') {
date_default_timezone_set("America/New_York");
}
Then two php scripts were used to display the data in a browser. One included the above statement and one did not. The one with the statement displayed the time as noon EST, and the one without displayed the time as noon PST. If mysql had stored the UTC offset, shouldn't there have been a three-hour difference in the times displayed?
Php version 5.3.3, mysql version 5.0.95
You have to face the following limits regarding PHP and MySQL:
MySQL does not have any column type that allows to store a local time with time zone information.
PHP cannot pass complex data types (such as DateTime instances) to MySQL, everything needs to be stringified, and MySQL doesn't have a syntax to pass a date literal with time zone information.
In practice it isn't as bad as it may seem because you don't normally need the local time of the user who inserted the information: you just need to know the exact moment in time the stored date refers to and (optionally) the time zone in which the stored date has to be displayed. And there're basically three sensible ways to do so:
Use a format that's unaffected by time zones, e.g. a Unix timestamp stored as INT.
Use a date column type with explicit time zone information, e.g. a TIMESTAMP column (not to be confused with Unix timestamps) where time zone is always UTC.
Use a date column type with implicit time zone information, e.g. a DATE or DATETIME column where you have decided that all dates belong to a given time zone, possibly UTC.
The difference between #2 and #3 is whether MySQL is aware of the time zone or it's something only you and your code know.
Whatever approach you chose, it's necessary that all the programs that are expected to do time zone conversions are instructed about what time zone to use:
PHP needs the time zone to generate/display Unix timestamps and to convert from/to the local time typed/expected by user and the one expected/printed by MySQL, e.g.:
$user_date = new DateTime('2016-10-03 00:00:00', new DateTimeZone('Europe/Berlin'));
$user_date->setTimezone(new DateTimeZone('UTC'));
echo $user_date->format('c');
MySQL needs the time zone when you use TIMESTAMP columns, to convert from/to the local time, e.g.:
mysql> create table foo(
-> foo int(10) unsigned auto_increment,
-> my_date timestamp,
-> primary key (foo)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> set ##time_zone = '+01:00';
Query OK, 0 rows affected (0.00 sec)
mysql> insert into foo (my_date) values ('2016-12-27 18:50:00');
Query OK, 1 row affected (0.00 sec)
mysql> set ##time_zone = '-07:00';
Query OK, 0 rows affected (0.00 sec)
mysql> insert into foo (my_date) values ('2016-12-27 18:50:00');
Query OK, 1 row affected (0.00 sec)
mysql> select * from foo order by 1;
+-----+---------------------+
| foo | my_date |
+-----+---------------------+
| 1 | 2016-12-27 10:50:00 |
| 2 | 2016-12-27 18:50:00 |
+-----+---------------------+
2 rows in set (0.00 sec)
mysql> set ##time_zone = '+09:30';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from foo order by 1;
+-----+---------------------+
| foo | my_date |
+-----+---------------------+
| 1 | 2016-12-28 03:20:00 |
| 2 | 2016-12-28 11:20:00 |
+-----+---------------------+
2 rows in set (0.00 sec)
I'm using Godaddy's MySQL database. Since their timezone is MST UTC -7, I needed to modify my code. I figured out how to do it when using NOW() function. However Im struggling while converting result of CURDATE() to my local date. Topics in the website didnt help it. I dont have privilege to change timezone of mysql since it is shared host. The problem about CURDATE() is, since there is 10 hours difference between server and my country, dates will be different at somepoint.
What I have tried so far
First attempt
SELECT convert_tz(CURDATE(),'-07:00','+03:00')
this query returns following output in the mysql.
convert_tz(CURDATE(),'-07:00','+03:00')
2016-05-14 10:00:00
I didnt try yet since still dates are the same but this code probably done the work. But the problem is about the time comes after date.CURDATE should return only date. I think it returns the differences between two timezones which equals to 10 hours but I think it is gonna cause problem when Im comparing 2 dates.
Second attempt
SELECT convert_tz(CURDATE(),'MST','EEST');
Since server's timezone is MST and my timezone is EEST, I tried in this way but it returns NULL.
The question is what should I do to just return date without that 10:00:00 there. or is there any better way?
There are a couple of problems with your approach.
First, CURDATE() returns a date, not a datetime.
Second, you need to convert the current date and time from the server's time zone to your time zone before truncating the time portion. This means using NOW() inside, not CURDATE().
Third, you need to use the correct abbreviations for the correct time zones for both sides of the conversion, for the entire year. CONVERT_TZ() will return NULL if either time zone is unrecognized.
In this case, MST/MDT is called "MST7MDT" and EET/EEST is called "EET" in the MySQL time zone tables. It's surprising that Go Daddy doesn't set their server clocks to UTC -- that's sort of a de facto standard for server clocks, but assuming not, "MST7MDT" is probably the most correct value.
mysql> SELECT DATE(CONVERT_TZ(NOW(),'MST7MDT','EET'));
+-----------------------------------------+
| DATE(CONVERT_TZ(NOW(),'MST7MDT','EET')) |
+-----------------------------------------+
| 2016-05-14 |
+-----------------------------------------+
1 row in set (0.00 sec)
Or, you could use the more intuitive localized names for the time zones. I believe these values would also be correct, an would accommodate summer and time changes correctly:
mysql> SELECT DATE(CONVERT_TZ(NOW(),'America/Denver','Europe/Bucharest'));
+-------------------------------------------------------------+
| DATE(CONVERT_TZ(NOW(),'America/Denver','Europe/Bucharest')) |
+-------------------------------------------------------------+
| 2016-05-14 |
+-------------------------------------------------------------+
1 row in set (0.00 sec)
Try to convert you date into string using CAST, and then get a substring.
SELECT SUBSTRING_INDEX( CAST(CONVERT_TZ(CURDATE(),'-07:00','+03:00') AS char), ':', -1)
Should return
2016-05-14 10:00
I am building a PHP to MySQL webpage and I didn't notice it until now. My date from form field which is posted to PHP script is sent to MySQL table for storage. Even though in the date field within the form is being displayed correctly and POST date value is verified to be exact, it is not going into my MySQL table correctly. For some odd reason, the year is 2006 not 2012. However, the time is correct.
Here is my Form date field:
<input type=text id="date" name="srdate" value=<?php echo date("m/d/y"); ?>
Date Column in my MySQL table is of TYPE DATE.
I did check the date stored in my table and it has wrong date not year 2012 but 2006.
Any Idea,
MySQL's only acceptable date input format is YYYY-MM-DD HH:MM:SS. The date you're generating in PHP will look like (for today) 06/12/12 which MySQL will try to parse, but see as 12th December, 2006, and not the actual June 12/2012.
As well, note that your code is gaping wide open for SQL injection attacks. Read up and learn how to prevent those before you go ANY FARTHER with this code. Otherwise your server is going to get pwn3d.
You need to use the yyyy-mm-dd syntax.
While I'd expect MySQL to throw an error when trying to insert 06/12/12 it apparently parses it as 2006-12-12:
mysql> SELECT cast('06/12/12' as date);
+--------------------------+
| cast('06/12/12' as date) |
+--------------------------+
| 2006-12-12 |
+--------------------------+
1 row in set (0.00 sec)
mysql> SELECT cast('2012-06-12' as date);
+----------------------------+
| cast('2012-06-12' as date) |
+----------------------------+
| 2012-06-12 |
+----------------------------+
1 row in set (0.00 sec)
From the manual:
Although MySQL tries to interpret values in several formats, date parts must always be given in year-month-day order (for example, '98-09-04'), rather than in the month-day-year or day-month-year orders commonly used elsewhere (for example, '09-04-98', '04-09-98').
I allow my users to add events to my webapp. Users can originate from all around the world.
My current idea is to determine the user's time zone automatically (using an IP to location API) and insert it into the users table (I will also allow them to change it).
On the events table I will insert the event start date/end date in UTC.
Then, whenever I need to display the event info, I would take the event start date/end date from the table and do the calculation against the user's timezone.
Is that considered as good practice or is there a better way to do that?
Anything I should be aware of when doing this?
Thanks,
Yes, that is indeed a good way. Also keep in mind that if you use the TIMESTAMP type in MySQL, MySQL handles the timezone converting for you. When you insert new dates/times to the database, MySQL converts it from the connection's timezone to UTC (TIMESTAMP is always stored in UTC). When you retrieve a TIMESTAMP field from database, MySQL converts it back to the connection's timezone.
So if you use TIMESTAMP fields in MySQL, all you need to do is tell the user's timezone to MySQL at start of each your page. You do so by:
SET time_zone = 'Europe/Helsinki'
You can also use numeric timezones:
SET time_zone = '+02:00'
Keep in mind that you might need to install the tzinfo to MySQL first, which is trivial though (only for the non-numeric version though). Here's information about how to do it: http://dev.mysql.com/doc/refman/5.0/en/mysql-tzinfo-to-sql.html
In a nutshell, this is the important part:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
Here's an example of how it works:
mysql> CREATE TEMPORARY TABLE test(foo TIMESTAMP);
Query OK, 0 rows affected (0.00 sec)
mysql> SET time_zone = '+00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO test VALUES ('2011-02-03 16:00:00');
Query OK, 1 row affected (0.00 sec)
mysql> SET time_zone = '+02:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT foo FROM test;
+---------------------+
| foo |
+---------------------+
| 2011-02-03 18:00:00 |
+---------------------+
1 row in set (0.00 sec)
If you don't use TIMESTAMP field, eg. you use DATETIME (which also supports wider range of dates), then you just need to make sure you always insert dates in UTC; I do this by always setting connection's timezone to +00:00. Then you can have a view helper in PHP that converts the datetime to the user's timezone, which is quite trivial to do with PHP's DateTime class and setTimezone function. There's an example in the last link. To use this method, you must make sure PHP is also set to use UTC as its default timezone, which you can do with this:
date_default_timezone_set('UTC');
Whichever method you use, you should always be aware of these facts:
The PHP and MySQL connection's timezones should always be set to the same value so they're consistent with each other
Generally it's a bad idea to mix TIMESTAMP and DATETIME types with each other
If you use TIMESTAMP type, set the timezone to the user's timezone
If you use DATETIME type, set the timezone to UTC and handle timezone convertions in PHP
Few thing I would consider:
Provide the users the means to set the time zone manually.
Assuming your users may change their location, let them specify the time zone for the event they add (or the location if you can get the time zome from that).
This way hopefully they won't miss anything :) Good luck with your webapp!
I have a date that looks like 1003029303, which I guess is what's known as a linux UNIX time stamp.
What format should I save it as in a mysql database? I don't suppose that an int(10) is the right way.
`gottime` int(10)
I take it you are trying to save Birthdays or something similar. DATETIME format is the proper way to store past dates. A unix timestamp should only be used for current day items that do not exceed 30-40 +/- years.
The MySQL DATETIME format is YYYY-MM-DD HH:MM:SS.
I suggest using the DATETIME data type, and then use the FROM_UNIXTIME() and UNIX_TIMESTAMP() functions to convert as required:
SELECT FROM_UNIXTIME(1003029303);
+---------------------------+
| FROM_UNIXTIME(1003029303) |
+---------------------------+
| 2001-10-14 05:15:03 |
+---------------------------+
1 row in set (0.08 sec)
SELECT UNIX_TIMESTAMP('2001-10-14 05:15:03');
+---------------------------------------+
| UNIX_TIMESTAMP('2001-10-14 05:15:03') |
+---------------------------------------+
| 1003029303 |
+---------------------------------------+
1 row in set (0.00 sec)
You shouldn't store UNIX timestamps in your database. Mainly because MySQL (amongst other brands) databases includes DATE functions to calculate most any type of DATE math you can think of. Whereas if you store it as a timestamp, you would have to either, convert the timestamp within mysql first and then format it OR use PHP's date() function.
Timestamps are simply integers. You could store it like that.
You could also use MySQL's TIMESTAMP data type with the FROM_UNIXTIME() and UNIX_TIMESTAMP() functions to convert to and from MySQL's format.
$query = "UPDATE table SET
datetimefield = FROM_UNIXTIME($phpdate)
WHERE...";
$query = "SELECT UNIX_TIMESTAMP(datetimefield)
FROM table WHERE...";
Personally, I store dates in MySQL databases using the DATETIME field type. For example, 2010-07-27 09:30:09.
It's far easier to read than a time stamp, and if you need to convert it to a UNIX timestamp you can do so with PHP's strtotime() function.