PHP DateTime::createFromFormat nu analizează data și ora ISO 8601 (Programare, Php, Datetime)

Jake a intrebat.

Codul vorbește un milion de cuvinte:

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)

Aveți vreo idee despre ce se întâmplă?

Btw, da, new DateTime(„2010-12-07T23:00:00.000.000Z”) funcționează bine. Dar prefer să știu ce intrare primesc.

Comentarii

  • Secundele tale fracționare nu fac parte din format. DateTime::ISO8601 este un șir de caractere cu valoarea Y-m-dTH:i:sO. –  > Por salathe.
  • WikiPedia nu este de acord: Fracțiile zecimale pot fi, de asemenea, adăugate la oricare dintre cele trei elemente de timp. O virgulă zecimală, fie o virgulă, fie un punct (fără nicio preferință, așa cum s-a afirmat cel mai recent în rezoluția 10 a celei de-a 22-a Conferințe Generale CGPM din 2003), este folosită ca separator între elementul de timp și fracțiunea sa. O fracțiune poate fi adăugată numai la elementul de timp de cel mai mic ordin din reprezentare. –  > Por Jake.
  • public static DateTime DateTime::createFromFormat ( string $format , string $time [, DateTimeZone $timezone ] ), , poate că este cauzată de includerea fusului orar –  > Por ajreal.
  • Se pare că nici „c” și DateTime::ISO8601 nu sunt același lucru: php > var_dump(DateTime::createFromFormat(‘c’, „2010-12-10T19:02:09+01:00″)); bool(false) php > var_dump(DateTime::createFromFormat(‘c’, ‘2010-12-10T19:02:09+01: 00”))); bool(false) php > var_dump(DateTime::createFromFormat(DateTime::ISO8601, ‘2010-12-10T19:02:09+01: 00’))); object(DateTime)#26 (3) { [„date”]=> string(19) „2010-12-10 19:02:09” [„timezone_type”]=> int(1) [„timezone”]=> string(6) „+01:00” } –  > Por Jake.
12 răspunsuri
Ja͢ck

Există un raport de eroare care descrie exact problema ta 🙂

https://bugs.php.net/bug.php?id=51950

Începând cu 2016-08-07, raportul de eroare a fost marcat ca „not a bug”. Trebuie să folosiți strtotime sau new DateTime în loc.

Constantele care au fost definite se aplică atât la formatare, cât și la analiză în același mod, ceea ce forțează căile tale.

Comentarii

  • Într-adevăr, dezamăgitor. De ce să numești o constantă „ISO8601” dacă nu este de fapt conformă cu acest standard. Acesta este motivul pentru care nu prea îmi place PHP. –  > Por jlh.
  • 7 ani mai târziu și acest lucru încă există…. și este chiar marcat ca nefiind un bug? If you want to accept different ISO-8601 date/time formats. Diferite formate ISO-8601??? De ce nu-mi place php pe scurt. –  > Por Douglas Gaskell.
  • Simt și eu durerea –  > Por Michael Ambrose.
a20

Parsarea datei ISO8601 și, de asemenea, schimbarea fusului orar:

// 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'

Comentarii

  • copiat codul dvs. și se imprimă: „2016-07-27T19:30:00:00+0000” folosind PHP 5.6.10 –  > Por Alex Angelico.
  • @AlexAngelico Care este rezultatul pentru echo DateTime::ISO8601;? Se tipărește Y-m-dTH:i:sO pentru mine. –  > Por a20.
  • da, DateTime::ISO8601 tipărește Y-m-dTH:i:sO Folosesc acest cod pentru a schimba ora în fusul orar corect. Eu sunt în GMT-5 $fecha_str = '2017-04-12T21:43:54+02:00'; $date = strtotime($fecha_str); echo date("Y-m-d h:i:s", $date); /output=> 2017-04-12 07:43:54 –  > Por Alex Angelico.
  • Doar ca o atenționare, nu folosiți format(DateTime::ISO8601) deoarece nu este un format ISO8601 valid. În schimb, utilizați fie c, , DateTime::ATOM fie DATE_ATOM. Motivul este că DateTime:ISO8601 ar stoca în mod greșit ca 2005-08-15T15:52:01+0000 când, de fapt, un șir de date ISO8601 valid ar trebui să aibă două puncte între indicatorul de fus orar, cum ar fi 2005-08-15T15:52:01+00:00. Acest lucru este menționat în documentația PHP, dar nu este imediat clar. Citiți documentația aici: ca3.php.net/manual/en/… –  > Por puiu.
  • Nici DateTime::ISO8601, nici DateTime::ATOM nu funcționează pentru mine pentru date precum ‘2020-02-04T00:00:00:00.000Z’. Formatul ‘Y-m-dTH:i:s+’ funcționează foarte bine. –  > Por ed22.
steven

Nimeni nu a menționat să folosească DATE_ATOM care este, din câte știu eu, cea mai corectă implementare phps a ISO 8601. Ar trebui să funcționeze cel puțin pentru ultimele 3 dintre acestea:

<?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: 
";
    var_dump($res);
    echo "

";
}

?>

Pentru a le putea analiza pe toate am scris o mică funcție:

<?php

function parse_iso_8601($iso_8601_string) {
    $results = array();
    $results[] = DateTime::createFromFormat("Y-m-dTH:i:s",$iso_8601_string);
    $results[] = DateTime::createFromFormat("Y-m-dTH:i:s.u",$iso_8601_string);
    $results[] = DateTime::createFromFormat("Y-m-dTH:i:s.uP",$iso_8601_string);
    $results[] = DateTime::createFromFormat("Y-m-dTH: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: 
";
    var_dump($res);
    echo "

";
}

?>

După cum a menționat @Glutexo, funcționează doar dacă există doar între 1 și 6 cifre de precizie pentru partea zecimală, de asemenea. Simțiți-vă liberi să o îmbunătățiți.

Comentarii

  • Tu ești omul! Am căutat pe Google ceva de genul „php parse all rfc 8601 formats” și am găsit din greșeală soluția ta. M-a ajutat foarte mult. –  > Por Serhii Popov.
Etienne

încercați acest lucru:

DateTime::createFromFormat('Y-m-dTH:i:sP', $date)

stloc

Pur și simplu :

$dt = new DateTime('2018-04-07T16:32:44Z');
$dt->format('Ymd'); // 20180407

Dmitri Davydov

Este foarte ciudat și dezamăgitor că acest bug este încă actual. iată un model corect pentru analiza datei cu microsecunde în partea zecimală a secundelor:

Y-m-dTH:i:s.uO

Utilizare:

$dateStr = '2015-04-29T11:42:56.000+0400'
$ISO = 'Y-m-dTH:i:s.uO'
$date = DateTime::createFromFormat($ISO, $dateStr)

Comentarii

  • De fapt, acest lucru funcționează numai dacă partea zecimală a secundelor este prezentă. În plus, funcționează numai dacă există doar între 1 și 6 cifre de precizie. În cazul în care partea zecimală este omisă sau dacă este mai precisă, se returnează false. –  > Por Glutexo.
Dzung Nguyen

Pentru răspunsul listat aici https://stackoverflow.com/a/14849503/2425651we se poate folosi acest format „Y-m-dTH: i: s.u+” pentru a păstra microsecundele.

$format = 'Y-m-dTH:i:s.u+';
$value = '2017-09-21T10:11:19.026Z'; // jsDate.toUTCString();
var_dump(DateTime::createFromFormat($format, $value));

John Erck

Utilizați DATE_ATOM în loc de 'c' atunci când formatați așa cum a spus @Steven. Acesta este modul în care se lucrează cu ISO 8601 în PHP.

<?php
$now_date = new DateTime();
$now_iso_8601 = $now_date->format(DATE_ATOM);
echo "Now in ISO 8601 format: {$now_iso_8601}
";
$date_from_string_and_format = date_create_from_format(DATE_ATOM, $now_iso_8601);
echo "ISO 8601 formatted string, back to DateTime object:
";
var_dump($date_from_string_and_format);

tipărește

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"
}

rckd

Acesta funcționează pentru mine:

$date = (new DateTime)->setTimestamp(strtotime('2017-12-31T23:00:00.000Z'));

Comentarii

  • Acesta este singurul răspuns care interpretează Z ca ora UTC și o corectează în funcție de fusul orar în care se află php. –  > Por Johan Velthuis.
OzzyCzech

Eu folosesc funcția follow care permite mai multe formate ISO8601:

function fromISO8601($time, DateTimeZone $timezone = null) {
    // valid ISO time 2019-04-01T00:00:00.000+02:00
    $t = DateTime::createFromFormat('Y-m-dTH:i:s.uO', $time) or
    // ISO time without millis 2019-04-01T00:00:00+02:00
    $t = DateTime::createFromFormat('Y-m-dTH:i:sO', $time) or
    // ISO time without timezone 2019-04-01T00:00:00.000
    $t = DateTime::createFromFormat('Y-m-dTH: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-dTH:i:s', $time, $timezone);

    return $t;
}

aici sunt toate datele acceptate

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')
);

Acest cod este binevoitor pentru fusul orar și milisecundele lipsă și funcționează în versiuni mai vechi de php.

Sasa Blagojevic

Am experimentat această problemă cu POSTGRES default Time with timezone format și acesta a fost formatul care a rezolvat-o pentru mine:

Y-m-d H:i:s.uO

NitrusCS

Acesta funcționează pentru mine:

$timeStamp = "2020-12-10T14:54:25.618Z";
var_dump(DateTime::createFromFormat('Y-m-dTH:i:s.vZ', $timeStamp));
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2020-12-10 14:54:25.618000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}