java.sql.Timeとjava.time.LocalTime間の変換でミリ秒が破棄される

java.sql.Timeには、下記のようなLocalTimeとの間で変換を行うメソッドがあります。

  • public LocalTime toLocalTime()
  • Time java.sql.Time.valueOf(LocalTime time)

ただ、これらメソッドは秒までしか対象にしておらず、ミリ秒が破棄されています。

    /**
     * Obtains an instance of {@code Time} from a {@link LocalTime} object
     * with the same hour, minute and second time value as the given
     * {@code LocalTime}.
     *
     * @param time a {@code LocalTime} to convert
     * @return a {@code Time} object
     * @exception NullPointerException if {@code time} is null
     * @since 1.8
     */
    @SuppressWarnings("deprecation")
    public static Time valueOf(LocalTime time) {
        return new Time(time.getHour(), time.getMinute(), time.getSecond());
    }

    /**
     * Converts this {@code Time} object to a {@code LocalTime}.
     * <p>
     * The conversion creates a {@code LocalTime} that represents the same
     * hour, minute, and second time value as this {@code Time}.
     *
     * @return a {@code LocalTime} object representing the same time value
     * @since 1.8
     */
    @SuppressWarnings("deprecation")
    public LocalTime toLocalTime() {
        return LocalTime.of(getHours(), getMinutes(), getSeconds());
    }

面倒ではありますが、LocalDateTimeなどを経由させて変換することによって、ミリ秒を維持したまま変換できます。

SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
DateTimeFormatter localTimeFormat = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");

LocalTime baseLocalTime = LocalTime.now();

// baseLocalTime: 01:41:44.369
System.out.printf(
        "baseLocalTime: %s\n",
        baseLocalTime.format(localTimeFormat));

{
    // NGなパターン
    Time time = Time.valueOf(baseLocalTime);
    LocalTime localTime = time.toLocalTime();

    // time: 01:41:44.000 localTime: 01:41:44.000 equals: false
    System.out.printf(
            "time: %s localTime: %s equals: %s\n",
            timeFormat.format(time),
            localTime.format(localTimeFormat),
            baseLocalTime.equals(localTime));
}

{
    // OKなパターン
    Time time = new Time(
            baseLocalTime.atDate(LocalDate.ofEpochDay(0)) // LocalDateTimeに変換
                    .atZone(ZoneId.systemDefault()) // ZonedDateTimeに変換
                    .toInstant().toEpochMilli()); // Epochミリ秒へ

    LocalTime localTime = LocalDateTime.ofInstant( // Epochミリ秒からLocalDateTimeへ
            Instant.ofEpochMilli(time.getTime()),
            ZoneId.systemDefault()).toLocalTime();

    // time: 01:41:44.369 localTime: 01:41:44.369 equals: true
    System.out.printf(
            "time: %s localTime: %s equals: %s\n",
            timeFormat.format(time),
            localTime.format(localTimeFormat),
            baseLocalTime.equals(localTime));
}