Why does Java 8 ZonedDateTime think 24:01 is a valid time string representation?

Gilbert :

I need to get two ZonedDateTime instances representing the beginning and the end of the current week.

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);

Output:

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

What does this string represent? Is it 1 minute after midnight, morning of 2018-06-18? If so, why is 24:01 a valid time? I thought it should be 00:01 in a 24 hour clock representation.

Edit: and is there a way to format this to show 00:01 instead without transforming the string into a new string?

Basil Bourque :

tl;dr

  • Change formatting pattern from kk to HH.
  • Your addition of one minute is irrelevant.

2018-06-18 24:01

…becomes:

2018-06-18 00:01

Tip: When debugging, use the default standard ISO 8601 format in generating a String.

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

2018-06-18T00:00+01:00[Africa/Tunis]

k = 1-24 hour (actually, 24-23)

The deleted Answer by Jorn Vernee was correct: You are using k which is documented as representing the time-of-day using a 24-hour clock running from 1 to 24.

What is not so clear in the doc is that the first hour is counted as 24, rather than 0. So it might be better stated to say that the day runs from 24-23, that is, 24, 1, 2, 3, … , 22, 23. The value 24 represents the first hour of the day, not the last.

Here is a simple example using time-of-day only to demonstrate this behavior.

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

Note that this kk behavior of java.time where the entire first hour of the day is labeled 24 in unusual, non-standard, and not commonly used. See Wikipedia. Apparently, this style is used occasionally in some special contexts where business hours extend beyond midnight such as broadcasting.

As for what you expected…

it should be 00:01 in a 24 hour clock representation.

If you want 0-23, use "H", as documented.

First, look at the default ISO 8601 formatted output.

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[America/Los_Angeles]

2018-06-24T00:01-07:00[America/Los_Angeles]

Now, your custom format. Change your formatting pattern from kk to 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

Week

Some other notes…

If you are trying to represent the week, there is no need to add a minute. The generally best practice in representing a span-of-time is the Half-Open approach where the beginning is inclusive while the ending is exclusive. So a week starts at the first moment of one Monday and runs up to, but does not include, the first moment of the following Monday.

Also, I recommend always passing a ZoneId explicitly to now even if that be ZoneId.systemDefault to make your intentions crystal-clear.

Adjusting to Monday has the issue of what to do if today is already a Monday. Use either of this pair of TemporalAdjuster implementations found in TemporalAdjusters to specify your choice of behavior:

Example code.

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 ) ;

For tracing and debugging, always generate a String representing the value of your date-time objects using standard ISO 8601 format rather than a custom format as seen your Question. The java.time classes use the standard formats by default in their toString methods.

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

2018-06-18T00:00+01:00[Africa/Tunis]

2018-06-25T00:00+01:00[Africa/Tunis]

If you want date-only values, use the LocalDateTime.

You can represent your time range in a single object using the LocalDateRange or Interval classes found in the ThreeTen-Extra project.

Last tip: Let java.time do the heavy-lifting by using DateTimeFormatter.ofLocalized… methods to automatically localize the generated strings using a specified Locale to determine the human language and cultural norms of formatting.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=35137&siteId=1