1

I have this String

Fri, 07 Aug 2020 18:00:00 +0000

And I need to convert it to a LocalDateTime

I tryed several ways:

create a DateTimeFormatter and parse the String

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss", Locale.getDefault());
LocalDateTime parsedDate = LocalDateTime.parse(publishedString, formatter);

Convert it to a Date with a SimpleDateFormat and then convert the resultDate to a LocalDateTime

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
SimpleDateFormat dateFormatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z");
Date date = dateFormatter.parse(publishedString);
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

both solution gives me the same exception:

java.time.format.DateTimeParseException: Text 'Fri, 07 Aug 2020 18:00:00 +0000' could not be parsed 
at index 0 at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)

How can I convert that String?

Ivan Notarstefano
  • 115
  • 1
  • 4
  • 18
  • does your computer "understand" english language? what do you have for Locale there? – ΦXocę 웃 Пepeúpa ツ Aug 06 '20 at 16:40
  • 3
    `DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ROOT)` should probably work. 1. Problem is your system locale probably isn't english so "Fri" is not parsed correctly and 2. Problem you forgot the offset part in your pattern at the end – OH GOD SPIDERS Aug 06 '20 at 16:44
  • 3
    I guess, your Locale is `Locale.ITALY`. Try to change it to `Locale.ENGLISH`. Also, pattern is not complete. Try to use `DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH)`. After this it should work. See also: [java.time DateTimeFormatter pattern for timezone offset](https://stackoverflow.com/questions/30710829/java-time-datetimeformatter-pattern-for-timezone-offset) – Michał Ziober Aug 06 '20 at 16:50

3 Answers3

5

tl;dr

OffsetDateTime.parse( 
    "Fri, 07 Aug 2020 18:00:00 +0000" , 
    DateTimeFormatter.RFC_1123_DATE_TIME 
)

See this code run live at IdeOne.com.

LocalDateTime is the wrong class

Your input string contains +0000 which indicates an offset-from-UTC.

So you should not be using LocalDateTime. That class purposely lacks any concept of time zone or offset. With LocalDateTime, your string Fri, 07 Aug 2020 18:00:00 +0000 will become 6M on August 7th 2020, but we won't know if that is 6 PM in Tokyo Japan, 6 PM in Toulouse France, or 6 PM in Toledo Ohio US — all different moments several hours apart.

OffsetDateTime

Instead, this value should be parsed as OffsetDateTime.

Parsing

Your input's format is that of RFC 1123. That particular format is predefined in java.time.

String input = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter f = DateTimeFormatter.RFC_1123_DATE_TIME;
OffsetDateTime odt = OffsetDateTime.parse( input , f );

odt.toString(): 2020-08-07T18:00Z

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 1
    `LocalDateTime is the wrong class` - Exactly!. I wanted to write a similar answer yesterday but couldn't manage time to do so. Probably, I wouldn't have written so beautifully as you have done. Nice answer! – Arvind Kumar Avinash Aug 07 '20 at 21:38
  • It's not the wrong class, I have an object with a LocalDateTime field and I have to put the result of the String conversion into that field – Ivan Notarstefano Aug 08 '20 at 04:07
  • @IvanNotarstefano Would you store an amount of money as a `BigDecimal` while discarding the indicator of which currency? That is analogous to what you are doing in discarding the offset indicated in your input. `LocalDateTime` cannot represent the value in your input, and is indeed the wrong class to use here. – Basil Bourque Aug 08 '20 at 05:46
  • What an analogy, @BasilBourque! I will certainly use this analogy in future to explain the importance of `ZonedDateTime` and `OffsetDateTime`. – Arvind Kumar Avinash Aug 08 '20 at 09:39
4

I'd say use Locale.ROOT and don't forget the Z in the DateTimeFormatter class

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ROOT);
LocalDateTime parsedDate = LocalDateTime.parse(dateString, formatter);
ItsLhun
  • 96
  • 1
  • 3
  • 2
    `LocalDateTime` is the wrong class to use here. You are discarding valuable information: the offset-from-UTC. – Basil Bourque Aug 07 '20 at 19:31
  • 1
    While it may or may not give the expected result in some or other cases, as indicated by @BasilBourque, there’s no need to code your own format pattern string. The format is built in as `DateTimeFormatter.RFC_1123_DATE_TIME`. – Ole V.V. Aug 09 '20 at 06:44
3

I understand that you need a LocalDateTime for an API that due to a design problem beyond your control is trying to use LocalDateTime for a point in time.

If an external contract dictates in which time zone or at which UTC offset the LocalDateTime is to be understood, LocalDateTime can be made to work, at least for 99.977 % of cases. You will still have a programming error waiting to happen on the day when some colleague programmer does not read the contract, a problem that we cannot solve in the code, only try to mitigate through good commenting.

If for example the contract says UTC, then we need to make sure we convert the time to UTC. And we need the offset from the string for doing so.

    ZoneOffset contractualOffset = ZoneOffset.UTC;
    String stringWeveGot = "Fri, 07 Aug 2020 18:00:00 +0000";
    LocalDateTime convertedDateTime = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME)
            .withOffsetSameInstant(contractualOffset)
            .toLocalDateTime();
    System.out.println(convertedDateTime);

Output:

2020-08-07T18:00

If the offset in the string is required to be 0 already, you need to validate that it is, or errors will go unnoticed and users will get wrong results. For example:

    OffsetDateTime parsedOdt = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME);
    if (! parsedOdt.getOffset().equals(contractualOffset)) {
        throw new IllegalStateException("Offset must be " + contractualOffset
                + ", was " + parsedOdt.getOffset());
    }
    LocalDateTime convertedDateTime = parsedOdt.toLocalDateTime();

If the contract mentions some time zone, convert to that time zone. I am taking Australia/Victoria as an example.

    ZoneId contractualZone = ZoneId.of("Australia/Victoria");
    String stringWeveGot = "Fri, 07 Aug 2020 18:00:00 +0000";
    LocalDateTime convertedDateTime = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME)
            .atZoneSameInstant(contractualZone)
            .toLocalDateTime();
    System.out.println(convertedDateTime);

2020-08-08T04:00

You will get an ambiguous result at time anomalies where the clock is turned backward, for example at the fall back when summer time (DST) ends.

What went wrong in your code?

The cause of your exception is explained here:

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • On the top of nice answer by Basil Bourque, whatever you have added is golden. – Arvind Kumar Avinash Aug 09 '20 at 07:10
  • Thank you, @ArvindKumarAvinash, that’s very nice! I agree of course, that my answer is nothing but an addition to the basic and important information given by Basil Bourque. – Ole V.V. Aug 09 '20 at 07:32