PHP e mySQL: Anno 2038 Bug: che cos'è? Come risolverlo?


116

Stavo pensando di utilizzare TIMESTAMP per memorizzare la data e l'ora, ma ho letto che c'è una limitazione dell'anno 2038 su di esso. Invece di porre la mia domanda in blocco, ho preferito suddividerla in piccole parti in modo che fosse facile da capire anche per gli utenti inesperti. Quindi la mia domanda (e):

  1. Qual è esattamente il problema dell'anno 2038?
  2. Perché si verifica e cosa succede quando si verifica?
  3. Come lo risolviamo?
  4. Esistono alternative possibili al suo utilizzo che non pongono un problema simile?
  5. Cosa possiamo fare alle applicazioni esistenti che utilizzano TIMESTAMP, per evitare il cosiddetto problema, quando si verifica realmente?

Grazie in anticipo.


1
Mancano ancora 28 anni. Utilizzi ancora tecnologie informatiche del 1982? Improbabile. Quindi non preoccuparti, perché entro il 2038 probabilmente non sarà più un problema.
Gordon

24
Gordon, creo un'applicazione che fa previsioni. Risparmio quanti soldi ho ogni mese e posso quindi stimare quando sarò un milionario. Nel mio caso, 28 anni non sono molti e sono sicuro di non essere l'unico ad avere questo problema in questo momento (l'ho risolto utilizzando numeri a 64 bit per rappresentare il timestamp).
Emil Vikström

2
@ Emil Usando gli interi a 64 bit in questo momento , ti sei trovato una soluzione facile a un problema concreto. Non applicabile a (o necessario a) tutti, ma funziona per il tuo caso d'uso. Il punto è che se l'OP non ha un problema concreto, come la previsione, allora questo potrebbe essere un argomento interessante, ma niente di cui dovrebbe preoccuparsi, perché risolverlo a livello generale non è un problema PHP (attenzione al tag). Solo il mio 2c.
Gordon

1
Entro il 2038, analizzare la stringa "AAAA-MM-GG HH: MM: SS: mmmm ..." sarà l'operazione più economica che puoi immaginare. Entro il 2038, 32 bit sarà obsoleto. Dubito che il timestamp Unix come sappiamo che esisterà allora, e se lo farà, i nostri sistemi a 256 bit gestiranno date che vanno ben oltre l'età in cui i sistemi a 4096 bit vengono distribuiti nei pasti felici.
Super Cat

8
Gordon, sono passati 9 anni ormai. I TIMESTAMPS sono ancora utilizzati. Usiamo ancora la tecnologia di 28 anni fa. Si chiama World Wide Web.
sfscs

Risposte:


149

L'ho contrassegnato come wiki della comunità, quindi sentiti libero di modificarlo a tuo piacimento.

Qual è esattamente il problema dell'anno 2038?

"Il problema dell'anno 2038 (noto anche come Unix Millennium Bug, Y2K38 per analogia al problema Y2K) potrebbe causare il malfunzionamento di alcuni software per computer prima o durante l'anno 2038. Il problema riguarda tutti i software e i sistemi che memorizzano l'ora di sistema come 32 -bit integer e interpreta questo numero come il numero di secondi trascorsi dalle 00:00:00 UTC del 1 gennaio 1970. "


Perché si verifica e cosa succede quando si verifica?

Gli orari oltre le 03:14:07 UTC di martedì 19 gennaio 2038 verranno "riassunti" e verranno memorizzati internamente come un numero negativo, che questi sistemi interpreteranno come un orario del 13 dicembre 1901 anziché del 2038. Ciò è dovuto a il fatto che il numero di secondi dall'epoca UNIX (1 gennaio 1970 00:00:00 GMT) avrà superato il valore massimo di un computer per un intero con segno a 32 bit.


Come lo risolviamo?

  • Usa tipi di dati lunghi (64 bit sono sufficienti)
  • Per MySQL (o MariaDB), se non hai bisogno delle informazioni sull'ora, considera l'utilizzo del DATEtipo di colonna. Se è necessaria una maggiore precisione, utilizzare DATETIMEinvece di TIMESTAMP. DATETIMEFai attenzione che le colonne non memorizzano informazioni sul fuso orario, quindi la tua applicazione dovrà sapere quale fuso orario è stato utilizzato.
  • Altre possibili soluzioni descritte su Wikipedia
  • Attendi che gli sviluppatori di MySQL correggano questo bug segnalato più di dieci anni fa.

Esistono alternative possibili al suo utilizzo che non pongono un problema simile?

Cerca, ove possibile, di utilizzare caratteri grandi per memorizzare le date nei database: 64 bit sono sufficienti: un tipo lungo lungo in GNU C e POSIX / SuS, o sprintf('%u'...)in PHP o l'estensione BCmath.


Quali sono alcuni casi d'uso potenzialmente dannosi anche se non siamo ancora nel 2038?

Quindi un MySQL DATETIME ha un intervallo di 1000-9999, ma TIMESTAMP ha solo un intervallo di 1970-2038. Se il tuo sistema memorizza date di nascita, date future future (ad esempio mutui a 30 anni) o simili, ti imbatterai già in questo bug. Di nuovo, non utilizzare TIMESTAMP se questo sarà un problema.


Cosa possiamo fare alle applicazioni esistenti che utilizzano TIMESTAMP, per evitare il cosiddetto problema, quando si verifica realmente?

Poche applicazioni PHP saranno ancora in circolazione nel 2038, anche se è difficile prevedere poiché il Web non è ancora una piattaforma legacy.

Qui è un processo per alterare una colonna della tabella di database per la conversione TIMESTAMPa DATETIME. Inizia con la creazione di una colonna temporanea:

# 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`

risorse


2
Eccezionale. Per me, la tua risposta è la più completa. Grazie mille
Devner

7
In MySQL, se la versione futura modifica il tipo di dati di archiviazione sottostante di TIMESTAMP a 64 bit, non è necessario modificare il codice in DATETIME, giusto? Semplicemente non penso che scoraggiare nessuno dall'usare TIMESTAMP sia la cosa giusta da fare perché quel tipo di dati ha il suo scopo.
pixelfreak

1
Il campo BIGINT firmato di MySQL sembra che funzionerebbe bene per la memorizzazione dei timestamp e ho eseguito alcuni test locali su MySQL 5.5 che conferma che funziona. Ovviamente usare firmato sarebbe meglio che non firmato in quanto puoi rappresentare anche date nel passato. Qualche motivo per non usare BIGINT per i timestamp?
zuallauz

Una cosa da notare, MySQL ha solo l'impostazione automatica di data / ora corrente (CURRENT_TIMESTAMP) per i campi timestamp. Si spera che questa funzionalità finirà per portare a tutti i tipi di data.
Ray

5
È assolutamente assurdo che MySQL (e MariaDB) non utilizzino numeri interi a 64 bit per memorizzare i timestamp su sistemi a 64 bit. L'utilizzo di DATETIME non è una soluzione adeguata perché non abbiamo idea del fuso orario. Questo è stato segnalato nel lontano 2005 , ma non è ancora disponibile alcuna correzione.
Mike

14

Quando si utilizzano i timestamp UNIX per memorizzare le date, si sta effettivamente utilizzando un numero intero a 32 bit, che tiene il conteggio del numero di secondi dal 1970-01-01; vedi Unix Time

Quel numero di 32 bit andrà in overflow nel 2038. Questo è il problema del 2038.


Per risolvere questo problema, non devi usare un timestamp UNIX a 32 bit per memorizzare le tue date - il che significa che, quando usi MySQL, non dovresti usare TIMESTAMP, ma DATETIME(vedi 10.3.1. I tipi DATETIME, DATE e TIMESTAMP ):

Il DATETIMEtipo viene utilizzato quando sono necessari valori che contengono informazioni sia sulla data che sull'ora. L'intervallo supportato è '1000-01-01 00:00:00'quello di '9999-12-31 23:59:59'.

Il TIMESTAMPtipo di dati ha un intervallo da '1970-01-01 00:00:01'UTC a '2038-01-19 03:14:07'UTC.


La cosa (probabilmente) migliore che puoi fare alla tua applicazione per evitare / risolvere quel problema è non usarla TIMESTAMP, ma DATETIMEper le colonne che devono contenere date che non sono comprese tra il 1970 e il 2038.

Una piccola nota, però: c'è molto probabilmente (statisticamente parlando) che la tua applicazione sarà stata riscritta un paio di volte prima del 2038 ^^ Quindi forse, se non hai a che fare con le date in futuro , non dovrai occuparti di quel problema con la versione corrente della tua applicazione ...


1
+1 Per le info. Il tentativo qui è di adattare le migliori pratiche fin dall'inizio in modo da non doversi preoccupare del problema in seguito. Quindi, anche se per ora il 2038 potrebbe non sembrare un problema, voglio solo seguire le migliori pratiche e seguirle per ogni applicazione che creo, fin dall'inizio. Spero che abbia un senso.
Devner

Sì, ha senso, capisco il tuo punto. Ma "best practice" può anche significare "ciò che risponde al bisogno": se sai che un timestamp sarà sufficiente, non è necessario utilizzare un datetime (ad esempio, è necessario più memoria per essere archiviato; e questo può essere importante se hai milioni di record) ;; Ho alcune applicazioni in cui utilizzo il timestamp per alcune colonne (colonne che contengono la data corrente, ad esempio) e datetime per alcune altre (colonne che contengono date passate / future, ad esempio)
Pascal MARTIN

Vedo che anche la tua procedura ha senso e funziona bene soprattutto per quanto riguarda lo spazio di archiviazione. Se va bene, posso chiederti quale struttura e lunghezza usi generalmente per la colonna TIMESTAMP nelle tue applicazioni? Grazie.
Devner

Bene, quando voglio usare un TIMESTAMP, è per / perché i miei dati di "data / ora" rientrano tra il 1970 e il 2038 e, quindi, utilizzo il tipo di dati MySQL TIMESTAMP.
Pascal MARTIN

Grazie per le informazioni. Dalla tua risposta, capisco che è sufficiente dichiarare la colonna come TIMESTAMP e non è necessario fornire alcuna lunghezza per essa (a differenza di e int o var, dove forniamo la lunghezza). Ho ragione?
Devner

7

Una rapida ricerca su Google farà il trucco: problema anno 2038

  1. Il problema dell'anno 2038 (noto anche come Unix Millennium Bug, Y2K38 per analogia al problema Y2K) può causare il malfunzionamento di alcuni software per computer prima o durante l'anno 2038
  2. Il problema interessa tutti i software e i sistemi che memorizzano l'ora di sistema come numero intero a 32 bit con segno e interpretano questo numero come il numero di secondi trascorsi dalle 00:00:00 UTC del 1 gennaio 1970. L'ultima ora che può essere rappresentata in questo modo è le 03:14:07 UTC di martedì 19 gennaio 2038. I tempi oltre questo momento verranno "riassunti" e verranno memorizzati internamente come un numero negativo, che questi sistemi interpreteranno come una data nel 1901 anziché nel 2038
  3. Non esiste una soluzione semplice per questo problema per le combinazioni CPU / OS esistenti, i file system esistenti o i formati di dati binari esistenti

2

http://en.wikipedia.org/wiki/Year_2038_problem contiene la maggior parte dei dettagli

In sintesi:

1) + 2) Il problema è che molti sistemi memorizzano le informazioni sulla data come int con segno a 32 bit uguale al numero di secondi dall'1 / 1/1970. L'ultima data che può essere memorizzata in questo modo è 03:14:07 UTC di martedì 19 gennaio 2038. Quando ciò accade, l'int "va a capo" e viene memorizzato come un numero negativo che verrà interpretato come una data nel 1901. Quello che succederà esattamente allora, varia da sistema a sistema, ma è sufficiente dire che probabilmente non andrà bene per nessuno di loro!

Per i sistemi che memorizzano solo date nel passato, quindi immagino che non devi preoccuparti per un po '! Il problema principale è con i sistemi che funzionano con le date future. Se il tuo sistema deve funzionare con date di 28 anni in futuro, dovresti iniziare a preoccuparti ora!

3) Utilizzare uno dei formati di data alternativi disponibili o passare a un sistema a 64 bit e utilizzare int a 64 bit. Oppure per i database utilizzare un formato di timestamp alternativo (ad es. Per MySQL utilizzare DATETIME)

4) Vedi 3!

5) Vedi 4 !!! ;)


I tuoi punti 4 e 5 mi ricordano "Call by Reference". Questo mi ha fatto sorridere. Grazie e +1 per lo stesso.
Devner

1

Fratelli, se devi usare PHP per visualizzare i timestamp, questa è la MIGLIORE soluzione PHP senza cambiare dal formato UNIX_TIMESTAMP.

Usa una funzione custom_date (). Al suo interno, usa DateTime. Ecco la soluzione DateTime .

Finché hai UNSIGNED BIGINT (8) come timestamp nel database. Finché hai PHP 5.2.0 ++


-1

Dato che non volevo aggiornare nulla, ho chiesto al mio backend (MSSQL) di fare questo lavoro invece di 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'];

Potrebbe non essere un modo efficiente, ma funziona.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.