Java 根据现有实例创建日期和时间

Java 根据现有实例创建日期和时间,用户希望修改 Date-Time API 中某个类的现有实例,如果需要进行简单的增减操作,使用 plusminus 方法;对于其他操作,使用 with 方法。Date-Time API 中的所有实例都是不可变的。一旦创建 LocalDateLocalTimeLocalDateTimeZonedDateTime,就无法修改它们。

Java 根据现有实例创建日期和时间 问题描述

用户希望修改 Date-Time API 中某个类的现有实例。

Java 根据现有实例创建日期和时间 解决方案

如果需要进行简单的增减操作,使用 plusminus 方法;对于其他操作,使用 with 方法。

Java 根据现有实例创建日期和时间 具体实例

Date-Time API 中的所有实例都是不可变的。一旦创建 LocalDateLocalTimeLocalDateTimeZonedDateTime,就无法修改它们。这对保持线程安全而言十分有利,不过如何根据现有实例创建新的实例呢?
LocalDate 类为例,它定义了多种对日期进行增减操作的方法,包括:

  • LocalDate plusDays(long daysToAdd)

  • LocalDate plusWeeks(long weeksToAdd)

  • LocalDate plusMonths(long monthsToAdd)

  • LocalDate plusYears(long yearsToAdd)

上述方法均返回一个新的 LocalDate,它是当前日期的副本,并添加了指定的值。
LocalTime 类也定义了类似的方法:

  • LocalTime plusNanos(long nanosToAdd)

  • LocalTime plusSeconds(long secondsToAdd)

  • LocalTime plusMinutes(long minutesToAdd)

  • LocalTime plusHours(long hoursToAdd)

类似地,每种方法均返回一个新的 LocalTime,它是当前时间的副本,并添加了指定的值。此外,LocalDateTime 类囊括了 LocalDateLocalTime 类中用于处理日期和时间增减的所有方法。例 8-6 显示了各种 plus 方法在 LocalDateLocalTime 类中的应用。

例 8-6 plus 方法在 LocalDateLocalTime 类中的应用

@Test
public void localDatePlus() throws Exception {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    LocalDate start = LocalDate.of(2017, Month.FEBRUARY, 2);

    LocalDate end = start.plusDays(3);
    assertEquals("2017-02-05", end.format(formatter));

    end = start.plusWeeks(5);
    assertEquals("2017-03-09", end.format(formatter));

    end = start.plusMonths(7);
    assertEquals("2017-09-02", end.format(formatter));

    end = start.plusYears(2);
    assertEquals("2019-02-02", end.format(formatter));
}

@Test
public void localTimePlus() throws Exception {
    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_TIME;

    LocalTime start = LocalTime.of(11, 30, 0, 0);
    LocalTime end = start.plusNanos(1_000_000);
    assertEquals("11:30:00.001", end.format(formatter));

    end = start.plusSeconds(20);
    assertEquals("11:30:20", end.format(formatter));

    end = start.plusMinutes(45);
    assertEquals("12:15:00", end.format(formatter));

    end = start.plusHours(5);
    assertEquals("16:30:00", end.format(formatter));
}

不少类还包括其他两种形式的 plusminus 方法。以 LocalDateTime 类为例,plusminus 方法的签名如下:

LocalDateTime plus(long amountToAdd, TemporalUnit unit)
LocalDateTime plus(TemporalAmount amountToAdd)

LocalDateTime minus(long amountToSubtract, TemporalUnit unit)
LocalDateTime minus(TemporalAmount amountToSubtract)

对于 LocalDateLocalDate 类,plusminus 方法的格式与 LocalDateTime 类相同,具有相应的返回类型。有趣的是,不妨将 minus 方法视为具有否定形式的 plus 方法。
对传入 TemporalAmount 的方法而言,参数通常为 PeriodDuration,但也可以是任何实现 TemporalAmount 接口的类型。该接口定义了 addTosubtractFrom 两种方法:

Temporal addTo(Temporal temporal)
Temporal subtractFrom(Temporal temporal)

跟踪调用栈(call stack)可以看到,调用 minus 委托给带有否定参数的 plus,而 plus 委托给 TemporalAmount.addTo(Temporal)TemporalAmount.addTo(Temporal) 再回调 plus(long, TemporalUnit),它将执行实际的操作。

例 8-7 显示了 plusminus 方法的相关应用。

例 8-7 plusminus 方法的应用

@Test
public void plus_minus() throws Exception {
    Period period = Period.of(2, 3, 4); // 两年3个月零4天
    LocalDateTime start = LocalDateTime.of(2017, Month.FEBRUARY, 2, 11, 30);
    LocalDateTime end = start.plus(period);

    assertEquals("2019-05-06T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.plus(3, ChronoUnit.HALF_DAYS);
    assertEquals("2017-02-03T23:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.minus(period);
    assertEquals("2014-10-29T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.minus(2, ChronoUnit.CENTURIES);
    assertEquals("1817-02-02T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.plus(3, ChronoUnit.MILLENNIA);
    assertEquals("5017-02-02T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}

当 API 调用 TemporalUnit 时,提供的实现类为 ChronoUnit,它定义了许多方便的枚举常量(enum constant)可供使用。

此外,每种类都定义了一系列 with 方法,可以一次修改一个字段。
with 方法用于处理常用的日期和时间,某些方法颇为有趣。以 LocalDateTime 类为例:

LocalDateTime withNano(int nanoOfSecond)
LocalDateTime withSecond(int second)
LocalDateTime withMinute(int minute)
LocalDateTime withHour(int hour)
LocalDateTime withDayOfMonth(int dayOfMonth)
LocalDateTime withDayOfYear(int dayOfYear)
LocalDateTime withMonth(int month)
LocalDateTime withYear(int year)

例 8-8 显示了各种 with 方法的应用。

例 8-8 with 方法在 LocalDateTime 类中的应用

@Test
public void with() throws Exception {
    LocalDateTime start = LocalDateTime.of(2017, Month.FEBRUARY, 2, 11, 30);
    LocalDateTime end = start.withMinute(45);
    assertEquals("2017-02-02T11:45:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.withHour(16);
    assertEquals("2017-02-02T16:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.withDayOfMonth(28);
    assertEquals("2017-02-28T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.withDayOfYear(300);
    assertEquals("2017-10-27T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

    end = start.withYear(2020);
    assertEquals("2020-02-02T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}

@Test(expected = DateTimeException.class)
public void withInvalidDate() throws Exception {
    LocalDateTime start = LocalDateTime.of(2017, Month.FEBRUARY, 2, 11, 30);
    start.withDayOfMonth(29);
}

由于 2017 年并非闰年,无法将日期设置为 2 月 29 日,第二个测试将抛出 DateTimeException
with 方法也可以传入 TemporalAdjusterTemporalField

LocalDateTime with(TemporalAdjuster adjuster)
LocalDateTime with(TemporalField field, long newValue)

传入 TemporalFieldwith 方法允许字段解析日期以使其有效。如例 8-9 所示,程序传入 1 月的最后一天,并尝试将月份改为 2 月(“2 月 31 日”)。此时,根据 Javadoc 的描述,系统将选择前一个有效日期,即 2 月的最后一天(2 月 28 日)。

例 8-9 月份调整(无效)

@Test
public void temporalField() throws Exception {
    LocalDateTime start = LocalDateTime.of(2017, Month.JANUARY, 31, 11, 30);
    LocalDateTime end = start.with(ChronoField.MONTH_OF_YEAR, 2);
    assertEquals("2017-02-28T11:30:00",
        end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}

可想而知,日期和时间的处理涉及一些相当复杂的规则,不过 Javadoc 对此做了系统而详尽的描述。
范例调节器与查询将讨论传入 TemporalAdjusterwith 方法。

赞(0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例