【UNIX】ファイルの最終更新日時の限界(2038年問題)と、javaによる変更サンプル(と、小話)


UNIX系OSには、ファイルやディレクトリのタイムスタンプに限界値が設けられており、それを超えると変な日時に逆転してしまう。
⇒いわゆる「2038年問題」にあたる
UTC+9:00の日本において、その限界値は2038年1月19日12時14分頃である。
これを超える(これより未来の)日付を与えて変更しようとすると、
変更自体は可能だが変更結果が1901年12月14日等の変な日時になってしまう。
なお、touchコマンドは対応していない(2039年に変更しようとするとエラーになる)。


 


 
例えばjavaで以下のように実装すると、ファイル「aaa.txt」の最終更新日時を変更することができる。

// ①Fileオブジェクト他を用意  
private static final File TEST_FILE = new File("aaa.txt");  
private static final DateFormat YYYYMMDDHHMM = new SimpleDateFormat("yyyyMMddHHmm");  
private static final DateFormat YYYYMMDDHHMMSS_FOR_PRINT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  

// ②変更前日時を標準出力
Date date = new Date(TEST_FILE.lastModified());
System.out.println("*** before:");
System.out.println(TEST_FILE.getName() + " last modified : " + YYYYMMDDHHMMSS_FOR_PRINT.format(date));

// ③変更
Date modDate = YYYYMMDDHHMM.parse(“201411281935”); // 2014年11月28日19時35分
TEST_FILE.setLastModified(modDate.getTime());

// ④変更前日時を標準出力
date = new Date(TEST_FILE.lastModified());
archives: [" new"," new Da"] System.out.println("*** after:");
System.out.println(TEST_FILE.getName() + " last modified : " + YYYYMMDDHHMMSS_FOR_PRINT.format(date));


これの実行結果は以下のようになる。

*** before:  
aaa.txt last modified : 2013/03/19 04:04:00  
*** after:  
aaa.txt last modified : 2014/11/28 19:35:00  


が、変更後の日時を例えば2999年12月31日23時59分(299912312359)に変更すると

*** before:  
aaa.txt last modified : 2014/11/28 19:35:00  
*** after:  
aaa.txt last modified : 1911/03/09 20:12:52 ←こんな感じで逆転してしまう   


となって求めている日時への変更ができない。

上の実装-つまりFile#setLastModifiedによる日時の変更は、
UNIX系OSで動作させる分にはエラーも何も出力しないで正常に変更できてしまうので、
一見するとうまくいったかのように見えるけど当然求めている結果にはなっていない。
変更するより前に自前でチェックロジックを組んで弾く等、個別の仕様の考慮が必要になるだろう。

ちなみにこの制限はUNIX系OSだけのもので、Windowsでは起きない(例え9999年12月31日23時59分でも変更できる)



この問題は、実は自分で実際に体験したことがある。

(1)画面からPDFファイルをアップロードして、UNIX系OSのサーバ上に格納する機能がある
(2)エンドユーザーは画面からそのPDFファイルに通じる記事詳細データ(的なもの)を参照し、DLすることができる。
(3)管理者ユーザーはPDFファイルのアップロードと共に、その記事データの「公開期間」を日時指定できる。
(4)「公開期間」を過ぎた記事データは画面上から参照できなくなる
(5)「公開期間」を過ぎた記事データと、それに紐づくPDFファイルは、専用の夜間バッチ処理にて削除する
↑簡単にいうとこういう仕組みを作った。
そしたら「アップロードした翌日にもう消えててPDFファイルまでたどり着けない!」という問題が発生した。
この原因は、運用上(3)で入力する日付を「2999/12/31」にしていたためだった。
内部的に、PDFファイルの最終更新日時をFile#setlastModifiedにより「2999/12/31」に変更していたので、
↑に載せた例に従いサーバ上では「1911/3/9」に変更されていた。
一方記事データ(DB)に持つ公開期間はこの制限に寄らないので、ちゃんと「2999/12/31」になっている。
本来同一であるはずのDBとPDFファイルの「公開期間」が、DB=2999/12/31、PDFファイル=1911/3/9で不整合となって、
(5)の夜間処理でPDFファイルの方だけ先に消えてしまって見えなくなっていた、
というのが事の真相であった。

(3)で管理者ユーザーが画面で入力する公開期間はWEBシステム上で手入力するものであり、
ここにはUNIX系OSの制限のことなど一切考慮になかったので(しておくべきだったが)、
どんな未来の日付も入力することが出来る。
システム全体の運用連絡等の目的に使用する記事は、半永久的に公開しておきたいといった要望があり、
このような記事の登録を許可するための実装だったが、
2038年になる前に2038年問題に遭遇するとは思わなかった。

チェックロジックを組み込むことも考えたが、
顧客側で運用回避をしてもらう(2029年とかにして凌ぐ)ことでこの件は解決に至った。
UNIX系OSでサーバ上にファイルを格納しておくような仕組みでは、
同じような課題に遭遇する人もいるのではないだろうか。
(俺だけなのかな…)