Java 8 ZonedDateTime为什么认为24:01是有效的时间字符串表示形式?

时间:2018-06-20 00:04:25

标签: java datetime java-8

我需要获得两个ZonedDateTime实例,分别代表当前一周的开始和结束。

ZonedDateTime currentDateTime = ZonedDateTime.now();
ZonedDateTime startOfThisWeek = currentDateTime.with(DayOfWeek.MONDAY).truncatedTo(ChronoUnit.DAYS).plusMinutes(1);
ZonedDateTime endOfThisWeek = startOfThisWeek.plusDays(6);
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd kk:mm");
String startweek = startOfThisWeek.format(df);
String endweek = endOfThisWeek.format(df);
System.out.println(startweek);
System.out.println(endweek);

输出:

2018-06-18 24:01
2018-06-24 24:01

此字符串代表什么?是2018-06-18午夜1分钟后吗?如果是这样,为什么24:01是有效时间?我以为24小时制应该是00:01。

编辑:是否可以将其格式化为显示00:01而不将字符串转换为新字符串?

1 个答案:

答案 0 :(得分:37)

tl; dr

  • 将格式设置模式从kk更改为HH
  • 您添加的一分钟无关紧要。
  

2018-06-18 24:01

…成为:

  

2018-06-18 00:01

提示:调试时,在生成String时使用默认的标准ISO 8601格式。

ZonedDateTime.now( 
    ZoneId.of( "Africa/Tunis" ) 
)
.truncatedTo( ChronoUnit.DAYS )
.with( 
    TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) 
)
.toString()
  

2018-06-18T00:00 + 01:00 [非洲/突尼斯]

k = 1-24小时(实际上是24-23)

deleted Answer by Jorn Vernee是正确的:您正在使用k,该文件记录为使用1到24之间的24小时制表示一天中的时间。

文档中尚不清楚的是,第一个小时被计为24,而不是0。因此,最好说这一天是从24到23,即24, 1, 2, 3, … , 22, 23 。值24代表一天的第一个小时,而不是最后一天。

这是一个使用每日时间的简单示例,仅用于演示此行为。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "kk:mm" );
LocalTime lt = LocalTime.MIN;
String output = lt.format( f );

System.out.println( lt.toString() );  // Standard ISO 8601 format, 00-24.
System.out.println( output );         // `kk` format is 24, 1 , 2, … , 22 , 23.
  

00:00

     

24:00

请注意, java.time 的这种kk行为是一天中的整个第一小时都以不常见,非标准且不常用的方式标记为24See Wikipedia。显然,这种风格偶尔会在某些特殊情况下使用,例如广播等营业时间超出午夜。

关于您的期望...

  

它应该是24小时制的00:01。

如果您想要0-23,请使用“ H”,as documented

首先,查看默认的ISO 8601格式的输出。

ZonedDateTime currentDateTime = ZonedDateTime.now();
ZonedDateTime startOfThisWeek = currentDateTime.with( DayOfWeek.MONDAY ).truncatedTo( ChronoUnit.DAYS ).plusMinutes( 1 );
ZonedDateTime endOfThisWeek = startOfThisWeek.plusDays( 6 );
System.out.println( startOfThisWeek );
System.out.println( endOfThisWeek );
  

2018-06-18T00:01-07:00 [美国/洛杉矶]

     

2018-06-24T00:01-07:00 [美国/洛杉矶]

现在,您的自定义格式。将格式从kk更改为HH

DateTimeFormatter df = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm" );
String startweek = startOfThisWeek.format( df );
String endweek = endOfThisWeek.format( df );
System.out.println( startweek );
System.out.println( endweek );
  

2018-06-18 00:01

     

2018-06-24 00:01

其他一些注释...

如果您要代表星期,则无需添加分钟。代表时间跨度的最佳实践通常是半开放式方法,其中开始为包含,而结束为排他。因此,一周从一个星期一的第一时刻开始,一直持续到(但不包括)下一个星期一的第一时刻。

此外,我建议始终将ZoneId显式传递给now,即使它是ZoneId.systemDefault也可以使您的意图清晰明了。

如果今天已经是星期一,那么调整为星期一会产生问题。使用TemporalAdjuster中的这对TemporalAdjusters实现中的任何一个来指定您的行为选择:

示例代码。

ZonedDateTime now = ZonedDateTime.now( ZoneId.of( "Africa/Tunis" ) ) ;  // Or `ZoneId.systemDefault()`.
ZonedDateTime weekStart = now.truncatedTo( ChronoUnit.DAYS ).with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );
ZonedDateTime weekStop = weekStart.plusWeeks( 1L ) ;

对于跟踪和调试,请始终使用标准ISO 8601格式(而不是您的问题所见的自定义格式)生成表示日期时间对象值的字符串。 java.time 类默认在其toString方法中使用标准格式。

String outputStart = weekStart.toString() ;
String outputStop = weekStop.toString() ;
  

2018-06-18T00:00 + 01:00 [非洲/突尼斯]

     

2018-06-25T00:00 + 01:00 [非洲/突尼斯]

如果需要仅日期的值,请使用LocalDateTime

您可以使用ThreeTen-Extra项目中的LocalDateRangeInterval类在单个对象中表示您的时间范围。

最后一个提示:让 java.time 通过使用DateTimeFormatter.ofLocalized…方法来进行繁重的工作,该方法使用指定的Locale自动定位生成的字符串,从而确定人类语言并格式化的文化规范。