PHP Converting milliseconds to date fails for specific millisecond (1425318722000) - php

So I'm trying to convert milliseconds to date in PHP and I thought the script I had was working fine but getting odd behaviour for a specific millisecond value (1425318722000).
I've checked this across a few websites and all come back with a valid value...
Monday, March 2, 2015 5:52:02 PM GMT
Mon Mar 02 2015 17:52:02
Mon Mar 02 2015 17:52:02 GMT+0000 (GMT)
Mon, 02 Mar 2015 17:52:02 GMT
Any idea why this occurs, is it a PHP bug perhaps?
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318721999/1000));"
object(DateTime)#1 (3) {
["date"]=>
string(19) "2015-03-02 17:52:01"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318722000/1000));"
bool(false)
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318722001/1000));"
object(DateTime)#1 (3) {
["date"]=>
string(19) "2015-03-02 17:52:02"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318722002/1000));"
object(DateTime)#1 (3) {
["date"]=>
string(19) "2015-03-02 17:52:02"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318722003/1000));"
object(DateTime)#1 (3) {
["date"]=>
string(19) "2015-03-02 17:52:02"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318722004/1000));"
object(DateTime)#1 (3) {
["date"]=>
string(19) "2015-03-02 17:52:02"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
php -r "var_dump(DateTime::createFromFormat('U.u', 1425318722005/1000));"
object(DateTime)#1 (3) {
["date"]=>
string(19) "2015-03-02 17:52:02"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}

It's the fact that you're using the 'U.u' mask, but the .u is lost from the value when the result is all 0s after the decimal for that division
for($ms = 1425318721999; $ms <= 1425318722001; ++$ms) {
var_dump(DateTime::createFromFormat('U.u', sprintf('%14.3f', $ms/1000)));
}
Will work, because you're using sprintf() to force those zeroes to be retained after the decimal point
for($ms = 1425318721999; $ms <= 1425318722001; ++$ms) {
var_dump(DateTime::createFromFormat('U', floor($ms/1000)));
}
would also work, but you'd lose the milliseconds precision
By way of some explanation:
public static DateTime DateTime::createFromFormat ( string $format , string $time [, DateTimeZone $timezone ] )
the createFromFormat() expects a string as the second argument, so PHP is loose casting the result of your division to a string, and a float value like 1425318722.000 will be cast to a string of "1425318722" with no decimal point or following zeroes, so it doesn't conform with the U.u mask which requires the decimal point and following digits

Related

How to get current DATE/TIME with microseconds in CI4?

Awkward problem -> I'm trying to get current date&time in ISO 8601 format (like this: 2022-02-11 12:30:02.846108).
When I run $time = new Time('now', 'Europe/Amsterdam') in CI4, $time returns this:
object(CodeIgniter\I18n\Time)#79 (6)
{ ["timezone":protected]=> object(DateTimeZone)#80 (2)
{ ["timezone_type"]=> int(3)
["timezone"]=> string(16) "Europe/Amsterdam"
}
["locale":protected]=> string(2) "en"
["toStringFormat":protected]=> string(19) "yyyy-MM-dd HH:mm:ss"
["date"]=> string(26) "2022-02-11 12:30:02.846108"
["timezone_type"]=> int(3)
["timezone"]=> string(16) "Europe/Amsterdam"
}
but when i try to get $time->date returns NULL.
Any ideea how to access date string in order to get the current time with microseconds?
Found out there is no ->date attribute in the DateTime object, which is why $time->date returns NULL.
The proper way to get the date with microseconds is:
$time->format('Y-m-d\TH:i:s.u')

Simple statement to calculate the number of days between two 8 Digit dates using PHP?

Please let me construct a statement to calculate the number of days between two variables that have the format: 20211024. This is of course the year, then month, then day in a single eight digit variable.
By using the DateTime class?
$d = new DateTime('20211024');
var_dump($d);
$d2 = new DateTime('20211014');
var_dump($d2);
echo $d->diff($d2)->format('%R%a days');
That example code gives the output:
object(DateTime)#7 (3) {
["date"]=>
string(26) "2021-10-24 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
object(DateTime)#8 (3) {
["date"]=>
string(26) "2021-10-14 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
-10 days
Example fiddle

Parse millisecond timestamp with DateTime in PHP

I want to create a DateTime object from a millisecond timestamp but that does not seem to work:
$timestamp_in_ms = 1546300800000; // int or string no difference
var_dump(DateTime::createFromFormat("Uv", $timestamp_in_ms));
var_dump(DateTime::getLastErrors());
This results in:
bool(false)
array(4) {
["warning_count"]=>
int(0)
["warnings"]=>
array(0) {
}
["error_count"]=>
int(1)
["errors"]=>
array(1) {
[13]=>
string(12) "Data missing"
}
}
However if I use a slightly different notation it does work:
$timestamp_in_ms = "1546300800.000";
var_dump(DateTime::createFromFormat("U\.v", $timestamp_in_ms));
var_dump(DateTime::getLastErrors());
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-01-01 00:00:00.000000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
Yes I can do something like this:
$timestamp_with_dot = $timestamp_in_ms / 1000 + "." + $timestamp_in_ms % 1000;
The documentation does not provide any insight in this. Both U and v are supported.
Anybody more info on this issue? I am running PHP 7.4.10
With a division by 1000 you can convert your millisecond timestamp as a float in seconds.
$msTimeStamp = 1600790571478;
$floatSec = $msTimeStamp/1000.0;
When using DateTime::createFromFormat, you can also use the "U.u" format. The "u" format should be exactly 6 characters. This can be done with sprintf().
$dateTime = DateTime::createFromFormat("U\.u", sprintf('%1.6F',$floatSec));
//test
echo $dateTime->format('Y-m-d H:i:s.u');
//2020-09-22 16:02:51.478000
//Timezone "+00:00" (UTC)
The millisecond timestamp can also be a float and also contain microseconds.
The above code works on 32-bit systems too.

PHP format date with milliseconds works but parse fails

I'm using the following format Y-m-d\TH:i:s.v\Z to follow the JavaScript toISOString implementation (2011-10-05T14:48:00.000Z).
Everything works fine if I have a DateTime and I want to format it, however I cannot parse a string that uses this format.
$format = 'Y-m-d\TH:i:s.v\Z';
$stringDateTime = (new \DateTime())->format($format);
var_dump(date_create_from_format($format,$stringDateTime));
I'm using PHP 7 and I have tested the code above with PHP 7.0,7.1 and 7.2. The return that I expect in line 3 is a DateTime class, however I'm getting a false due to there is a parse problem.
I hope someone can clarify this behavior.
Thanks
Datetime will handle it fine, you won't need to create from format.
<?php
$format = 'Y-m-d\TH:i:s.v\Z';
$stringDateTime = (new \DateTime())->format($format);
var_dump(date_create($stringDateTime));
https://3v4l.org/phLE1
Result:
object(DateTime)#1 (3) {
["date"]=>
string(26) "2018-03-13 18:07:30.005000"
["timezone_type"]=>
int(2)
["timezone"]=>
string(1) "Z"
}
This also works:
<?php
$format = 'Y-m-d\TH:i:s.u\Z';
$stringDateTime = (new \DateTime())->format($format);
var_dump(date_create_from_format($format, $stringDateTime));
$format = \DateTime::ISO8601;
$stringDateTime = (new \DateTime())->format($format);
var_dump(date_create_from_format($format, $stringDateTime));
https://3v4l.org/FcUqe
object(DateTime)#1 (3) {
["date"]=>
string(26) "2018-03-13 18:15:10.011717"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "Europe/Amsterdam"
}
object(DateTime)#1 (3) {
["date"]=>
string(26) "2018-03-13 18:15:10.000000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+01:00"
}

DateDiff return different result for each timezones

I have problem with PHP DateDiff, i dont understand why each timezone returns different results, for example in this case Prague return 0 month, and US return 1 month.
What do this difference and how i return 1 month (instead 30 days, when i adding 1 month) as expected?
code Europe/Prague:
date_default_timezone_set("Europe/Prague");
$from = new \DateTimeImmutable('2016-09-01');
$to = $from->add(new \DateInterval('P1M'));
var_dump($from);
var_dump($to);
var_dump($from->diff($to)->m);
var_dump($from->diff($to)->d);
result Europe/Prague:
object(DateTimeImmutable)#1 (3) {
["date"]=>
string(26) "2016-09-01 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Prague"
}
object(DateTimeImmutable)#3 (3) {
["date"]=>
string(26) "2016-10-01 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Prague"
}
int(0)
int(30)
--
code US/Pacific:
date_default_timezone_set("US/Pacific");
$from = new \DateTimeImmutable('2016-09-01');
$to = $from->add(new \DateInterval('P1M'));
var_dump($from);
var_dump($to);
var_dump($from->diff($to)->m);
var_dump($from->diff($to)->d);
result US/Pacific:
object(DateTimeImmutable)#2 (3) {
["date"]=>
string(26) "2016-09-01 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
object(DateTimeImmutable)#4 (3) {
["date"]=>
string(26) "2016-10-01 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
int(1)
int(0)
This is indeed a small bug in PHP DateTime class.
You must use the UTC timezone and set the desired timezone after the calculation :
date_default_timezone_set('UTC');
$europePrag = new DateTimeZone('Europe/Prague');
$usPacific = new DateTimeZone('US/Pacific');
$from = new \DateTimeImmutable('2016-11-01');
$to = $from->add(new \DateInterval('P1M'));
$from->setTimezone($europePrag);
var_dump($from);
var_dump($to);
var_dump($from->diff($to)->m);
var_dump($from->diff($to)->d);
$from = new \DateTimeImmutable('2016-11-01');
$to = $from->add(new \DateInterval('P1M'));
$from->setTimezone($usPacific);
var_dump($from);
var_dump($to);
var_dump($from->diff($to)->m);
var_dump($from->diff($to)->d);
I think it is the behaviour described in this ticket:
https://bugs.php.net/bug.php?id=52480
So, yes it seems to be a bug in PHP.

Categories