冒頭
現場のPJでの詳細設計にて以下のような記載をしました。
「○○日より1年前の日付を取得する。」
特定の日付を基準にして、その前後の日付を取得するというよくある処理だと思うのですが
レビューにて「うるう年の一年前の日付を取得した場合どうなりますか?」と質問があり、私もふと疑問に思いました。
「うるう年の一年前の日付を取得したら、前年の3月1日なのか?それとも2月28日なのか?」
今回の記事はそんな疑問から始まった記事であり、実際にいくつかの言語、DBで試してみた結果を書いていきたいと思います。
Javaの場合
Javaにて日付の計算を行う場合、Calendarクラスを使用します。Caleandarクラスのインスタンスを生成し、生成したインスタンスのsetTimeメソッドの引数にDate型を指定することで日時の設定ができます。そしてaddメソッドにて第一引数にCalendar.YEAR、第二引数にint型(加算の場合は正の数、減算の場合は負の数)を指定することで日付の計算が可能となります。
以下がサンプルコードになります。
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;
public class Main {
public static void main(String[] args) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date1 = sdf.parse("2020/02/29");
Calendar calendar = Calendar.getInstance();
calendar.setTime(date1);
calendar.add(Calendar.YEAR, -1);
Date date2 = calendar.getTime();
System.out.println(sdf.format(date2)); //2019/02/28
}
}
Javaの場合はうるう年の一年前の日付を取得した場合、2月28日となりました。
JavaScriptの場合
JavaScriptにて日付の計算を行う場合、Date型のインスタンスを生成した後に、生成したインスタンスのsetFullYearメソッドを使用し、引数にて生成インスタンスのgetFullYearメソッド使用して取得した年を加算、減算することで可能となります。
(本記事では年単位の計算を行っていますが、setMonth、setDateメソッドを使用することで月単位、年単位での計算も可能です。)
以下がサンプルコードになります。
var dt = new Date(2020, 1, 29);
dt.setFullYear(dt.getFullYear() - 1);
console.log(dt);//2019/03/01
JavaScriptの場合はうるう年の一年前の日付を取得した場合、3月1日となりました。
Javaでは2月28日、JavaScriptでは3月1日と「うるう年の1年前の日付を取得する」という処理について言語により結果が異なるということがわかりました。
引き続き他の言語、DBも見ていきます。
PHPの場合
PHPにて日付の計算を行う場合、strtotime関数を使用する方法とDateTimeクラスのmodifyメソッドを使用する方法の2つのやり方があります。
以下がサンプルコードになります。
<?php
echo date("Y-m-d", strtotime("2020-02-29 -1 year"));
$date = new DateTime('2020-02-29');// 2019-03-01
$date->modify('-1 year');
echo $date->format('Y-m-d'); // 2019-03-01
?>
strtotime関数を使用する方法、DateTimeクラスのmodifyメソッドを使用する方法いずれも3月1日となりました。
PostgreSQLの場合
PostgreSQLにて日付の計算を行う場合、INTERVAL型で時間を指定し、日付から加算、減算することで可能となります。
以下がサンプルコードになります。
select cast('2020/02/29' as date) - cast('1 year' as INTERVAL)
--2019-02-28T00:00:00Z
PostgreSQLの場合はうるう年の一年前の日付を取得した場合、2月28日となりました。
MySQLの場合
MySQLにて日付の計算を行う場合、DATE_ADD、DATE_SUB関数を使用し、第一引数に計算元となる日付、第二引数にINERVAL型で時間を指定することで、計算元日付からそれぞれ加算、減算することが可能となります。
以下がサンプルコードになります。
(減算のため、DATE_SUB関数を使用していますが、DATE_ADD関数で第二引数にINTERVAL -1 YEARを指定することで実質減算を行うこともできます。)
select DATE_ADD('2020/02/29', INTERVAL -1 YEAR) FROM dual;
--2019-02-28
MySQLの場合はうるう年の一年前の日付を取得した場合、2月28日となりました。
SQLiteの場合
SQLiteにて日付の計算を行う場合、date関数を使用し第一引数に計算元となる日付、第二引数に時間を指定することで加算、減算することが可能となります。
以下がサンプルコードになります。
select date('2020-02-29', '-1 year');
--2019-03-01
SQLiteの場合はうるう年の一年前の日付を取得した場合、3月1日となりました。
まとめ
ここまで、いくつかの言語、DBでうるう年の一年前の日付の取得結果を確認してきましたが、まとめると以下のようになります。
Java → 2月28日を出力
Java Script → 3月1日を出力
PHP → 3月1日を出力
PostgreSQL → 2月28日を出力
MySQL → 2月28日を出力
SQLite → 3月1日を出力
言語、DBによってうるう年の一年前の日付の取得結果は異なるという面白い結果が得られました。
他の言語、DBではどうなるのか、あるいはどうしてこのような違いが生まれるのか、日付計算のロジックに違いがあるのかといったように話を広げても面白いかもしれません。
今回の記事は以上となります。