PHP y mySQL: año 2038 Error: ¿Qué es? ¿Cómo resolverlo?

116

Estaba pensando en usar TIMESTAMP para almacenar la fecha y la hora, pero leí que hay una limitación del año 2038. En lugar de hacer mi pregunta a granel, preferí dividirla en partes pequeñas para que también sea fácil de entender para los usuarios novatos. Entonces mi pregunta (s):

  1. ¿Cuál es exactamente el problema del año 2038?
  2. ¿Por qué ocurre y qué sucede cuando ocurre?
  3. ¿Como lo resolvemos?
  4. ¿Existen posibles alternativas a su uso que no planteen un problema similar?
  5. ¿Qué podemos hacer con las aplicaciones existentes que usan TIMESTAMP, para evitar el llamado problema, cuando realmente ocurre?

Gracias por adelantado.

Devner
fuente
1
Aún quedan 28 años. ¿Sigue utilizando alguna tecnología relacionada con la informática de 1982? Improbable. Así que no se preocupe, porque para 2038 probablemente ya no será un problema.
Gordon
24
Gordon, hago una aplicación que hace pronósticos. Ahorro la cantidad de dinero que tengo cada mes y luego puedo estimar cuándo seré millonario. En mi caso, 28 años no es tanto, y estoy seguro de que no soy el único que tiene este problema en este momento (lo resolví usando números de 64 bits para representar la marca de tiempo).
Emil Vikström
2
@Emil Al usar números enteros de 64 bits en este momento , ha encontrado una solución fácil para un problema concreto. No es aplicable a (ni necesario para) todos, pero funciona para su caso de uso. El punto es que si el OP no tiene un problema concreto, como la previsión, entonces este podría ser un tema interesante, pero nada de lo que deba preocuparse, porque resolver esto a nivel general no es un problema de PHP (cuidado con la etiqueta). Solo mi 2c.
Gordon
1
Para el año 2038, analizar la cadena "AAAA-MM-DD HH: MM: SS: mmmm ..." será la operación más barata con la que pueda soñar. Para el 2038, 32 Bit estarán obsoletos. Dudo que exista entonces la marca de tiempo de Unix tal como la conocemos, y si lo hace, nuestros sistemas de 256 bits manejarán fechas que van mucho más allá de la edad en la que los sistemas de 4096 bits se distribuyen en comidas felices.
Super Cat
8
Gordon, ahora son 9 años más tarde. Los TIMESTAMPS todavía se utilizan. Todavía usamos tecnología de hace 28 años. Se llama World Wide Web.
sfscs

Respuestas:

149

He marcado esto como un wiki de la comunidad, así que siéntete libre de editarlo cuando quieras.

¿Cuál es exactamente el problema del año 2038?

"El problema del año 2038 (también conocido como Unix Millennium Bug, Y2K38 por analogía con el problema Y2K) puede hacer que algunos programas de computadora fallen antes o en el año 2038. El problema afecta a todos los programas y sistemas que almacenan la hora del sistema como un 32 -bit entero e interprete este número como el número de segundos desde las 00:00:00 UTC del 1 de enero de 1970 ".


¿Por qué ocurre y qué sucede cuando ocurre?

Las horas posteriores a las 03:14:07 UTC del martes 19 de enero de 2038 se 'envolverán' y se almacenarán internamente como un número negativo, que estos sistemas interpretarán como una hora del 13 de diciembre de 1901 en lugar de 2038. Esto se debe a el hecho de que el número de segundos desde la época de UNIX (1 de enero de 1970 00:00:00 GMT) habrá excedido el valor máximo de una computadora para un entero de 32 bits con signo.


¿Como lo resolvemos?

  • Utilice tipos de datos largos (64 bits es suficiente)
  • Para MySQL (o MariaDB), si no necesita la información de tiempo, considere usar el DATEtipo de columna. Si necesita una mayor precisión, utilice en DATETIMElugar de TIMESTAMP. Tenga en cuenta que las DATETIMEcolumnas no almacenan información sobre la zona horaria, por lo que su aplicación tendrá que saber qué zona horaria se utilizó.
  • Otras posibles soluciones descritas en Wikipedia
  • Espere a que los desarrolladores de MySQL solucionen este error informado hace más de una década.

¿Existen posibles alternativas a su uso que no planteen un problema similar?

Intente siempre que sea posible utilizar tipos grandes para almacenar fechas en bases de datos: 64 bits es suficiente: un tipo largo y largo en GNU C y POSIX / SuS, o sprintf('%u'...)en PHP o la extensión BCmath.


¿Cuáles son algunos casos de uso potencialmente innovadores aunque todavía no estemos en 2038?

Entonces, un DATETIME de MySQL tiene un rango de 1000-9999, pero TIMESTAMP solo tiene un rango de 1970-2038. Si su sistema almacena fechas de nacimiento, fechas futuras (por ejemplo, hipotecas a 30 años) o similares, ya se encontrará con este error. Nuevamente, no use TIMESTAMP si esto va a ser un problema.


¿Qué podemos hacer con las aplicaciones existentes que usan TIMESTAMP, para evitar el llamado problema, cuando realmente ocurre?

Pocas aplicaciones PHP seguirán existiendo en 2038, aunque es difícil prever ya que la web no es una plataforma heredada todavía.

Aquí es un proceso para modificar una columna de tabla de base de datos para convertir TIMESTAMPa DATETIME. Comienza con la creación de una columna temporal:

# rename the old TIMESTAMP field
ALTER TABLE `myTable` CHANGE `myTimestamp` `temp_myTimestamp` int(11) NOT NULL;

# create a new DATETIME column of the same name as your old column
ALTER TABLE `myTable` ADD `myTimestamp` DATETIME NOT NULL;

# update all rows by populating your new DATETIME field
UPDATE `myTable` SET `myTimestamp` = FROM_UNIXTIME(temp_myTimestamp);

# remove the temporary column
ALTER TABLE `myTable` DROP `temp_myTimestamp`

Recursos

Corey Ballou
fuente
2
Increíble. Para mí, tu respuesta es la más completa. Muchas gracias.
Devner
7
En MySQL, si la versión futura cambia el tipo de datos de almacenamiento subyacente de TIMESTAMP a 64 bits, entonces no es necesario cambiar su código a DATETIME, ¿verdad? Simplemente no creo que disuadir a nadie de usar TIMESTAMP sea lo correcto porque ese tipo de datos tiene su propósito.
pixelfreak
1
El campo BIGINT firmado de MySQL parece que funcionaría bien para almacenar marcas de tiempo y he realizado algunas pruebas locales en MySQL 5.5 que confirman que funciona. Obviamente, usar firmado sería mejor que sin firmar, ya que también puede representar fechas en el pasado. ¿Alguna razón para no usar BIGINT para las marcas de tiempo?
zuallauz
Una cosa a tener en cuenta, MySQL solo tiene configuración automática a la fecha / hora actual (CURRENT_TIMESTAMP) para los campos de marca de tiempo. Con suerte, esta funcionalidad eventualmente se trasladará a todos los tipos de fechas.
Ray
5
Es absolutamente absurdo que MySQL (y MariaDB) no utilicen enteros de 64 bits para almacenar marcas de tiempo en sistemas de 64 bits. Usar DATETIME no es una solución adecuada porque no tenemos idea de la zona horaria. Esto se informó en 2005 , pero aún no hay una solución disponible.
Mike
14

Cuando usa UNIX Timestamps para almacenar fechas, en realidad está usando un entero de 32 bits, que mantiene la cuenta del número de segundos desde 1970-01-01; ver hora de Unix

Ese número de 32 bits se desbordará en 2038. Ese es el problema de 2038.


Para resolver ese problema, no debe usar una marca de tiempo UNIX de 32 bits para almacenar sus fechas, lo que significa que cuando use MySQL, no debe usar TIMESTAMP, pero DATETIME(consulte 10.3.1. Los tipos DATETIME, DATE y TIMESTAMP ):

El DATETIMEtipo se utiliza cuando necesita valores que contienen información de fecha y hora. El rango admitido es '1000-01-01 00:00:00'a '9999-12-31 23:59:59'.

El TIMESTAMPtipo de datos tiene un rango de '1970-01-01 00:00:01'UTC a '2038-01-19 03:14:07'UTC.


Lo mejor (probablemente) que puede hacer con su aplicación para evitar / solucionar ese problema es no usar TIMESTAMP, pero DATETIMEpara las columnas que deben contener fechas que no estén entre 1970 y 2038.

Sin embargo, una pequeña nota: es muy probable (estadísticamente hablando) que su aplicación haya sido reescrita un par de veces antes de 2038 ^^ Así que tal vez, si no tiene que lidiar con fechas en el futuro , no tendrá que ocuparse de ese problema con la versión actual de su aplicación ...

Pascal MARTIN
fuente
1
+1 Para la información. El intento aquí es adaptar las mejores prácticas desde el principio para que uno no tenga que preocuparse por el problema más adelante. Entonces, aunque por ahora 2038 puede no parecer un problema, solo quiero seguir las mejores prácticas y seguirlas para todas y cada una de las aplicaciones que creo, desde el principio. Espero que tenga sentido.
Devner
Sí, tiene sentido, veo tu punto. Pero "mejores prácticas" también puede significar "lo que responde a la necesidad": si sabe que una marca de tiempo será suficiente, no hay necesidad de usar una fecha y hora (por ejemplo, se necesita más memoria para almacenar; y eso puede importar si tienes millones de registros) ;; Tengo algunas aplicaciones en las que uso la marca de tiempo para algunas columnas (columnas que contienen la fecha actual, por ejemplo) y la fecha y hora para algunas otras (columnas que contienen fechas pasadas / futuras, por ejemplo)
Pascal MARTIN
Veo que su procedimiento también tiene sentido y funciona bien, especialmente cuando se trata de espacio de almacenamiento. Si está bien, ¿puedo preguntarle qué estructura y longitud usa generalmente para la columna TIMESTAMP en sus aplicaciones? Gracias.
Devner
Bueno, cuando quiero usar un TIMESTAMP, es para / porque mis datos de "fecha / hora" se ajustan entre 1970 y 2038 - y, entonces, uso el tipo de datos MySQL TIMESTAMP.
Pascal MARTIN
Gracias por la info. De su respuesta, entiendo que es suficiente con declarar la columna como TIMESTAMP y no necesitamos proporcionarle ninguna longitud (a diferencia de e int o var, donde proporcionamos la longitud). Estoy en lo cierto?
Devner
7

Una búsqueda rápida en Google hará el truco: problema del año 2038

  1. El problema del año 2038 (también conocido como Unix Millennium Bug, Y2K38 por analogía con el problema Y2K) puede hacer que algunos programas de computadora fallen antes o en el año 2038
  2. El problema afecta a todo el software y los sistemas que almacenan la hora del sistema como un entero de 32 bits con signo e interpretan este número como el número de segundos desde las 00:00:00 UTC del 1 de enero de 1970. La última hora que se puede representar de esta manera son las 03:14:07 UTC del martes 19 de enero de 2038. Los tiempos posteriores a este momento se "envolverán" y se almacenarán internamente como un número negativo, que estos sistemas interpretarán como una fecha de 1901 en lugar de 2038
  3. No hay una solución fácil para este problema para las combinaciones de CPU / SO existentes, sistemas de archivos existentes o formatos de datos binarios existentes.
Rubens Farias
fuente
2

http://en.wikipedia.org/wiki/Year_2038_problem tiene la mayoría de los detalles

En resumen:

1) + 2) El problema es que muchos sistemas almacenan la información de fecha como un int de 32 bits con signo igual al número de segundos desde el 1/1/1970. La última fecha que se puede almacenar de esta manera es a las 03:14:07 UTC del martes 19 de enero de 2038. Cuando esto suceda, el int "se ajustará" y se almacenará como un número negativo que se interpretará como una fecha de 1901. Lo que sucederá exactamente entonces varía de un sistema a otro, pero basta con decir que probablemente no sea bueno para ninguno de ellos.

Para los sistemas que solo almacenan fechas en el pasado, ¡supongo que no necesita preocuparse por un tiempo! El principal problema son los sistemas que funcionan con fechas en el futuro. Si su sistema necesita funcionar con fechas de 28 años en el futuro, ¡debería empezar a preocuparse ahora!

3) Utilice uno de los formatos de fecha alternativos disponibles o cambie a un sistema de 64 bits y utilice entradas de 64 bits. O para bases de datos, use un formato de marca de tiempo alternativo (por ejemplo, para MySQL use DATETIME)

4) ¡Ver 3!

5) Ver 4 !!! ;)

Addsy
fuente
Sus puntos 4 y 5 me recuerdan a 'Llamar por referencia'. Eso puso una sonrisa en mi cara. Gracias y +1 por lo mismo.
Devner
1

Hermanos, si necesita usar PHP para mostrar marcas de tiempo, esta es la MEJOR solución PHP sin cambiar el formato UNIX_TIMESTAMP.

Utilice una función custom_date (). Dentro de él, use el DateTime. Aquí está la solución DateTime .

Siempre que tenga SIN FIRMAR BIGINT (8) como marcas de tiempo en la base de datos. Siempre que tenga PHP 5.2.0 ++

Diestro
fuente
-1

Como no quería actualizar nada, le pedí a mi backend (MSSQL) que hiciera este trabajo en lugar de PHP.

$qry = "select DATEADD(month, 1, :date) next_date ";
$rs_tmp = $pdo->prepare($qry);
$rs_tmp->bindValue(":date", '2038/01/15');
$rs_tmp->execute();
$row_tmp = $rs_tmp->fetch(PDO::FETCH_ASSOC);

echo $row_tmp['next_date'];

Puede que no sea una forma eficiente, pero funciona.

Venkatesh GS Rao
fuente