タイトルの通り、java.sql.Dateをシリアル値に、シリアル値をjava.sql.Dateに変換してみようと思います。
まずはjava.sql.Date -> シリアル値
日付のシリアル値は
・1900年1月1日を 1
・1900年1月2日を 2
・1900年1月3日を 3
と、毎日1づつ増加していくので、1899-12-30
からシリアル値にしたい日付までの日数を求めれば、シリアル値を求めることができます。
↓java.sql.Date -> シリアル値に変換するコード
/**
* 引数の日付をシリアル値に変換
*
* @param date 日付
* @return シリアル値取得
*/
public static Double convertToSerialNumber(Date date) {
if (date == null) {
return null;
}
final var requestLocalDate = date.toLocalDate();
final var baseLocalDate = Date.valueOf("1899-12-30").toLocalDate();
return (double) ChronoUnit.DAYS.between(baseLocalDate, requestLocalDate);
}
出力結果:
log.debug(convertToSerialNumber(Date.valueOf("2022-08-30")) // 44803
次にシリアル値→java.sql.Date
これはコード内で説明していこうと思います。
/**
* シリアル値を日付に変換する。
*
* @param serialNumber シリアル値
* @return 日付
*/
public static String parseExcelSerialNumber(String serialNumber) {
if (serialNumber == null) {
return null;
}
// 1900/1/1のシリアル値が1なので、加算する値は日付にしたいシリアル値-1になる
int serial = new BigDecimal(serialNumber).intValue() - 1;
// 1900/2/29は存在しないがエクセル内では存在しているらしいのでその日付以降であればさらに-1をする
serial -= (serial > 60 ? 1 : 0);
// Windowsでのデフォルトでは1900/1/1 12:00:00AMからなので,その時刻を表すCalendarクラスのインスタンス
Calendar cal = Calendar.getInstance();
cal.set(1900, 0, 1, 12, 0, 0);
// 基準日に加算
cal.add(Calendar.DATE, serial);
return new Date(cal.getTime().getTime());
}
出力結果:
log.debug(parseExcelSerialNumber(Date.valueOf("44803")) // 2022-08-30
最後に
コード上に何か疑問点があればご指摘いただけると幸いです。