暦週の基準年について
1,はじめに
昨日Twitterを流し見していたところ、Javaの日付の書式について興味深いTweetがあった。
それは、暦週の基準年という考え方についてであった。
暦週の基準年とは、新年度の1月1日と同じ週に属する日については、新年度に属するとする考え方である(ISO 8601, 2.3.暦週日付)。
この日付の仕様が、私が使用している他の言語(Perl)ではどうなのかについて、調べてみたい。
2,暦週の基準年
(1)何が問題なのか
以下がJavaの日付の特殊な書式について発見をした方のTweetである。
まずは、問題の再現を試みたい。
筆者の環境は下記の通りである。
R:\>javac -version
javac 1.8.0_152
以上の環境で、先述のJavaの仕様を確認できたコードは下記の通りである。
TestDataFormat.java
/*
* TestDateFormat
* 2018/12/31
* jskny
*/
import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.ParseException;
public class TestDateFormat {
public static void main(String[] args) {
Date tDate = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
// 暦週の基準年を使用する場合、
// 年の指定の「y」を小文字の「y」から大文字の「Y」にする。
SimpleDateFormat sdf2 = new SimpleDateFormat("YYYY/MM/dd");
System.out.println("===== Data Format Test =====");
// 現在の日時を表示
System.out.println("@ Running Date");
System.out.println(tDate);
// 指定日時のセットとその表示 (2018/1/1)
System.out.println("@ Selected Date (2018/1/1)");
try {
tDate = sdf.parse("2018/1/1");
}
catch (ParseException e) {
e.printStackTrace();
}
System.out.println(tDate);
System.out.println("");
System.out.println("[NORMAL CASE]");
// 問題となる暦週の基準年(正常変換の場合)
System.out.println("@ Figure yyyy (2018/12/31)");
try {
tDate = sdf.parse("2018/12/31");
}
catch (ParseException e) {
e.printStackTrace();
}
System.out.println(tDate);
System.out.println("[ABNORMAL CASE]");
System.out.println("@ Figure YYYY (2018/12/31)");
try {
tDate = sdf2.parse("2018/12/31");
}
catch (ParseException e) {
e.printStackTrace();
}
System.out.println(tDate);
// 2018/12/31 12:39
// 暦週の基準年の方は、年が2018ではなく2019になると思っていたが、
// 2017として出力されていた。前年がなぜ出力されるのだろうか。
/* 出力結果
[NORMAL CASE]
@ Figure yyyy (2018/12/31)
Mon Dec 31 00:00:00 JST 2018
[ABNORMAL CASE]
@ Figure YYYY (2018/12/31)
Sun Dec 31 00:00:00 JST 2017
*/
System.out.println("====================");
try {
tDate = sdf.parse("2018/12/31");
System.out.println("[NORMAL CASE 2]");
System.out.println(sdf.format(tDate));
System.out.println("[ABNORMAL CASE 2]");
System.out.println(sdf2.format(tDate));
}
catch (ParseException e) {
e.printStackTrace();
}
// 問題の再現確認 2018/12/31 12:44
/* 出力結果
[NORMAL CASE 2]
2018/12/31
[ABNORMAL CASE 2]
2019/12/31
*/
}
}
出力結果
R:\>java TestDateFormat
===== Data Format Test =====
@ Running Date
Mon Dec 31 12:45:18 JST 2018
@ Selected Date (2018/1/1)
Mon Jan 01 00:00:00 JST 2018
[NORMAL CASE]
@ Figure yyyy (2018/12/31)
Mon Dec 31 00:00:00 JST 2018
[ABNORMAL CASE]
@ Figure YYYY (2018/12/31)
Sun Dec 31 00:00:00 JST 2017
====================
[NORMAL CASE 2]
2018/12/31
[ABNORMAL CASE 2]
2019/12/31
以上のように、暦週の基準年が使用された場合、新年の1月1日と同じ週に属する12月31等は、2018年ではなく、2019年として処理されている。
(2)Perlの場合
この暦週の基準年という日付処理は、他の言語でも取り入れられている考え方だろうか。
まずは、Perlの日付処理で試してみたい。
結論から言えば、標準モジュールには搭載されていないと思われるが、よくわからない。
TestDataFormat.pl
# Perlでの日付処理
# 2018/12/31
# jskny
use strict;
use warnings;
# Perl 5.10 から標準搭載
# 詳しい使用方法等につき、下記サイトをご確認ください。
# http://d.hatena.ne.jp/perlcodesample/20091105/1246274997
use Time::Piece;
# Time::Pieceオブジェクトの取得
my $t = localtime;
# 現在日時の表示
print $t->datetime . "\n";
# 指定日時のセット
$t = Time::Piece->strptime("2018/1/1", "%Y/%m/%d");
print $t->datetime . "\n";
print "[NORMAL CAEE]\n";
$t = Time::Piece->strptime("2018/12/31", "%Y/%m/%d");
print $t->datetime . "\n";
# モジュールがあるのかよくわからないので、手動で実装すればよい。
# 次の年度の1月1日が月曜日でない場合、
# 1/1の曜日から何日減算すれば月曜日になるかを計算し、
# その減算値分、前年度の12月末日から新年度に属する日数を算出する。
print "[ABNORMAL CAEE]\n";
$t = Time::Piece->strptime($t->year + 1 . "/1/1", "%Y/%m/%d");
# 1が日曜日
my $tmp = $t->wday;
if ($tmp eq 1) {
# ISO 8061 的には月曜日からのカウント開始なので、
# 日曜日を最後に持ってくる。
$tmp = 8;
}
if ($tmp eq 2) {
print "年の更新の必要はありません。" . "\n";
}
else {
# 1/1の週初めが月曜日ではないため、12月末日からさかのぼる数日が、
# 新年度に属する日を出す。
$tmp -= 2;
for (my $i = 0; $i strptime("2018/12/" . (31 - $i), "%Y/%m/%d");
print $t->year . "/" . $t->mon . "/" . $t->mday . " is [2019]\n";
}
}
出力結果
R:\>perl TestDateFormat.pl
2018-12-31T13:46:36
2018-01-01T00:00:00
[NORMAL CAEE]
2018-12-31T00:00:00
[ABNORMAL CAEE]
2018/12/31 is [2019]
3,おわりに
これで最悪の場合、年末がつぶれるエンジニアが出るかと思うと、大文字の「Y」と小文字の「y」に違いを持たせたJavaの作者たちは正気を失っていると思う。
せめて別な文字にしてほしいな。
最近のコメント