Home > Net >  How to parse "2021-11-20 01:00" formatted date string to ZonedDateTime - error
How to parse "2021-11-20 01:00" formatted date string to ZonedDateTime - error

Time:11-21

How to parse "2021-11-20 01:00" formatted date to LocalDateTime? I'm trying like:

String value = "2021-11-20 01:00";
ZonedDateTime zDate = ZonedDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-ddxxx"));

But getting this strange error:

java.time.format.DateTimeParseException: Text '2021-11-20 01:00' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed

...Unsupported field: InstantSeconds...

Any suggestions? This time format is used by European VIES VAT ID checking system when receiving XML (SOAP) response: <requestDate>2021-11-20 01:00</requestDate>. The same error with OffsetDateTime..

Interesting thing is that Javadoc says "Three letters (x) outputs the hour and minute, with a colon, such as ' 01:30'". So why above pattern is not working?

Tried also this one - the same error:

ZonedDateTime zDate = ZonedDateTime.parse(value, DateTimeFormatter.ISO_OFFSET_DATE);

Complete error log:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-11-20  01:00' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:402)
    at javaapplication5.JavaApplication5.checkVATatVIES(JavaApplication5.java:162)
    at javaapplication5.JavaApplication5.main(JavaApplication5.java:65)
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed
    at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:370)
    at java.base/java.time.format.Parsed.query(Parsed.java:235)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    ... 3 more
Caused by: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed
    at java.base/java.time.Instant.from(Instant.java:378)
    at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:365)
    ... 5 more
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds
    at java.base/java.time.format.Parsed.getLong(Parsed.java:203)
    at java.base/java.time.Instant.from(Instant.java:373)
    ... 6 more

Using OpenJDK 11.

CodePudding user response:

There is no Java type consisting of only a date and a timezone. ZonedDateTime and OffsetDateTime contain, as their names imply, both a date and a time. So you need to create a formatter that will assume a default time of day:

String value = "2021-11-20 01:00";

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("yyyy-MM-ddxxx");
builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
DateTimeFormatter formatter = builder.toFormatter();

ZonedDateTime zDate = ZonedDateTime.parse(value, formatter);

Of course, the hour of day can be any value you choose: 12 to represent noon, for example. But it has to be something valid.

CodePudding user response:

tl;dr

org.threeten.extra.OffsetDate.parse( "2021-11-20 01:00" )

org.threeten.extra.OffsetDate

The java.time classes built into Java do not offer a class for the idea of a date combined with an offset. So your parse attempts make no sense.

The ThreeTen-Extra project aims at supplementing the functionality of java.time. It includes the OffsetDate class for your particular need. Add the library to your project.

String input = "2021-11-20 01:00" ;
OffsetDate od = OffsetDate.parse( input ) ;

From that OffsetDate object you can obtain either the LocalDate part or the ZoneOffset part. And you can determine other date-time values such as adding a time-of-day to get an OffsetDateTime.

You commented:

if I make a request at 21th at 00:30 AM, I would get the response containing date of 20th.. because our time is UTC 2, and their time is UTC 1

As an aside, a tip: I suggest making a habit of using fully-formed offsets of hour with padding zero, and with minute too. While allowed by various standards, I have seen multiple libraries that fail when abbreviated. So I recommend “ 02:00” rather than “UTC 2”.

Here is some example code demonstrating the scenario you describe. We use the time zone of Africa/Brazzaville because it has an offset of 01:00 at that moment, and we use Europe/Kaliningrad for its offset then of 02:00.

LocalDate ld = LocalDate.of( 2021 , Month.NOVEMBER , 21 );
LocalTime lt = LocalTime.of( 0 , 30 );
ZoneId zKaliningrad = ZoneId.of( "Europe/Kaliningrad" );
ZonedDateTime zdtKaliningrad = ZonedDateTime.of( ld , lt , zKaliningrad );

ZoneId zBrazzaville = ZoneId.of( "Africa/Brazzaville" );
ZonedDateTime zdtBrazzaville = zdtKaliningrad.withZoneSameInstant( zBrazzaville );

OffsetDate od = OffsetDate.from( zdtBrazzaville );

OffsetDateTime odt = od.atTime( LocalTime.MIN );
ZonedDateTime asSeenInKaliningrad = odt.atZoneSameInstant( zKaliningrad );

When run.

zdtKaliningrad = 2021-11-21T00:30 02:00[Europe/Kaliningrad]
zdtBrazzaville = 2021-11-20T23:30 01:00[Africa/Brazzaville]
od = 2021-11-20 01:00
odt = 2021-11-20T00:00 01:00
asSeenInKaliningrad = 2021-11-20T01:00 02:00[Europe/Kaliningrad]

Personally, I question the practical value of the concept of date-with-offset, but that is beside the point of your Question.

Working in specific time zones is usually less problematic than working with mere offsets.

I would argue that it makes more sense to track a date-with-zone as a moment, the point on the timeline when the day begins, along with the named time zone rather than an offset.

String result = 
    LocalDate
        .of( 2021 , 11 , 20 )
        .atStartOfDay( ZoneId.of( "Africa/Brazzaville" ) )
        .toString() 
;

See this code run live at IdeOne.com.

2021-11-20T00:00 01:00[Africa/Brazzaville]

For data exchange of text representing a single day, I would recommend transmitting this string 2021-11-20T00:00 01:00[Africa/Brazzaville] in its format that extends ISO 8601 by appending the name of the time zone in square brackets.

CodePudding user response:

You can specify ur pattern by yourself as String like this:

String str = "2021-11-20 01:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime date= LocalDateTime.parse(str, formatter);
  • Related