Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 474
Gilbert Avatar asked Jun 20 '18 00:06

Gilbert


People also ask

Which time format is followed in Java 8?

For time patterns, use mm for two-digit minute and ss for two-digit second. The hh pattern generates the two-digit hour for a 12-hour clock (e.g., 6PM is "06") and HH generates the two-digit hour for a 24-hour clock (e.g., 6PM is "18").

What is a ZonedDateTime in the Java 8 date and time API?

ZonedDateTime is an immutable representation of a date-time with a time-zone. This class stores all date and time fields, to a precision of nanoseconds, and a time-zone, with a zone offset used to handle ambiguous local date-times.

How do I get current time in ZonedDateTime?

now() now() method of a ZonedDateTime class used to obtain the current date-time from the system clock in the default time-zone. This method will return ZonedDateTime based on system clock with default time-zone to obtain the current date-time. The zone and offset will be set based on the time-zone in the clock.

Is ZonedDateTime a UTC?

A ZonedDateTime represents a date-time with a time offset and/or a time zone in the ISO-8601 calendar system. On its own, ZonedDateTime only supports specifying time offsets such as UTC or UTC+02:00 , plus the SYSTEM time zone ID.


1 Answers

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:

  • previous( DayOfWeek )
  • previousOrSame( DayOfWeek )

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.

like image 69
Basil Bourque Avatar answered Oct 06 '22 22:10

Basil Bourque