PHP ve mySQL: Yıl 2038 Hatası: Nedir? Nasıl çözeceksin?


116

Tarih + saati saklamak için TIMESTAMP kullanmayı düşünüyordum, ancak üzerinde 2038 yılı sınırlaması olduğunu okudum. Soruyu toplu olarak sormak yerine, acemi kullanıcıların da anlayabilmesi için küçük parçalara ayırmayı tercih ettim. Öyleyse sorum (larım):

  1. 2038 yılı sorunu tam olarak nedir?
  2. Neden oluyor ve gerçekleştiğinde ne oluyor?
  3. Nasıl çözeriz?
  4. Kullanmanın benzer bir problem oluşturmayan olası alternatifleri var mı?
  5. Gerçekten ortaya çıktığında, sözde sorunu önlemek için TIMESTAMP kullanan mevcut uygulamalara ne yapabiliriz?

Şimdiden teşekkürler.


1
Hala 28 yıl var. Hala 1982'den beri bilgisayarla ilgili herhangi bir teknolojiyi kullanıyor musunuz? Olası olmayan. Bu yüzden endişelenmeyin, çünkü 2038'de muhtemelen artık bir sorun olmayacak.
Gordon

24
Gordon, tahmin yapan bir uygulama yapıyorum. Her ay ne kadar param var ve ne zaman milyoner olacağımı tahmin edebiliyorum. Benim durumumda, 28 yıl o kadar da değil ve eminim şu anda bu sorunu yaşayan tek kişi ben değilim (Zaman damgasını temsil etmek için 64 bitlik sayılar kullanarak çözdüm).
Emil Vikström

2
@Emil Şu anda 64bit tamsayılar kullanarak , kendinize somut bir soruna kolay bir çözüm buldunuz. Herkes için geçerli değil (veya herkes tarafından ihtiyaç duyulmayan), ancak sizin kullanım durumunuz için çalışıyor Demek istediğim, OP'nin tahmin gibi somut bir sorunu yoksa, bu ilginç bir konu olabilir, ancak endişelenmesi gereken bir konu değil, çünkü bunu genel düzeyde çözmek bir PHP (etikete dikkat edin) sorunu değildir. Sadece benim 2c.
Gordon

1
2038'e kadar "YYYY-AA-GG SS: DD: SS: aaa ..." dizesini ayrıştırmak hayal edebileceğiniz en ucuz işlem olacak. 2038'e kadar 32 Bit kullanımdan kalkacak. O zaman var olacağını bildiğimiz şekliyle Unix zaman damgasından şüpheliyim ve eğer öyleyse, 256 Bit sistemlerimiz, mutlu yemeklerde 4096 bit sistemlerin verildiği çağın çok ötesine geçen tarihleri ​​işleyecektir.
Süper Kedi

8
Gordon, artık 9 yıl sonra. TIMESTAMPS hala kullanılmaktadır. Hala 28 yıl önceki teknolojiyi kullanıyoruz. Buna dünya çapında ağ denir.
sfscs

Yanıtlar:


149

Bunu bir topluluk wiki olarak işaretledim, bu yüzden boş zamanlarınızda düzenlemekten çekinmeyin.

2038 yılı sorunu tam olarak nedir?

"2038 yılı sorunu (Y2K sorununa benzer şekilde Unix Millennium Bug, Y2K38 olarak da bilinir) bazı bilgisayar yazılımlarının 2038'den önce veya 2038 yılında başarısız olmasına neden olabilir. Sorun, sistem zamanını imzalı 32 olarak depolayan tüm yazılımları ve sistemleri etkiler. -bit tamsayı ve bu sayıyı 1 Ocak 1970 00:00:00 UTC'den bu yana geçen saniye sayısı olarak yorumlayın. "


Neden oluyor ve gerçekleştiğinde ne oluyor?

19 Ocak 2038 Salı günü 03:14:07 UTC'yi aşan zamanlar , bu sistemlerin 2038'den ziyade 13 Aralık 1901'deki bir zaman olarak yorumlayacağı negatif bir sayı olarak 'çevrilecek' ve dahili olarak depolanacaktır. UNIX döneminden (1 Ocak 1970 00:00:00 GMT) bu yana geçen saniye sayısının, bir bilgisayarın 32 bitlik işaretli bir tamsayı için maksimum değerini aşmış olacağı gerçeği.


Nasıl çözeriz?

  • Uzun veri türlerini kullanın (64 bit yeterlidir)
  • MySQL (veya MariaDB) için, zaman bilgisine ihtiyacınız yoksa, DATEsütun türünü kullanmayı düşünün . Daha yüksek doğruluğa ihtiyacınız varsa, DATETIMEyerine kullanın TIMESTAMP. DATETIMESütunların saat dilimi hakkında bilgi depolamadığına dikkat edin , bu nedenle uygulamanızın hangi saat diliminin kullanıldığını bilmesi gerekir.
  • Wikipedia'da açıklanan diğer olası çözümler
  • MySQL geliştiricilerinin on yıldan uzun bir süre önce bildirilen bu hatayı düzeltmesini bekleyin .

Kullanmanın benzer bir problem oluşturmayan olası alternatifleri var mı?

Veri tabanlarında tarih depolamak için mümkün olan her yerde büyük türleri kullanmayı deneyin: 64 bit yeterlidir - GNU C ve POSIX / sprintf('%u'...)SuS'de veya PHP'de veya BCmath uzantısında uzun uzun bir tür .


Henüz 2038'de olmamamıza rağmen, potansiyel olarak kırılma olasılığı olan kullanım örnekleri nelerdir?

Yani bir MySQL DATETIME 1000-9999 aralığına sahiptir, ancak TIMESTAMP yalnızca 1970-2038 aralığına sahiptir. Sisteminiz doğum tarihlerini, gelecekteki ileriye dönük tarihleri ​​(örneğin 30 yıllık ipotekler) veya benzerlerini saklarsa, bu hatayla zaten karşılaşacaksınız. Yine, bu bir sorun olacaksa TIMESTAMP'ı kullanmayın.


Gerçekten ortaya çıktığında, sözde sorunu önlemek için TIMESTAMP kullanan mevcut uygulamalara ne yapabiliriz?

2038'de az sayıda PHP uygulaması hala mevcut olacak, ancak web'in henüz eski bir platform olmadığını tahmin etmek zor.

İşte bir veritabanı tablosu sütununu dönüştürmek TIMESTAMPiçin bir işlem DATETIME. Geçici bir sütun oluşturmakla başlar:

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

kaynaklar


2
Muhteşem. Bana göre cevabınız en eksiksiz. Çok teşekkürler.
Devner

7
MySQL'de, gelecekteki sürüm TIMESTAMP'ın temel depolama veri türünü 64 bit olarak değiştirirse, kodunuzu DATETIME olarak değiştirmenize gerek yoktur, değil mi? Ben sadece kimseyi TIMESTAMP kullanmaktan vazgeçirmenin yapılacak doğru şey olduğunu düşünmüyorum çünkü bu veri türünün bir amacı var.
pixelfreak

1
MySQL'in imzalı BIGINT alanı, zaman damgalarını saklamak için iyi çalışıyor gibi görünüyor ve MySQL 5.5 üzerinde çalıştığını doğrulayan bazı yerel testler yaptım. Açıkçası, geçmişte tarihleri ​​de temsil edebileceğiniz için işaretli kullanmak işaretsiz kullanmaktan daha iyi olacaktır. Zaman damgaları için BIGINT kullanmamak için herhangi bir neden var mı?
zuallauz

Unutulmaması gereken bir şey, MySQL'in zaman damgası alanları için yalnızca geçerli tarih / saate (CURRENT_TIMESTAMP) otomatik ayarı vardır. Umarım, bu işlevsellik sonunda tüm tarih türlerine aktarılır.
Ray

5
MySQL'in (ve MariaDB'nin) 64 bitlik sistemlerde zaman damgalarını depolamak için 64 bitlik tamsayılar kullanmaması kesinlikle saçmadır. DATETIME kullanmak yeterli bir çözüm değildir çünkü saat dilimi hakkında hiçbir fikrimiz yok. Bu, 2005 yılında bildirildi , ancak hala bir düzeltme mevcut değil.
Mike

14

Tarihleri ​​saklamak için UNIX Zaman Damgalarını kullandığınızda, gerçekte, 1970-01-01'den bu yana geçen saniye sayısını tutan 32 bitlik tamsayılar kullanıyorsunuz; Unix Zamanına bakın

Bu 32 bitlik sayı 2038'de taşacak. 2038 problemi bu.


Bu sorunu çözmek için, tarihlerinizi saklamak için 32 bitlik bir UNIX zaman damgası kullanmamalısınız - bu, MySQL kullanırken kullanmamalısınız TIMESTAMP, ancak DATETIME(bkz. 10.3.1. DATETIME, DATE ve TIMESTAMP Türleri ):

DATETIMETarih ve saat bilgilerini her ikisini de içerir değerleri gerektiğinde tip kullanılır. Desteklenen aralığı '1000-01-01 00:00:00'için '9999-12-31 23:59:59'.

TIMESTAMPVeri tipi yelpazesine sahiptir '1970-01-01 00:00:01'için UTC '2038-01-19 03:14:07'UTC.


(Muhtemelen) Eğer sorun kullanmayın olduğunu önlemek / düzeltme için uygulama için yapabileceğiniz en iyi şey TIMESTAMP, ancak DATETIME1970 ve 2038 arasında olmayan tarihleri içermek zorunda sütunlar için.

Yine de küçük bir not: Başvurunuzun 2038'den önce epeyce yeniden yazılması ihtimali çok yüksek (istatistiksel olarak konuşulduğunda) ^^ Yani belki, gelecekte tarihlerle uğraşmak zorunda kalmazsanız , uygulamanızın mevcut sürümüyle bu sorunu halletmeniz gerekmeyecek ...


1
+1 Bilgi için. Buradaki girişim, en iyi uygulamaları en baştan uyarlamaktır, böylece daha sonra sorun hakkında endişelenmenize gerek kalmaz. Bu nedenle, şimdilik 2038 bir sorun gibi görünmese de, en iyi uygulamaları takip etmek ve bunu en başından itibaren oluşturduğum her ve her uygulama için takip etmek istiyorum. Umarım mantıklıdır.
Devner

Evet, mantıklı, ne demek istediğini anlıyorum. Ancak "en iyi uygulama" aynı zamanda "ihtiyaca cevap veren" anlamına da gelebilir - Bir zaman damgasının yeterli olacağını biliyorsanız, bir tarih saati kullanmaya gerek yoktur (örneğin, depolanması için daha fazla belleğe ihtiyaç duyar; milyonlarca kaydınız var) ;; Bazı sütunlar için zaman damgası (örneğin, güncel tarihi içeren sütunlar) ve diğerleri için (örneğin geçmiş / gelecek tarihleri ​​içeren sütunlar) zaman damgası kullandığım bazı uygulamalarım var
Pascal MARTIN

Prosedürünüzün de mantıklı olduğunu ve özellikle depolama alanıyla ilgili olarak iyi çalıştığını görüyorum. Eğer uygunsa, uygulamalarınızda TIMESTAMP sütunu için genel olarak hangi yapı ve uzunluğu kullandığınızı sorabilir miyim? Teşekkürler.
Devner

Bir TIMESTAMP kullanmak istediğimde, "tarih / saat" verilerim 1970 ile 2038 arasına sığdığı için / ve bu yüzden MySQL TIMESTAMP veri türünü kullanıyorum.
Pascal MARTIN

Bilgi için teşekkürler. Cevabınızdan, sütunu sadece TIMESTAMP olarak bildirmenin yeterli olduğunu ve bunun için herhangi bir uzunluk belirtmemize gerek olmadığını anlıyorum (uzunluğu sağladığımız yerde int veya var gibi). Haklı mıyım
Devner

7

Google'da hızlı bir arama hile yapacak: 2038 yılı sorunu

  1. 2038 yılı problemi (Y2K problemine benzetilerek Unix Millennium Bug, Y2K38 olarak da bilinir) bazı bilgisayar yazılımlarının 2038'den önce veya 2038 yılında başarısız olmasına neden olabilir.
  2. Sorun, sistem saatini işaretli 32 bit tam sayı olarak depolayan tüm yazılımları ve sistemleri etkiler ve bu sayıyı 1 Ocak 1970 00:00:00 UTC'den bu yana geçen saniye sayısı olarak yorumlar. Bu şekilde gösterilebilecek en son saat 19 Ocak 2038, Salı, 03:14:07 UTC. Bu anın ötesindeki zamanlar "çevrelenecek" ve dahili olarak negatif bir sayı olarak saklanacak ve bu sistemler bunu 2038 yerine 1901'de bir tarih olarak yorumlayacak.
  3. Mevcut CPU / OS kombinasyonları, mevcut dosya sistemleri veya mevcut ikili veri formatları için bu soruna yönelik kolay bir düzeltme yoktur.

2

http://en.wikipedia.org/wiki/Year_2038_problem ayrıntıların çoğuna sahiptir

Özetle:

1) + 2) Sorun, birçok sistemin tarih bilgisini 1/1 / 1970'den bu yana geçen saniye sayısına eşit olarak 32 bitlik işaretli int olarak depolamasıdır. Bu şekilde saklanabilecek en son tarih 19 Ocak 2038 Salı günü 03:14:07 UTC'dir. Bu olduğunda int "etrafını saracak" ve 1901'de tarih olarak yorumlanacak negatif bir sayı olarak saklanacaktır. O zaman tam olarak ne olacak, sistemden sisteme değişir, ancak muhtemelen bunların hiçbiri için iyi olmayacağını söylemek yeterlidir!

Sadece geçmişte tarih depolayan sistemler için, sanırım bir süre endişelenmenize gerek yok! Ana sorun, gelecekte tarihlerle çalışan sistemlerdir. Sisteminizin 28 yıl sonraki tarihlerle çalışması gerekiyorsa, şimdi endişelenmeye başlamalısınız!

3) Mevcut alternatif tarih biçimlerinden birini kullanın veya 64 bit sisteme geçin ve 64 bit ints kullanın. Veya veritabanları için alternatif bir zaman damgası biçimi kullanın (ör. MySQL için DATETIME kullanın)

4) 3'e bakın!

5) Bkz. 4 !!! ;)


Puanlarınız 4 ve 5 bana 'Referansla Arama'yı hatırlatıyor. Bu yüzüme bir gülümseme koydu. Aynı için teşekkürler ve +1.
Devner

1

Bros, zaman damgalarını görüntülemek için PHP kullanmanız gerekiyorsa, bu UNIX_TIMESTAMP biçiminden değişmeden EN İYİ PHP çözümüdür.

Bir custom_date () işlevi kullanın. İçinde DateTime'ı kullanın. İşte DateTime çözümü .

Veritabanında zaman damgalarınız olarak UNSIGNED BIGINT (8) olduğu sürece. PHP 5.2.0 ++ 'a sahip olduğunuz sürece


-1

Hiçbir şeyi yükseltmek istemediğim için arka ucumdan (MSSQL) bu işi PHP yerine yapmasını istedim!

$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'];

Etkili bir yol olmayabilir ama işe yarıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.