Code speaks a million words:
php > echo strtotime("2010-12-07T23:00:00.000Z");
1291762800
echo date('c', 1291762800);
2010-12-08T00:00:00+01:00
php > var_dump(DateTime::createFromFormat('c', "2010-12-07T23:00:00.000Z"));
bool(false)
php > var_dump(DateTime::createFromFormat(DateTime::ISO8601, "2010-12-07T23:00:00.000Z"));
bool(false)
Any idea what's going on?
Btw, yes, new DateTime("2010-12-07T23:00:00.000Z") works fine. But I prefer to know what input I am getting.
There's a bug report that exactly describes your problem :)
https://bugs.php.net/bug.php?id=51950
Since 2016-08-07, the bug report has been marked as "not a bug". You need to use strtotime or new DateTime instead.
The constants that have been defined apply to both formatting and parsing in the same way, which forces your ways.
Parsing ISO8601 date, and also switching timezone:
// create ISO8601 dateTime
$date = DateTime::createFromFormat(DateTime::ISO8601, '2016-07-27T19:30:00Z');
// set to user's timezone
$date -> setTimeZone('Asia/Singapore');
echo $date -> format(DateTime::ISO8601);
// prints '2016-07-28T03:30:00+0800'
Nobody mentioned to use DATE_ATOM which is as far as i know phps most correct implementation of ISO 8601. It should at least work for the last 3 of these:
<?php
$dates = array(
"2010-12-07T23:00:00.000Z",
"2010-12-07T23:00:00",
"2010-12-07T23:00:00Z",
"2010-12-07T23:00:00+01:00",
(new \DateTime("now"))->format(DATE_ATOM)
);
foreach($dates as $d) {
$res = \DateTime::createFromFormat(DATE_ATOM, $d);
echo "try $d: \n";
var_dump($res);
echo "\n\n";
}
?>
To be able to parse all of them i wrote a tiny function:
<?php
function parse_iso_8601($iso_8601_string) {
$results = array();
$results[] = \DateTime::createFromFormat("Y-m-d\TH:i:s",$iso_8601_string);
$results[] = \DateTime::createFromFormat("Y-m-d\TH:i:s.u",$iso_8601_string);
$results[] = \DateTime::createFromFormat("Y-m-d\TH:i:s.uP",$iso_8601_string);
$results[] = \DateTime::createFromFormat("Y-m-d\TH:i:sP",$iso_8601_string);
$results[] = \DateTime::createFromFormat(DATE_ATOM,$iso_8601_string);
$success = array_values(array_filter($results));
if(count($success) > 0) {
return $success[0];
}
return false;
}
// Test
$dates = array(
"2010-12-07T23:00:00.000Z",
"2010-12-07T23:00:00",
"2010-12-07T23:00:00Z",
"2010-12-07T23:00:00+01:00",
(new \DateTime("now"))->format(DATE_ATOM)
);
foreach($dates as $d) {
$res = parse_iso_8601($d);
echo "try $d: \n";
var_dump($res);
echo "\n\n";
}
?>
As #Glutexo mentioned it works only if there are only 1 to 6 precision digits for the decimal part, too. Feel free to improve it.
try this:
DateTime::createFromFormat('Y-m-d\TH:i:sP', $date)
It is very strange and disappointing that this bug is still actual.
Here is a right pattern for parsing date with microseconds in decimal part of seconds:
Y-m-d\TH:i:s.uO
Usage:
$dateStr = '2015-04-29T11:42:56.000+0400'
$ISO = 'Y-m-d\TH:i:s.uO'
$date = DateTime::createFromFormat($ISO, $dateStr)
Simply :
$dt = new DateTime('2018-04-07T16:32:44Z');
$dt->format('Ymd'); // 20180407
Use DATE_ATOM rather than 'c' when formatting like #Steven said. This is how you work with ISO 8601 in PHP.
<?php
$now_date = new DateTime();
$now_iso_8601 = $now_date->format(DATE_ATOM);
echo "Now in ISO 8601 format: {$now_iso_8601}\n";
$date_from_string_and_format = date_create_from_format(DATE_ATOM, $now_iso_8601);
echo "ISO 8601 formatted string, back to DateTime object:\n";
var_dump($date_from_string_and_format);
prints
Now in ISO 8601 format: 2018-09-05T08:17:35-10:00
ISO 8601 formatted string, back to DateTime object:
object(DateTime)#2 (3) {
["date"]=>
string(26) "2018-09-05 08:17:35.000000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "-10:00"
}
For the answer listed here https://stackoverflow.com/a/14849503/2425651
we can use this format "Y-m-d\TH: i: s.u+" to keep the microseconds.
$format = 'Y-m-d\TH:i:s.u+';
$value = '2017-09-21T10:11:19.026Z'; // jsDate.toUTCString();
var_dump(\DateTime::createFromFormat($format, $value));
This one works for me:
$date = (new DateTime)->setTimestamp(strtotime('2017-12-31T23:00:00.000Z'));
I've experienced this issue with POSTGRES default Time with timezone format and this was the format that fixed it for me:
Y-m-d H:i:s.uO
This works for me:
$timeStamp = "2020-12-10T14:54:25.618Z";
var_dump(DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $timeStamp));
object(DateTime)#1 (3) {
["date"]=>
string(26) "2020-12-10 14:54:25.618000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
I am using follow function that allow multiple ISO8601 formats:
function fromISO8601($time, \DateTimeZone $timezone = null) {
// valid ISO time 2019-04-01T00:00:00.000+02:00
$t = \DateTime::createFromFormat('Y-m-d\TH:i:s.uO', $time) or
// ISO time without millis 2019-04-01T00:00:00+02:00
$t = \DateTime::createFromFormat('Y-m-d\TH:i:sO', $time) or
// ISO time without timezone 2019-04-01T00:00:00.000
$t = \DateTime::createFromFormat('Y-m-d\TH:i:s.u', $time, $timezone) or
// ISO time without millis and timezone 2019-04-01T00:00:00.000+02:00
$t = \DateTime::createFromFormat('Y-m-d\TH:i:s', $time, $timezone);
return $t;
}
here are all supported dates
var_dump(
fromISO8601('2019-04-01T00:00:00.000+02:00'),
fromISO8601('2019-04-01T00:00:00+02:00'),
fromISO8601('2019-04-01T00:00:00.000'),
fromISO8601('2019-04-01T00:00:00')
);
This code is benevolent for missing timezone and milliseconds and works in older php versions.
Related
This question already has answers here:
Convert one date format into another in PHP
(17 answers)
Closed 3 years ago.
I face this problem to integrate bKash Online Payment Gateway API.
The Documentation is here - https://developer.bka.sh/docs/create-payment-1.
In 'Create Payment' section bKash return paymentCreateTime string as following format.
2020-01-07T11:55:34:438 GMT+0600
How can I convert it to 2020-01-07 11:55:34 to save in MySQL?
I find a solution like following way.
$input = "2020-01-07T11:55:34:438 GMT+0600" // "2020-01-07T11:55:34:438 GMT+0600"
$timestamp = substr($input,0,19); // "2020-01-07T11:55:34"
$mysql = date_format(date_create($timestamp),'Y-m-d H:i:s'); // "2020-01-07 11:55:34"
But I need a solution without substr() function.
You can use createFromFormat() to directly parse your string.
Importantly, you'll need to escape T and GMT.
Everything else in https://www.php.net/manual/en/datetime.createfromformat.php
$input = '2020-01-07T11:55:34:438 GMT+0600';
$date = DateTime::createFromFormat('Y-m-d\Th:i:s:u \G\M\TO', $input);
echo $date->format('Y-m-d h:i:s');
This will cleanly deliver an object that you can format however you like.
Here is a demonstration:
$date = new DateTime("now");
echo $date->format('Y-m-d\Th:i:s:u \G\M\TO');
echo "\n---\n";
$input = '2020-01-07T11:55:34:438 GMT+0600';
echo $input;
echo "\n---\n";
$date = DateTime::createFromFormat('Y-m-d\Th:i:s:u \G\M\TO', $input);
echo $date->format('Y-m-d h:i:s');
echo "\n===\n";
var_dump($date);
Demonstration Output:
2020-01-07T10:12:53:000245 GMT+0100
---
2020-01-07T11:55:34:438 GMT+0600
---
2020-01-07 11:55:34
===
object(DateTime)#2 (3) {
["date"]=>
string(26) "2020-01-07 11:55:34.438000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+06:00"
}
You can use it like this
date_default_timezone_set("Asia/Dhaka");
$mysql1 = date_format(date_create(strtotime($input)),'Y-m-d H:i:s');
it will print 2020-01-07 09:17:23
This question already has an answer here:
Create Date object in PHP for dates before 1970 in certain format
(1 answer)
Closed 4 years ago.
PHP v 5.6.2
I need to convert dates such as 18-Jul-46 to give me 18/07/1946 and no amount of DateTime functions work correctly. (As strtotime wont work for dates before 1970). They all end up giving 18/07/2046 which is incorrect.
Codes I tried so far:
$date = new DateTime("18-Jul-46");
$date->format('d/m/Y');
Another one with DateTime
$date = DateTime::createFromFormat('d-M-y', "18-Jul-46");
$date->format('d/m/Y');
Earlier also tried,
$date = date('d/m/Y', strtotime('18-Jul-46'));
None of them gave me the correct format. Any pointers or help is appreciated.
Thanks in advance!
If you have a date such as '31-Dec-18", it is ambiguous since it may refer to a date in 1918 or else a date in 2018. However, if you know that all the dates refer to the 1900s, then code such as the following is feasible given a two-digit year:
<?php
$now = new DateTime();
$formatted = "";
$arrFormattedBDays = [];
$birthdays = ['18-Jul-46','19-Aug-47','1-Jan-19','31-Dec-18'];
foreach ($birthdays as $b){
list($d,$m,$y) = explode("-",$b);
$y = '19'.$y;
$dt = new DateTime("$d-$m-$y");
$formatted = $dt->format('d/m/Y');
$arrFormattedBDays[] = $formatted;
}
var_dump($arrFormattedBDays);
Output:
array(4) {
[0]=>
string(10) "18/07/1946"
[1]=>
string(10) "19/08/1947"
[2]=>
string(10) "01/01/1919"
[3]=>
string(10) "31/12/1918"
See live code
Otherwise, by default DateTime creates a date object based on the current year which you may format according to the truth you seek to perpetuate; see here. Note: if you know that the dates all occur in the 20th century, i.e. 1901-2000, then you may amend this code by adding in a little logic; see here.
The computer doesn't know whether you mean 2000 or 1900. You can just take the last 2 digits of the year and put "19" before like:
$date = new DateTime("18-Jul-46");
$date->format('d/m/19y');
If you want to use 2000 as well, this code will use the closest number to 1970
$date = new DateTime("18-Jul-20");
$date->format('d/m/Y');
$t1 = $date->format('19y');
$t2 = $date->format('20y');
if(abs(1970-$t1)<abs(1970-$t2))
echo $t1; //Take the 19.. one
else
echo $t2; //Take the 20.. one
But in the end, you can't be sure that even 2030 would be correct.
I want to parse postgres datetime with timezone by DateTime::createFromFormat(). But I don't know, how to compose $format parameter.
$dates = [
'2016-03-04 10:29:40+01',
'2016-03-04 10:29:40.123456+01',
];
$format = 'Y-m-d H:i:s.uP';
foreach ($dates as $value) {
$dt = DateTime::createFromFormat($format, $value);
var_dump($dt);
}
bool(false)
object(DateTime)#2 (3) {
["date"]=>
string(26) "2016-03-04 10:29:40.123456"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+01:00"
}
First date is not parsed, second yes.
Edit
As you want DateTime::createFromFormat() solution only, you'll face a problem: createFromFormat() don't support optional part. There is no way to parse directly a string that may have microsecond.
One of the hack you can do is searching for the . that's used for the ms part, and change your pattern on the fly:
<?
$dates = [
'2016-03-04 10:29:40+01',
'2016-03-04 10:29:40.123456+01',
];
foreach ($dates as $value) {
$format = (strpos($value,'.')===FALSE) ?'Y-m-d H:i:sP':'Y-m-d H:i:s.uP';
$dt = DateTime::createFromFormat($format, $value);
var_dump($dt);
}
PHP::strtotime() is probably the best solution if you can't format your output with Postgresql::to_char()
<?
echo date('Y-m-d H:i:s.uP', strtotime('2016-03-04 10:29:40+01'));
// 2016-03-04 01:29:40.000000-08:00
echo date('Y-m-d H:i:s.uP', strtotime('2016-03-04 10:29:40.123456+01'));
// 2016-03-04 01:29:40.000000-08:00
I am having trouble with the following string which is supposed to be a datetime (response from an api).
2013-03-08T19:11:11.234+0000
I need to convert the string to ISO 8601.
The exact format should look like:
2016-03-01T12:00:00+00:00
If someone could help me do this in php would be great but I would be already more than happy if someone could identify the format (like yyyy-mm-ddThh:mm:ss).
It seems the API returns the time with milliseconds.
Try this:
$date = '2013-03-08T19:11:11.234+0000';
$format = 'Y-m-d\TH:i:s.uO';
$dt = DateTime::createFromFormat($format, $date);
echo $dt->format(DateTime::ISO8601);
Result:
2013-03-08T19:11:11+0000
EDIT
Based on comments on your answer, you need to convert such a time:
2016-03-01T11:01:51.126044 00:00
Since it's not possible to parse this format with DateTime, you need to make it readable first.
There are many ways to do this. What we need is to add a + before timezone, so that my code above work.
If you are sure the date you get from the API is always this format, and that there will always be only 1 space in the string, just replace the string with a space:
$date = '2016-03-01T11:01:51.126044 00:00';
str_replace(' ', '+', $date);
$format = 'Y-m-d\TH:i:s.uP';
$dt = DateTime::createFromFormat($format, $date);
echo $dt->format(DateTime::ISO8601);
This should work (note that here the timezone mask is P since there is a colon)
You can also use preg_replace() instead of DateTime, if you don't need the date for calculations:
$date = '2016-03-01T11:01:51.126044 00:00';
$date = preg_replace('`(\.[0-9]+\s)`', '+', $date);
echo $date;
This produces:
2016-03-01T11:01:51+00:00
If you need the date for calculation, just convert it to DateTime after:
$date = '2016-03-01T11:01:51.126044 00:00';
$date = preg_replace('`(\.[0-9]+\s)`', '+', $date);
$date = DateTime::createFromFormat(DateTime::ISO8601, $date);
var_dump($date);
This produces:
object(DateTime)#1 (3) {
["date"]=>
string(26) "2016-03-01 11:01:51.000000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
I suggest you to use Carbon: http://carbon.nesbot.com/docs/
echo Carbon::parse('2013-03-08T19:11:11.234+0000')->toAtomString();
Also, the format you want is ATOM, not ISO 8601. You could do #Arcesilas way, it should work too.
my current code:
<?php
$timestamp = DateTime::createFromFormat("d.m.Y H:i", $_POST["datetime"]);
echo $timestamp; // works!
?>
Now I've a form with two fields: date and time, separated.
<?php
$timestamp = DateTime::createFromFormat("d.m.Y", $_POST["date"]);
echo $timestamp; // works!
$timestamp = DateTime::createFromFormat("H:i", $_POST["time"]);
echo $timestamp; // doesn't work (error below)!
// Edit:
/* Fatal error: Call to a member function getTimestamp() on a non-object in */
?>
I would like to avoid using explode() with mktime(). That feels dirty and I think there could be another, clean way.
How would you create the timestamp from format?
Thanks in advance!
You can't. The timestamp requires a date in order to be calculated, and there isn't one available to use.
If you want to get the date and time from $_POST["date"] and $_POST["time"] Use following
DateTime::createFromFormat("d.m.Y H:i", $_POST["date"]." ".$_POST["time"]);
If you want to kow the number of seconds from midnight for that particular time you can however use this,
DateTime::createFromFormat("Ymd H:i", date("Ymd")." ".$_POST["time"]);
Use Date time's settime method
$timestamp = \DateTime::createFromFormat("d.m.Y", '10.10.2012');
$timestamp->setTime(17,12);
var_dump($timestamp);
Gives
object(DateTime)#1142 (3) { ["date"]=> string(19) "2012-10-10 17:12:00" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" }