2010-12-10 50 views
31

Código habla de un millón de palabras:PHP DateTime :: createFromFormat no analiza la norma ISO 8601 de fecha y hora

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) 

alguna idea de lo que está pasando?

Por cierto, sí, nuevo DateTime ("2010-12-07T23: 00: 00.000Z") funciona bien. Pero prefiero saber qué entrada estoy recibiendo.

+3

Sus fracciones de segundo no son parte del formato. 'DateTime :: ISO8601' es una cadena con el valor' Y-m-d \ TH: i: sO'. – salathe

+4

WikiPedia no está de acuerdo: Las fracciones decimales también se pueden agregar a cualquiera de los tres elementos de tiempo. Un punto decimal, ya sea una coma o un punto (sin ninguna preferencia, como se indicó más recientemente en la resolución 10 de la 22ª Conferencia General CGPM en 2003), se usa como un separador entre el elemento de tiempo y su fracción. Una fracción solo se puede agregar al elemento de tiempo de orden más bajo en la representación. – Jake

+0

'public static DateTime DateTime :: createFromFormat (string $ format, string $ time [, DateTimeZone $ timezone])', puede deberse a la inclusión de la zona horaria – ajreal

Respuesta

32

Hay un informe de error que describe exactamente su problema :)

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

Desde 08/07/2016, el informe de error se ha marcado como "no es un error". En su lugar, debe usar strtotime o new DateTime.

Las constantes que se han definido se aplican tanto al formateo como al análisis de la misma manera, lo que obliga a sus formas.

+2

De hecho decepcionante. ¿Por qué nombrar un "ISO8601" constante si no es realmente compatible con ese estándar? Es por eso que no me gusta mucho PHP. – jlh

+0

Bloody 7 años después y aún existe ... ¿e incluso está marcado como un error? 'Si desea aceptar diferentes formatos de fecha/hora ISO-8601'. ¿Diferentes formatos ISO-8601? Por qué no me gusta el php en pocas palabras. –

+0

Sintiendo el dolor también –

1

Es muy extraño y decepcionante que este error siga siendo real. Aquí es un modelo adecuado para la fecha de análisis con microsegundos en la parte decimal de segundos:

Y-m-d\TH:i:s.uO 

Uso:

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

En realidad, esto funciona solo si la parte decimal de los segundos está presente. Además, solo funciona si solo hay de 1 a 6 dígitos de precisión. Si se omite la parte decimal o si es más precisa, se devuelve falso. – Glutexo

6

intente esto:

fecha
DateTime::createFromFormat('Y-m-d\TH:i:sP', $date) 
10

de análisis de ISO8601 y también cambiar zona horaria:

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

copió su código e imprime: "2016-07-27T19: 30: 00 + 0000" usando PHP 5.6.10 –

+0

@AlexAngelico ¿Cuál es el resultado para 'echo DateTime :: ISO8601;'? Imprime 'Y-m-d \ TH: i: sO' para mí. – a20

+0

sí, DateTime :: ISO8601 imprime Y-m-d \ TH: i: sO Estoy usando este código para cambiar la hora a la zona horaria correcta. Estoy en GMT-5 '$ fecha_str = '2017-04-12T21: 43: 54 + 02: 00'; \t $ date = strtotime ($ fecha_str); \t echo date ("Y-m-d h: i: s", $ date); \t/output => 2017-04-12 07:43:54 ' –

2

Nadie mencionó a utilizar DATE_ATOM que es como lo que sé phps más correcta aplicación de la norma ISO 8601. Debe al menos trabajo para el último 3 de estos:

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

?> 

Para ser capaz de analizar todos ellos i escribió una función pequeña:

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

?> 

Como se mencionó @Glutexo funciona sólo si hay sólo 1 a 6 dígitos de precisión para la parte decimal, también. Siéntase libre de mejorarlo.

0

Para la respuesta que figura aquí https://stackoverflow.com/a/14849503/2425651 podemos utilizar este formato "Y-m-d \ TH: i: s.u +" para guardar los microsegundos.

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

Esta funciona para mí:

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