Parsing a datetime string with timezone in PHP - php

I have a string in this format: 2013-07-31T19:20:30.45-07:00 and I want to parse it so that I can, for example, say what day of the week it is. But I'm struggling to cope with the timezone offset. If I do date_parse("2013-07-31T19:20:30.45-07:00") then I end up with something like this:
array(15) {
["year"]=> int(2013)
["month"]=> int(7)
["day"]=> int(31)
["hour"]=> int(19)
["minute"]=> int(20)
["second"]=> int(30)
["fraction"]=> float(0.45)
["warning_count"]=> int(0)
["warnings"]=> array(0) { }
["error_count"]=> int(0)
["errors"]=> array(0) { }
["is_localtime"]=> bool(true)
["zone_type"]=> int(1)
["zone"]=> int(420)
["is_dst"]=> bool(false)
}
It's done something with the timezone, but what do I do with 420 if I want to, for example, show information about the timezone?
In case it matters, I have previously set my default timezone using date_default_timezone_set('UTC').
UPDATE: If the string has a positive timezone, eg 2013-07-31T19:20:30.45+07:00 then the last part of the date_parse() output is:
["is_localtime"]=> bool(true)
["zone_type"]=> int(1)
["zone"]=> int(-420)
["is_dst"]=> bool(false)
}

420 is zone in minutes.
420/60 = 7
I want to parse it so that I can, for example, say what day of the
week it is.
If you want to know what day of the week it is, you have many options. For example you can use date and mktime-functions:
$parsed = date_parse("2013-07-31T19:20:30.45-07:00");
$unix_timestamp = mktime($parsed['hour'], 0, 0, $parsed['month'], $parsed['day'], $parsed['year'], (int)$parsed['is_dst']);
echo date('l', $unix_timestamp);
So you want to show the information about timezone? You can get the time zone name by using timezone_name_from_abbr function:
$name = timezone_name_from_abbr("", -1*$parsed['zone']*60, false); // NB: Offset in seconds!
var_dump($name);
$timezone = new DateTimezone($name);
var_dump($timezone);

2013-07-31T19:20:30.45-07:00
^ y-m-d ^ time ^ timezone offset
I'm guessing the timezone is -7 hours from UTC.
Keep in mind that some countries have half-hour timezones, or even minute timezones. This is probably why you get the timezone in minutes.

Related

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 DateInterval create split second (microsecond or milisecond) interval

How to create split second DateInterval in PHP?
For example:
0.123456 sec
?
interval_spec of DateInterval::__construct
does not have F nor f similarly to DateInterval::format that supports split seconds.
Relative format listing of DateInterval::createFromDateString also does not mention any fraction of a sec.
But the DateInterval class properties listing shows:
f
Number of microseconds, as a fraction of a second.
I was able to get DateInterval with a split second by using the DateTime::diff of two DateTimes with a split seconds
example:
$formatStr = 'Y-m-d H:i:s.u';
$dateTimeStr = '2000-01-01 00:00:00.0';
$timeZone = new \DateTimeZone('UTC');
$timerDateTimeStart = \DateTimeImmutable::createFromFormat($formatStr, $dateTimeStr, $timeZone);
$formatStr = 'Y-m-d H:i:s.u';
$dateTimeStr = '2000-01-01 00:00:00.123456';
$timeZone = new \DateTimeZone('UTC');
$timerDateTimeEnd = \DateTimeImmutable::createFromFormat($formatStr, $dateTimeStr, $timeZone);
$timerDiff = $timerDateTimeStart->diff($timerDateTimeEnd);
$intervalStr = $timerDiff->format('%Y-%M-%D %H:%I:%S.%f');
echo 'Interval (yyyy-mm-dd hh:mm:ss.sfract): ' . $intervalStr;
gives:
Interval (yyyy-mm-dd hh:mm:ss.sfract): 00-00-00 00:00:00.123456
since DateTime supports the split second time_format in its constructor and DateTime::createFromFormat understands
u Microseconds (up to six digits) Example: 45, 654321
BTW: don't you think that u in case of working with the DateTime and F or f in case of DateInterval has a potential for making your day a little bit less boring?
One of my workaround could be to create two DateTime with a split second precision and then diff to get the DateInterval with the same split second precision but I would like to get the same result with just the DateInterval.
Do you know how to create DateInterval having 0.123456 sec by using just the DateInterval?
Solution based on the accepted answer:
$dateInterval = DateInterval::createFromDateString("1 day, 2 hours, 3 minutes, 1 second, 123456 microsecond");
$intervalStr = $dateInterval->format('%D %H:%I:%S.%F');
echo 'Interval (dd hh:mm:ss.sfract): ' . $intervalStr . PHP_EOL;
gives:
Interval (dd hh:mm:ss.sfract): 01 02:03:01.123456
There is a way to do this using DateInterval::createFromDateString:
$di = DateInterval::createFromDateString('123456 microseconds');
var_dump($di);
Output:
object(DateInterval)#1 (16) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(0)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["f"]=>
float(0.123456)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
bool(false)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
Demo on 3v4l.org
From the manual:
time
A date with relative parts. Specifically, the relative formats supported by the parser used for strtotime() and DateTime will be used to construct the DateInterval.

Incorrect time showing after converting to date format - PHP

Below is the time string I have,
'2019-12-30T15:42:33.891+11:00'
I tried to convert this to date format using below php code.
date('Y-m-d H:i:s',strtotime('2019-12-30T15:42:33.891+11:00'));
And i am getting 2019-12-30 10:12:33 as the output. It seems date getting correctly but time not.
How to display the time also correctly? Thanks in advance!
This date and time format relates to the "Zulu time" (UTC). UTC is refereed as Universal Time Coordinated. It is also known as “Z time” or “Zulu Time”.
You should change your timezone by some command like below code:
$date = new DateTime('2019-12-30T15:42:33.891+11:00', new DateTimeZone('UTC'));
echo $date->format('Y-m-d H:i:s');
Or, for having more tools you can use the Carbon library.
Your time string '2019-12-30T15:42:33.891+11:00' contains time zone information of +11:00.
With date() the date and time are converted into the local time zone of your server. For example, I get "2019-12-30 05:42:33" (Timezone Europe/Berlin).
You can set a different time zone with date_default_timezone_set() or use DateTime to output the time for a location with the time zone "+11:00".
echo date_create('2019-12-30T15:42:33.891+11:00')->format('Y-m-d H:i:s');
//2019-12-30 15:42:33
strtotime () returns an integer timestamp. The Iformation of the Timezone +11:00 are lost in the process, but are processed when the time stamp is determined.
The DateTime object always has a time zone.
echo '<pre>';
var_dump(date_create('2019-12-30T15:42:33.891+11:00'));
Output:
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-12-30 15:42:33.891000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+11:00"
}
Note (Update):
When creating a DateTime object, the optional parameter for the time zone is only taken into account if the time string does not contain a time zone!
var_dump(new DateTime('2019-12-30T15:42:33.891 +11:00', new DateTimeZone('UTC')));
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-12-30 15:42:33.891000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+11:00"
}
//without +11:00
var_dump(new DateTime('2019-12-30T15:42:33.891', new DateTimeZone('UTC')));
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-12-30 15:42:33.891000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}

Difference in dates between servers

I am wondering why I have been getting totally invalid computation by PHP all the time.
I have code like this:
echo floor((strtotime("2017-03-27") - strtotime("2017-03-24"))/86400);
which on the one of the server returns: 3 (like 3 days)
and on the another server returns: 2! (2.9583333333333 day?) Why is there a variance?
Or, just use better methods:
EDIT I updated the code to use the same dates as OP. It returns 3, as it should, and I'm fairly confident it will do that on whatever server you put it on to test.
<?php
$date1 = date_create('2017-03-27');
$date2 = date_create('2017-03-24');
$interval = date_diff($date1,$date2);
echo $interval->format('%a');
?>
Ref: http://php.net/manual/en/function.date-diff.php
Your servers maybe sitting in two different time zones. Convert the days to hours and you will get a difference of one hour.
3 days * 24 hours/1 day = 72 hours
2.958333 days * 24 hours/1 day = 71 hours
Since some people are having difficulty accepting that this is due to a TZ-specific DST change, have some proof:
function poc($tz_str) {
$tz = new DateTimeZone($tz_str);
$start = (new DateTime('2017-03-24', $tz))->format('U');
$end = (new DateTime('2017-03-27', $tz))->format('U');
return [$tz_str, $end - $start, ( $end - $start ) / 86400];
}
var_dump(
poc('UTC'),
poc('Europe/London'),
poc('America/New_York')
);
Output:
array(3) {
[0]=> string(3) "UTC"
[1]=> int(259200)
[2]=> int(3)
}
array(3) {
[0]=> string(13) "Europe/London"
[1]=> int(255600)
[2]=> float(2.9583333333333)
}
array(3) {
[0]=> string(16) "America/New_York"
[1]=> int(259200)
[2]=> int(3)
}
UTC has no DST, not affected.
Most of the eastern hemisphere has their DST change on March 26, affected.
Most of the western hemisphere has their DST change on March 12, not affected.
That said, don't do manual date math on a Unix timestamp.

DateTime compare two dates

The goal is to diff two dates.
I have a DateTime object stored in my db table under timestamp column. I use Doctrine to retrieve the date, Once retrieved the date from my db it looked like this by var_dump;
array(1) {
[0]=>
array(1) {
["timestamp"]=>
object(DateTime)#321 (3) {
["date"]=>
string(26) "2016-08-03 11:03:36.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
}
}
the retrieved object was assigned to a $result variable now to get to the DateTime object I did this $result[0][timestamp].
To retrieve the actual data i did this $date1 = $result->format('Y-m-d H:i:s'); according to this documentation
So now that i have retrieved the date a row was inserted into my db I need another date as of current time.
i.e:
$date1 = $result->format('Y-m-d H:i:s');
$date2 = new DateTime();
$test = $date1->diff($date2);
diff according to this documentation
this gives me this error:
Error: Call to a member function diff() on a non-object
Any idea why i get this error message looks like am doing things the right way according to the docks. Maybe there is another way do diff two dates OPP way.
UPDATE:
Ok so yes it is true if I use $date1 = $result->format('Y-m-d H:i:s'); its no longer a object its a stiring.
So now my code looks like this:
$test = $result->diff(new DateTime());
var_dump($test);
it returns DateInterval object but what do i make out of it:
object(DateInterval)#317 (15) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(0)
["h"]=>
int(1)
["i"]=>
int(27)
["s"]=>
int(5)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(0)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
What i need if Date1 diff to Date2 is > than 30 mins I want to take some actions.
It is because method format returns string, but not object. Try to use:
$test = $result->diff(new DateTime());
$date1 = $result->format('Y-m-d H:i:s');
$date1 is now a string which doesn't have a format() method.
Try this instead:-
$date1 = $result->format('Y-m-d H:i:s');
$date2 = new DateTime();
$test = $result->diff($date2);
After you do $date1 = $result->format('Y-m-d H:i:s');. $date1 is no more a DateTime object, rather a string representation of date and time.
Hence skip this line. Don't format, and then check for difference. That will give you a DateTimeInterval object.
For anyone who doesn't know what diff() does, it compares two dates and returns the difference -- but unlikely other subtraction methods, diff() never returns a negative value. It doesn't matter if the first date is larger or smaller than the second date.
Now, if that is not a concern for this case, the next step is to access the necessary values in the DateInterval object.
$diff=(array)$date1->diff($date2); // calculate the difference and cast as an array
$labels=array("y"=>"year","m"=>"month","d"=>"day","h"=>"hour","i"=>"minute");
// filter the $diff array to only include the desired elements and loop
foreach(array_intersect_key($diff,$labels) as $k=>$v){
if(($k!="i" && $v>0) || $v>30){
// $labels[$k] = $v > 30 minutes, take some action
}
}

Categories