【问题标题】:java.time equivalent of Joda-Time `withTimeAtStartOfDay`? (get first moment of the day)java.time 相当于 Joda-Time `withTimeAtStartOfDay`? (获取当天的第一刻)
【发布时间】:2015-05-18 01:06:43
【问题描述】:

Joda-Time 库中,DateTime 类提供了一个方法withTimeAtStartOfDay 来获取当天的第一刻。你可能会认为那一刻是“午夜”。第一个时刻通常是时间00:00:00.000,但并非总是如此。

Java 8 及更高版本中的java.time package 是否具有等效功能?

【问题讨论】:

  • AFAIK 不是直接的,但你可以做类似LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0); 的事情,这有点冗长但应该达到相同的结果:P

标签: java datetime java-time


【解决方案1】:

等效的方法是在 LocalDate 类中使用特殊方法 atStartOfDay

ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zdt = LocalDate.now(zoneId).atStartOfDay(zoneId);

还请注意,Joda-Time DateTime 的等价物不是LocalDateTime,而是ZonedDateTimezoneId 参数在这里很重要。迁移的具体示例 - 另请参阅 timezone website 了解有关巴西 Daylight Saving Time (DST) 转换的详细信息:

Joda-Time(旧方式)

DateTime dt = 
  new DateTime(2015, 10, 18, 12, 0, DateTimeZone.forID("America/Sao_Paulo"));
dt = dt.withTimeAtStartOfDay();
System.out.println(dt); // 2015-10-18T01:00:00.000-02:00

请注意,此代码甚至会在第一行调用constructor 时引发午夜异常。

java.time(新方式)

ZoneId zoneId = ZoneId.of("America/Sao_Paulo");
ZonedDateTime zdt =
  ZonedDateTime.of(2015, 10, 18, 12, 0, 0, 0, zoneId);
zdt = zdt.toLocalDate().atStartOfDay(zoneId);
System.out.println(zdt); // 2015-10-18T01:00-02:00[America/Sao_Paulo]

第二个程序语句的行为与 Joda-Time 不同,因为它不会抛出异常,而是将本地时间静默地移动所讨论的间隙大小,这里是一小时。这意味着,如果您选择了午夜,结果将是相同的(即 1:00)。如果您选择了 00:30,那么结果将是 01:30。上面给出的示例选择中午作为输入。

引用ZonedDateTime.of(…)的文档:

在大多数情况下,本地日期时间只有一个有效偏移量。在重叠的情况下,当时钟被调回时,有两个有效的偏移量。此方法使用通常对应于“summer”的较早偏移量。

在间隙的情况下,当时钟向前跳时,没有有效的偏移量。相反,本地日期时间会根据间隙的长度进行调整。对于典型的一小时夏令时更改,本地日期时间将在一小时后移动到通常对应于“夏季”的偏移量中。

不可能 100% 迁移所有细节,例如异常行为和应用的 DST 转换策略,因为这两个库差异太大。但这是你的指导方针:

  • DateTime 替换为ZonedDateTime
  • 考虑切换到LocalDate 进行中间计算(参见示例)
  • 使用对时区的显式引用并将DateTimeZone 替换为ZoneId

【讨论】:

  • 如果第二行(ZonedDateTime.of)向前或向后滑动,为什么我们需要第三行zdt.toLocalDate().atStartOfDay(zoneId)
  • @BasilBourque 首先让我感谢您的英文更正。关于第二行的含义,好吧,没有这行“zdt”将只是在中午(原值为12:00)。但问题是关于一天的开始。对于您的问题,另一种可能更合适的答案是:ZonedDateTime.of(2015, 10, 18, 0, 0, 0, 0, zoneId) 产生午夜后一小时的时间(默默地向前推进一小时)。我个人觉得使用atStartOfDay(...) 方法的风格更具表现力。
  • @BasilBourque 两个库都缺少一个不提供替代转换策略的功能。 Joda-Time 只是为无效的当地时间抛出异常的行为很难令人满意。而且 JSR-310 已经抛弃了这个特性(曾经在 Threeten 中出现过!),现在只限于 PUSH_FORWARD 策略。
  • @BasilBourque 让它更清楚。我根本不喜欢隐式默认值和静默调整。如果 API 提供任何机会使推进调整更加清晰(此处为 atStartOfDay(ZoneId)),那么我更喜欢并提倡这一点。否则我很遗憾 JSR-310 在设计上提供了太多的隐式默认值。
【解决方案2】:

Java 时间truncatedTo

虽然这是一个老问题,但我刚刚发现了另一种方法,使用truncation,对我来说感觉更优雅:

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime today = now.truncatedTo(ChronoUnit.DAYS); // today, midnight

这更短,并且在代码中需要更少的中间值。据我所知,语义与接受的答案相同。

【讨论】:

    【解决方案3】:

    您可以使用LocalDate 来执行此操作:

    // or a similar factory method to get the date you want
    LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
    

    将此日期与午夜时间相结合,以在此日期的开头创建一个 LocalDateTime。这将返回从该日期开始的午夜 00:00 形成的 LocalDateTime。

    https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#atStartOfDay--

    【讨论】:

    • @MenoHochschild 是什么让你这么认为?他甚至包含一个指向该方法的 JDK 8 API 文档的链接——它不是 Joda Time 代码。
    • @Jesper 请再看一下代码示例。 JSR-310 (Java-8) 不使用公共构造函数,而是使用静态工厂方法。
    【解决方案4】:

    TemporalAdjuster

    每个LocalTime 也是一个TemporalAdjuster,因此可以将if 传递给大多数时间类型的with() 方法,并将更新时间字段以匹配。

    即:

    @Test
    public void date_time_at_start_of_day() throws Exception {
        assertThat(LocalDateTime.parse("2015-05-22T12:27:00")
                                .with(LocalTime.MIDNIGHT),
                   equalTo(LocalDateTime.parse("2015-05-22T00:00:00")));
        assertThat(OffsetDateTime.parse("2015-05-22T12:27:00+01:00")
                                 .with(LocalTime.MIDNIGHT),
                   equalTo(OffsetDateTime.parse("2015-05-22T00:00:00+01:00")));
        assertThat(ZonedDateTime.parse("2015-05-22T12:27:00+01:00[Europe/London]")
                                .with(LocalTime.MIDNIGHT),
                   equalTo(ZonedDateTime.parse("2015-05-22T00:00:00+01:00[Europe/London]")));
    }
    

    【讨论】:

      猜你喜欢
      • 2014-04-04
      • 2011-06-13
      • 1970-01-01
      • 2018-07-10
      • 2010-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多