Why this datetime to timestamp convertion in PHP doesn't work? - php

I want to convert this format of datetime "31-12-2018 19:30 hs." from Argentina to an UTC timestamp, I am using the following code:
$clean_date = substr($date, 0, -4);
$dt = new DateTime($clean_date, new DateTimeZone('America/Argentina/Buenos_Aires'));
$dt->setTimeZone(new DateTimeZone('UTC'));
$timestamp = $dt->getTimestamp();
But it doesn't work, in the database the record is "0000-00-00 00:00:00", but if I echo the $dt, till there is working perfectly and showing the datetime in UTC.
Could someone please help me?
Thanks.

This has nothing to do with PHP. You're simply using the incorrect date literal format in MySQL. A per the docs:
MySQL recognizes DATETIME and TIMESTAMP values in these formats:
As a string in either 'YYYY-MM-DD HH:MM:SS' or 'YY-MM-DD HH:MM:SS' format. A “relaxed” syntax is permitted here, too: Any
punctuation character may be used as the delimiter between date parts
or time parts.
As a string with no delimiters in either 'YYYYMMDDHHMMSS' or 'YYMMDDHHMMSS' format, provided that the string makes sense as a
date.
As a number in either YYYYMMDDHHMMSS or YYMMDDHHMMSS format, provided that the number makes sense as a date.
1546335960 could be the last case but numbers don't make sense as date because year 1546 did not have 33 months.
To make it worse, many MySQL Servers are configured by default to let these kind of errors slip through:
mysql> CREATE TABLE test (
-> foo TIMESTAMP
-> );
Query OK, 0 rows affected (0.74 sec)
mysql> SET ##SESSION.sql_mode = '';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO test (foo) VALUES (1546335960);
Query OK, 1 row affected, 1 warning (0.39 sec)
mysql> SHOW WARNINGS;
+---------+------+------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------+
| Warning | 1265 | Data truncated for column 'foo' at row 1 |
+---------+------+------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM test;
+---------------------+
| foo |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (0.00 sec)
As you can see, you got a mere warning (that you need to read explicitly) and data corruption.
If you configure your app to use a strict mode you'll get a proper error message just in time:
mysql> SET ##SESSION.sql_mode = 'TRADITIONAL,NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO test (foo) VALUES (1546335960);
ERROR 1292 (22007): Incorrect datetime value: '1546335960' for column 'foo' at row 1
mysql>
Please note that timestamp is just a generic English word:
A digital record of the time of occurrence of a particular event.
It isn't necessarily synonym for Unix time.

Related

MariaDB/MySQL str_to_date format with timezone offset

Edit: TL;DR
# This query ---------------------------------------
SELECT STR_TO_DATE('2020-10-20T14:43:49+00:00', '%Y-%m-%dT%H:%i:%s') AS date;
# Results in----------------------------------------
+---------------------+
| date |
+---------------------+
| 2020-10-20 14:43:49 |
+---------------------+
# But throws----------------------------------------
+---------+------+-----------------------------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect datetime value: '2020-10-20T14:43:49+00:00' |
+---------+------+-----------------------------------------------------------------+
Why?
Detailed description
I have read through a lot of questions regarding similar issues, but could not find a definitive answer to this problem.
I'm running Doctrine migrations in a Symfony 5.2.x project on a MariaDB 10.2 database. I am trying to extract a date string from a JSON data column into its own column on the same table, but running into error messages when the original date string has a certain format.
ALTER TABLE form
ADD updated_at DATETIME DEFAULT NULL;
UPDATE form AS f
SET updated_at = STR_TO_DATE(
TRIM(BOTH '"' FROM (
SELECT JSON_EXTRACT(f.data, '$.updatedAt')
)),
'%Y-%m-%dT%H:%i:%s+00:00'
);
This works for any date string with a timezone offset of 0, like 2020-12-04T11:14:07+00:00. For obvious reasons, it fails for a non-zero offset like 2020-12-04T11:14:07+01:00, because
Literal characters in format must match literally in str.
-- https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_str-to-date
and results in an error
Warning | 1411 | Incorrect datetime value: '2020-12-04T11:14:07+01:00' for function str_to_date
However, if I understand the documentation correctly, I shouldn't even have to include the timezone offset in the format string:
Extra characters at the end of str are ignored.
But when I change the format string from '%Y-%m-%dT%H:%i:%s+00:00' to '%Y-%m-%dT%H:%i:%s', the update operation fails for all items, even though the dates are parsed correctly (or, at least, look correct):
MariaDB [db]> select STR_TO_DATE('2020-10-20T14:43:49+00:00', '%Y-%m-%dT%H:%i:%s') as date;
+---------------------+
| date |
+---------------------+
| 2020-10-20 14:43:49 |
+---------------------+
1 row in set, 1 warning (0.00 sec)
MariaDB [db]> show warnings;
+---------+------+-----------------------------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect datetime value: '2020-10-20T14:43:49+00:00' |
+---------+------+-----------------------------------------------------------------+
1 row in set (0.00 sec)
The Question
Apart from the historic bug in the application that would in some cases result in a non-UTC updated_at date, what am I doing wrong? As I understand it, anything in the string after the bit matching %s should be ignored by STR_TO_DATE() and irrelevant to the query. Why are my migrations failing when the DB clearly manages to parse the strings to something that looks like the datetime type it understands? How can I make sure it parses every item's date irrespective of its TZ offset (I wouldn't even mind if the result was updated_at times for some items with an hour's difference to the actual datetime)?
Edit
Because I don't fully understand what it does or what the implications are, I've tried changing sql_mode before executing my queries, but got the same results:
SET ##SQL_MODE = REPLACE(##SQL_MODE, 'NO_ZERO_IN_DATE', '');
SET ##SQL_MODE = REPLACE(##SQL_MODE, 'NO_ZERO_DATE', '');
Edit II
I ended up rewriting the migration and manually looping over each entry in PHP, re-setting the timezone and writing the corrected (UTC) value back to the DB.
This is obviously way more verbose and much slower than the SQL one-liner. The lack of answers (or even comments) here suggests I might have stumbled upon either a Maria/MySQL bug or faulty documentation.

difference of time MYSQL PHP

How can I get an update another field (whole column) the difference of time in (TIME 00:00:00) format in my MYSQL phpMyAdmin database
<?php
$con=mysqli_connect("***","****","****","******");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
mysqli_query($con,"UPDATE members SET time_on = current_time-logon_time");
?>
I keep getting 0:00:00 and no results when I do this in PHP. The other times our fine, just trying to get the difference and update all fields in the time_on column.
You should use timediff()
mysqli_query($con,"UPDATE members SET time_on = timediff(current_time,logon_time)");
You can use TIMESTAMPDIFF() to get the difference between two dates and/or datetimes.
Try this:
UPDATE members
SET time_on = TIMESTAMPDIFF(SECOND, logon_time, current_time)
This will set the difference in seconds.
Just to expand on why the other answers are right: You're doing MATH on times, which doesn't work as you expect:
mysql> create table foo (x time, y time);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into foo (x, y) values ('14:15:16', '09:08:07');
Query OK, 1 row affected (0.00 sec)
mysql> select * from foo;
+----------+----------+
| x | y |
+----------+----------+
| 14:15:16 | 09:08:07 |
+----------+----------+
1 row in set (0.00 sec)
mysql> select x - y from foo;
+-------+
| x - y |
+-------+
| 50709 |
+-------+
1 row in set (0.00 sec)
Note how the result is NOT a time value, it's an integer. It happens to be a direct time->string->numeric conversion of 05:07:09, which is the literal as-expected time difference of the two values. But since you're doing MATH, i.e. subtraction, MySQL is assuming you want a numeric answer, and giving it to you.
That's why there's timediff() and similar date/time functions, which return a real date/datetime value, not this wonky/useless integer.

Is it better to have a time stamp through mySQL or php?

I'm starting to design a database and before too many records get inputted, I want to think into the future and collect as much data as possible. I think it would be good for me to know when the record was added. Based on your experience, is it better to do this through mySQL via datetime or through php via the date function. I'll be using php to input all the values, so it would simply be another field.
So far, I like the php approach because I can customize it to take up minimal space: yymmddhhmm & time zone.
Based on your experience, what is the best way to store this data or are the two ways indifferent?
Also, what time zone would you suggest using? The time zone where I am located or GMT? Is it best to use GMT if say I were to move later on or if individuals from multiple timezones administered the database.
Store it as DATETIME/TIMESTAMP in MySQL, it is stored as an integer anyway, just goes in and comes out as a timestamp. Store the data in UTC.
You can manipulate the timestamp in PHP by constructing it with DateTime() and then going from there.
This also allows you to put a NOT NULL DEFAULT CURRENT_TIMESTAMP on the column, which saves you actively having to build it in php.
For simplicity, I suggest using MySQL's timestamp field. Because the database then understands what it is, it's stored much more efficiently than your text version (as a number, rather than a string of characters), and you can do more with it.
For example:
mysql> CREATE TABLE foo (something TEXT NOT NULL, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);
Query OK, 0 rows affected (0.11 sec)
mysql> INSERT INTO foo (something) VALUES ("one");
Query OK, 1 row affected (0.07 sec)
mysql> INSERT INTO foo (something) VALUES ("two");
Query OK, 1 row affected (0.13 sec)
mysql> SELECT * FROM foo;
+-----------+---------------------+
| something | created |
+-----------+---------------------+
| one | 2013-09-18 22:57:01 |
| two | 2013-09-18 22:57:03 |
+-----------+---------------------+
2 rows in set (0.00 sec)
mysql> SELECT something, NOW() - created as seconds_since_insert FROM foo;
+-----------+----------------------+
| something | seconds_since_insert |
+-----------+----------------------+
| one | 136 |
| two | 134 |
+-----------+----------------------+
2 rows in set (0.00 sec)

mySQL TIME function and Timezone

I want to use the NOW() function with a specific timezone, say GMT+8 for a user and GMT-2 for another user. How can I achieve this?
I am guessing that the time for NOW() is related somewhat to the timezone and time of the SQL server, but I want it to be such that FN(GMT+8) always give me the NOW() in GMT+8 irregardless of the timezone the SQL server is in.
Mini question: How do i display/know the current time of the SQL server?
The NOW() function provides the current time in the local timezone of the server. If you wish to convert to a different timezone, you can use CONVERT_TZ()
UPDATE:
You can use a per-connection timezone (that doesn't affect the system timezone) and get the effect you want:
mysql> select now();
+---------------------+
| now() |
+---------------------+
| 2011-06-03 22:40:51 |
+---------------------+
1 row in set (0.00 sec)
mysql> SET time_zone = '+08:00';
Query OK, 0 rows affected (0.00 sec)
mysql> select now();
+---------------------+
| now() |
+---------------------+
| 2011-06-04 10:41:15 |
+---------------------+
1 row in set (0.00 sec)

Change MySQL time zone without super permissions

I added this line to my config.inc.php file.
$query = "SET SESSION time_zone = 'Europe/Rome'";
if (mysql_query($query, DB_LINK) == FALSE) {
die(mysql_error(DB_LINK));
}
It doesn't give me any error, but when I use the NOW() or the CURRENT_TIMESTAMP() function it saves the record with a wrong time.
How can I set the date time zone in MySQL without the super permissions?
Assuming you are using 5.5, if you see http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html, it says:
mysql> SET time_zone = timezone;
It also says:
The current session time zone setting affects display and storage of time values that are zone-sensitive. This includes the values displayed by functions such as NOW() or CURTIME(), and values stored in and retrieved from TIMESTAMP columns. Values for TIMESTAMP columns are converted from the current time zone to UTC for storage, and from UTC to the current time zone for retrieval.
The current time zone setting does not affect values displayed by functions such as UTC_TIMESTAMP() or values in DATE, TIME, or DATETIME columns. Nor are values in those data types stored in UTC; the time zone applies for them only when converting from TIMESTAMP values. If you want locale-specific arithmetic for DATE, TIME, or DATETIME values, convert them to UTC, perform the arithmetic, and then convert back.
So try it without the SESSION and see if it works, and also check that select ##session.time_zone;
gives you the right timezone.
EDIT: you may just have an issue with your database. I just tried this on one of my databases (5.5.8) and it worked, but it failed on 5.0.51. So you may need a db upgrade.
mysql> select CURRENT_TIMESTAMP();
+---------------------+
| CURRENT_TIMESTAMP() |
+---------------------+
| 2011-05-29 14:33:06 |
+---------------------+
1 row in set (0.00 sec)
mysql> set time_zone = 'Europe/Rome';
Query OK, 0 rows affected (0.00 sec)
mysql> select CURRENT_TIMESTAMP();
+---------------------+
| CURRENT_TIMESTAMP() |
+---------------------+
| 2011-05-29 16:33:11 |
+---------------------+
1 row in set (0.00 sec)
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.5.8-log |
+-----------+
1 row in set (0.00 sec)
Erm I'm not sure how to set time zone with mysql...
but i prefer to set the time zone with php...
date_default_timezone_set('Europe/Rome');

Categories