猫も杓子も記事を書く

140文字ではかけないことをかこうと思います。

Javaの日付処理のはなし

最近、とある客先に配属されて仕事しているのですが、勤怠の登録システムがすこぶるめんどくさいのです。

「月末に出退勤時間書いたCSV渡すからそれ見て勤務時間計算して日報データに書いて!その勤務時間とDBの勤怠時間が合ってるかどうか確認して!」 って・・・
仮にもIT企業なんだからそれぐらい自動でやってくれてもよかろうに・・・というかDBの勤怠時間を使わせてもらうわけにはいかないのか・・・ 15分刻みで計算とか、いろいろ計算条件も多くて、月末忙しい時にこれをやらされるのは手間です。

こういう時、プログラマーはどうするか。 真面目に計算する人もいますが、そんな時間は身にならないし、何よりめんどくさい。

というわけで、計算プログラムを自作するのです。

CalcWorkingTime.java

Java案件担当中なので、Javaで書きました。 1,2時間でさくっと作るつもりが、丸一日ちかくかかってしまった・・・
まあ今は暇を持て余している状況なので、いい暇つぶしにはなりましたが。
(変数名とか大目に見てね・・・)

PHPでは似たようなことしたことあったんですが、Javaは時間の計算がすこぶるめんどくさいですねー。
今回は勤怠システムなので、記録される時間に合わせて生のデータに手を加えなければいけなくて、型変換とか考えるの大変でした。半分くらいIDEのおかげです。 まあ、いい勉強になったということで・・・
ちなみに上司はVBAでやってました。それ正解。ぼくできないけど、VBA

・・・そして、このプログラムを読んで、ふと気になったことがひとつ。
このプログラムでも使っているgetTime()あたりのメソッドは、UNIX時間にもとづいて時間を取得していますので、
いわゆる、"2038年問題"とかどうなんだろう、とか思ったわけです。

2038年問題とは、乱暴な言い方をすれば、UNIX時間から経過した秒数が、プログラムの中で扱える範囲を2038年に超えてしまうため、 システムの時間がおかしくなっちゃうよ、ということです。
そして、その影響をもろに受けるのがC言語。 若干古めの言語では有りますが、まだまだ現役ですし、
日本のコンピュータシステムは、根っこの部分でC言語を使っている(金融系とかそうじゃないかな?)のも多いので、 社会インフラ混乱するんじゃね?とか言われていたりするんです。
まあいろいろ対策はあったりするみたいですけどね。詳しく知りたい方はウィキってみるなどしてみてください。

んで調べてみたら、やはりUNIX時間なので、このメソッドで取れる時間にも限界があるみたいです。
え?いつかって?
・・・2億9千万年後に。

2億9千万年問題 ‐ 通信用語の基礎知識

・・・まあ、考える必要も無いレベルの次元の話ですし、
そもそももし2038年におかしくなるとしても、そこまで動かすプログラムを書いたつもりもないですし・・・
ただ、さっきも書いたとおり、Javaってほんとに日付・時刻の計算がめんどくさいので、
なんかないかなーと思っていたところ・・・

見た瞬間、目からウロコがぽろぽろ落ちてきました。
Javaってこんなんあるんやん!
ぼくが調べた時にはDateクラスとかCalendarクラスの記事しか出てこなかったので、完全に盲点でした・・・

acro-engineer.hatenablog.com

この新API(といっても3年近く前ですが)、Oracleにとってかなりの肝いりのようで、従来の日付関係のクラスとは比べ物にならないほど進化・充実しています。
とはいえ、基本的な使い方は従来のクラスと変わらないので、学習コストもそれほどかかりません。
イミュータブル(変数に入れた値が変わらない)でスレッドセーフ(同時に複数同じメソッドを実行しても互いの処理結果に影響しない)のもよい!

というわけで、調べながら、プログラムを新API仕様に書き直して完成させる、はずだったのですが、

CalcWorkingTimeWithNewAPI.java

Exception in thread "main" java.time.format.DateTimeParseException: Text '9:15' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 09:15 of type java.time.format.Parsed
    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
    at java.time.LocalDateTime.parse(LocalDateTime.java:492)
    at DateTimeAPIPractice.CalcWorkingTimeWithNewAPI.main(CalcWorkingTimeWithNewAPI.java:105)

・・・絶賛詰んでます。
APIの一つであるDateTimeFormatterクラスが結構曲者で、 パターンの判定がけっこう厳格なんですよねー。
とはいえ、条件を緩やかにしても変わらないし・・・
エラーメッセージで調べても、英語のstackoverflowばっかりだし、
内容も

Javaのバグやで!最新のJavaダウンロードしな!」(今使ってるのもうそれよりだいぶ後なんですけど・・・)

みたいなのもあって、うーんって感じです。
日付系の処理は今後も間違いなく使うので、この機会にすっぽり覚えてしまいたいんですけどね。うまくいかないなあ。


6/24追記

試行錯誤して、なんとか動くところまではきました。
実際のところどうなっているのかは不明ですが、時:分形式のFormatterにこだわるのをやめて、

DateTimeFormatter csvFormat2 = DateTimeFormatter.ofPattern("yyyy/MM/dd []H:[]m");

LocalDateTime shukkintime = LocalDateTime.parse(time1, csvFormat2);

こうしました。そうしたら認識してくれました。
H:mではなぜparseできなかったのか、は謎のままです。うーむ。

あと、意地悪な上司に言われたのですが、 生データをいじられるということを想定してなかったので、DateTimeParceExceptionを書いてませんでした。
客先の勤怠時間を記録するDBには正しい時間が入っているはずなので、勿論ここをいじったぐらいで実際の勤怠時間を改ざんすることはできませんが、止まっちゃうのも嫌なので、手元のコードはExceptionを追記して使っています。