0

I am working on xampp when a user takes some action then a record is inserted in the database

$creation_datetime = new DateTime($row0['creation_date_time']);

output of the above statement where creation_date_time is of type timestamp in the database and it fills automatically when a record is inserted.

DateTime Object ( [date] => 2020-07-25 15:31:46.000000 [timezone_type] => 3 [timezone] => Europe/Berlin )

I see that the time displayed in the database actually matches time in my country so it seems local time but the server timezone is different.

print_r(new DateTime());

Output of the above statement

DateTime Object ( [date] => 2020-07-25 12:40:33.451120 [timezone_type] => 3 [timezone] => Europe/Berlin )

In both cases the record coming from database and date function the timezone is europe/berlin but the DateTime function displays the correct date based on the server time.

(Bot these outputs were recorded just after the creation of the record the time there is hardly any time difference )

Why is this happening ? I want to calculate expiration from created time and new DateTime which is the current time.

Should I not use timestamp and insert the creation date based on new DateTime(); function which seems to output the correct date time based on server.

Whats the most reliable way to go about it ?

nloo
  • 13
  • 3

1 Answers1

1

If the column creation_date_time is a TIMESTAMP, the value is internally stored Universal Coordinated Time (UTC) but when it is retrieved it will be converted to the current session time zone, which will be the server's default time zone unless you have set the session time zone to be something different. When you assign this value, $row0['creation_date_time'] to a PHP DateTime without explicitly setting a time zone, it will use UTC.

So to be consistent and assuming you want to use a specific time zone, you should explicitly set a session time zone for your MySql connection. So a person in New York might do the following:

set session time_zone = 'America/New_York'; /* MySql */

And in PHP:

$date = new DateTime($row0['creation_date_time'], new DateTimeZone('America/New_York'));

Alternatively, if you do not want to create a DateTime with anything other than UTC, then you should at least set your MySql session time zone to UTC prior to retrieving the TIMESTAMP. You will then have consistent time zones but not necessarily the displayable value you want.

For example, if an event occurred on 7/1/20 at 5am in New York and a timestamp of this was kept in a TIMESTAMP column, internally it would be stored as '2020-07-01 01:00:00' because there is a 4 hour difference. This will be independent of the current session time zone in effect when the event occurred assuming that the value of NOW() or CURRENT_TIMESTAMP() was used to store the timestamp (if, however, you stored a literal into a TIMESTAMP column, it would be interpreted in the current session's time zone before being converted to UTC). When I retrieve the value, I would like it, however, to reflect "local" time. So, I need to set the session time zone to America/New_York prior to fetching the column if I wish for the retrieved value to be '2020-07-01 05:00:00'. If I assign this to a PHP DateTime without setting the appropriate time zone, it will display as 5am but with the UTC time zone, and that is not really correct.

I am not sure if all of the above really answered your question, so let me tell you the approach I would take in general not having knowledge or your actual circumstances:

I would not attempt to update the timestamp explicitly using a PHP initialized variable but would instead allow MySql to initialize on creation and/or updating depending on your requirements:

UPDATE my_table set creation_date_time = now(), etc.

The issue is that when you set a TIMESTAMP column with a literal value, it will be interpreted in whatever the current session time zone is and without setting it explicitly, this becomes non-deterministic unless you have control over the system time zone (applications also migrate to different ISPs and therefore time zones, at least in my country). Even though you will be setting the session time zone explicitly if you follow my advice, it is far simpler to just let MySql do the initialization.

I would then ensure that the first thing you do after connecting is to set the local session timezone:

set session_time_zone = 'Europe/Berlin';

And then your PHP code is:

$date1 = new DateTime($row0['creation_date_time'], new DateTimeZone('Europe/Berlin'));
$date1 = $date1->modify("+1 month");
$date2 = new DateTime("now", new DateTimeZone('Europe/Berlin'));
if ($date1 <= date2) {
    // expired
}

UPDATE

If the current MySql server time were 'Europe/Berlin', then when you did a SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP) query, I would expect a result of '02:00' not '05:30' that you did get, as evidenced by the following:

mysql> set session time_zone = 'Europe/Berlin';
Query OK, 0 rows affected (0.19 sec)

mysql> SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);
+--------------------------------+
| TIMEDIFF(NOW(), UTC_TIMESTAMP) |
+--------------------------------+
| 02:00:00                       |
+--------------------------------+
1 row in set (0.00 sec)

So it appears that whatever the MySql server time is, it is 03:30 hours east of Berlin. Now the timestamps you are comparing are:

From MySql:

DateTime Object ( [date] => 2020-07-25 15:31:46.000000 [timezone_type] => 3 [timezone] => Europe/Berlin )

From PHP:

DateTime Object ( [date] => 2020-07-25 12:40:33.451120 [timezone_type] => 3 [timezone] => Europe/Berlin )

Now it's a little difficult to compare these two precisely. If you created the PHP timestamp simultaneously with the MySql timestamp and the MySql timestamp was created using a MySql function such as NOW() (in which case the TIMESTAMP would be internally correct), then I would expect the MySql timestamp to be 03:30 later than the PHP timestamp (because of the timezone in effect when the TIMESTAMP is being converted back from UTC). But if you actually created the PHP timestamp about 40 minutes later, then what you show is more or less what I would expect to see. You tell me if 40 minutes is closer to the elapsed time between the two events. Assuming it is, then this also tells me that you have been initializing the TIMESTAMP column with either NOW() or its equivalent CURRENT_TIMESTAMP, which would be a very good thing because internally the timestamp would be correct and your only problem is when you retrieve it you are getting it converted to the wrong timezone. But this can be remedied by setting the session timezone immediate following connection:

set session time_zone = 'Europe/Berlin'

Update 2

If you cannot set the above timezone, you are missing timezones from your MySql installation and you need to load them (the reason you see that timezone on your PHP size is because PHP has the timezone and you are explicitly loading it into your DateTime object).

URL https://dev.mysql.com/downloads/timezones.html has files of timezones. Pick the POSIX version appropriate for your MySql release and download it to your PC.

URL https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html has the instructions for loading this file. Read this and make sure you understand what you are doing (somebody has to take responsibility). In short, you will be executing:

mysql_tzinfo_to_sql path-to-downloaded-timezones-file | mysql -u admin-user-id -p admin-password

After all of this, if you still feel like doing some light reading, see What difference between the DATE, TIME, DATETIME, and TIMESTAMP Types, specifically the answer given by me (Booboo).

Booboo
  • 38,656
  • 3
  • 37
  • 60
  • Weird the new DateTimeZone doesnt change anything the date time from mysql is still outputted in my localtime zone. May be the problem is my xampp installation. DateTime Object ( [date] => 2020-08-25 18:17:07.000000 [timezone_type] => 3 [timezone] => Europe/Berlin ) – nloo Jul 25 '20 at 12:52
  • You have been storing `creation_date_time` values as string literals such as `2020-07-01 14:22:31`, which may have been Berlin time, correct? If the server's time is not Berlin time, then the timestamp will be converted to the wrong UTC time. Assuming when you retrieve the column back and assuming the same server timezone is in effect, it will be converted back to the same string literal you started with but of course interpreted with the server's local time zone. When you construct a DateTime with this string literal in PHP, you are forcing a Berlin time zone on the DateTime, so it looks OK. – Booboo Jul 25 '20 at 13:40
  • (continued) So as long as you are doing date comparisons exclusively in PHP you might be OK. But the TIMESTAMP value is fundamentally wrong. If you were using MySql to test for expiration by using DATE_ADD on creation_date_time and comparing the results with NOW(), you would get erroneous results. All of this is assuming, that you created the date literal using Berlin time and your server was using something other than Berlin time. My proposal assumes creation_date_time has been correctly initialized, which is probably not the case if the server time zone did not match the PHP time zone. – Booboo Jul 25 '20 at 13:45
  • (continued) Assuming I have understood everything, I can tell you how to correct the current creation_date_time columns so that (1) they store the correct UTC time and (2) return the correct expected string value when you have set 'Europe/Berlin' as the session timezone. – Booboo Jul 25 '20 at 14:07
  • Server time is berlin time when you do echo new DateTime(); without any timezone it outputs time in berlin time. But when u extract timestamp from database then it doesnt convert to the server time zone it stays the same mo matter what you do. – nloo Jul 25 '20 at 14:15
  • I meant your MySql server, which could be using a different timezone.You must have `date.timezone = 'Europe/Berlin'` set in your php.ini file. To get a sense of your MySql session time zone, open up a MySql console and try the following commands: (1) `SELECT @@system_time_zone;` (2) `SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);` (3) `SELECT NOW();` (1) might give you an actual name, (2) will tell you how what the difference from UTC your timezone is (should be two hours, I believe if your current timezone were Berlin) and (3) will tell you what the current date and time is in the current time zone. – Booboo Jul 25 '20 at 15:10
  • @@system_time_zone `unknown` TIMEDIFF(NOW(), UTC_TIMESTAMP) `05:30:00` in php.ini its set to berlin time I dont know why it is my local time. Time in database matches exactly my country local time. – nloo Jul 25 '20 at 16:50
  • php.ini `; Local Variables: ; tab-width: 4 ; End: [Syslog] define_syslog_variables=Off [Session] define_syslog_variables=Off [Date] date.timezone=Europe/Berlin [MySQL] mysql.allow_local_infile=On mysql.allow_persistent=On` – nloo Jul 25 '20 at 16:52
  • Where do i set session time_zone = 'Europe/Berlin'; in php file it gives error and in phpmy admin it shows Unknown or incorrect time zone: 'Europe/Berlin' Thanks..Yes it doesnt correctly convert the timezone even though in the object it shows the timezone is set to Berlin. – nloo Jul 25 '20 at 19:14
  • Your `php.ini` already has `date.timezone=Europe/Berlin` so nothing to do on PHP side. When you do a connect to your MySql server using either PDO or mysqli interface, you do a query with the returned connection object and the query string is `"set session time_zone = 'Europe/Berlin'"`. If 'Europe/Berlin' is an invalid timezone you have a second problem, namely not all of the time zones are loaded on your system. I will get back to you or you can Google loading additional MySql time zones. Start here: https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html – Booboo Jul 25 '20 at 19:34