1

How do I format an ISO-8601 string 2020-02-10T22:55:13-08:00 into a custom format like 1:30pm? I kept getting unparseable date exception. My code snippet is below:

    String string = "2020-02-10T22:55:13-08:00";
    SimpleDateFormat sdfSource = new SimpleDateFormat("YYYY-MM-DD'T'HH:mm:ss'Z'", Locale.getDefault());
    SimpleDateFormat sdfTarget = new SimpleDateFormat("h:mma", Locale.getDefault());
    try {
        Date date = sdfSource.parse(string);
        String createdAt = sdfTarget.format(date);
        System.out.println(createdAt);
    } catch (ParseException e) {
      e.printStackTrace();
    }
Ole V.V.
  • 65,573
  • 11
  • 96
  • 117
selbay84
  • 37
  • 1
  • 7
  • possible duplicate of https://stackoverflow.com/questions/2201925/converting-iso-8601-compliant-string-to-java-util-date – Maxim Popov Feb 11 '20 at 09:08
  • 1
    I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `OffsetDateTime` and `DateTimeFormatter`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Feb 11 '20 at 18:10
  • `OffsetDateTime.parse("2020-02-10T22:55:13-08:00").format(DateTimeFormatter.ofPattern("h:mma", Locale.getDefault()))`. No need for any explicit formatter for parsing. And `OffsetDateTime` is the right class to use, better suited and more correct than than `ZonedDateTime` and `LocalDateTime` suggested in a couple of the answers. – Ole V.V. Feb 11 '20 at 18:17
  • Do you want the time converted to a well-defined time zone before formatting it? (My code line in the previous comment does not do that.) – Ole V.V. Feb 11 '20 at 18:18

3 Answers3

6
    private String  getZonedDateTime(String startTime,String inExpectedTimeZone){
        return ZonedDateTime
                .parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"))
                .withZoneSameInstant(ZoneId.of(inExpectedTimeZone)).format(DateTimeFormatter.ofPattern("hh:mm a"));
    }

In the above method just pass the zulu string and expected Time in which you want the value it will be parsed using java 8 ZonedDateTime and DateTimeFormatter.

Sriram
  • 438
  • 4
  • 10
  • You should use `uuuu` for the year. See [here](https://stackoverflow.com/a/41178418/5221149). – Andreas Feb 11 '20 at 09:04
  • Or a more thorough treatment [here: `uuuu` versus `yyyy` in `DateTimeFormatter` formatting pattern codes in Java?](https://stackoverflow.com/questions/41177442/uuuu-versus-yyyy-in-datetimeformatter-formatting-pattern-codes-in-java) (also @Andreas) – Ole V.V. Feb 11 '20 at 17:53
  • 1
    Thank you so much @Palani! This worked like a charm :) – selbay84 Feb 12 '20 at 01:57
  • 1
    No need to define that formatting pattern. Already defined in the constant [`DateTimeFormatter.ISO_OFFSET_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME). – Basil Bourque Feb 12 '20 at 16:33
  • 1
    Using `OffsetDateTime` rather than `ZonedDateTime` is more appropriate here. – Basil Bourque Feb 12 '20 at 16:35
1

Use X (unquoted) instead of 'Z' (quoted) in your sdfSource. That will solve the problem. Since your are using quotes a literal Z is expected in the input. Secondly you need to use X instead of Z because the X means an RFC822 timezone and that means no colon between the hour and the minute. Your example value has colon between the hour and the minute.

Btw: You can switch to the new Date & Time classes introduced in Java 8 including its parser/formatter. It is a lot more versatile than SimpleDateFormat. It also handles a lot more use-cases out-of-the-box with pre-defined formats so that you won't have to create your own parser. ... as you do now.

lbruun
  • 131
  • 5
1

If you are on Java 8 or beyond, you may use LocalDateTime, as shown below.

Edit Based on the comments from @Andreas and @OleV.V., I thought of compiling 4 ways for easy reference. 2 of these use OffsetDateTime and 2 use @Sriram's ZonedDateTime answer. Also, based on whether one has a string at the start or a LocalDateTime.

String string = "2020-02-10T22:55:13-08:00";
DateTimeFormatter inF = DateTimeFormatter.ISO_DATE_TIME;
String desiredZoneId = "Z";
int desiredOffset = 0;

DateTimeFormatter toF = DateTimeFormatter.ofPattern( "h:mm a" );
LocalDateTime ldt = LocalDateTime.parse(string.trim(), inF );

/* Prints -> "ldt.atOffset: 6:55 AM" */
/* 1. If one already has a LocalDateTime. Drawback: One needs to know the offset in the time string. */
System.out.println( "ldt.atOffset: " + ldt.atOffset( ZoneOffset.ofHours( -8 ) ).withOffsetSameInstant( ZoneOffset.ofHours( desiredOffset ) ).format( toF ) );

/* Prints -> "ldt.atZone: 6:55 AM" */
/* 2. If one already has a LocalDateTime. Drawback: One needs to know the offset in the time string. */
System.out.println( "ldt.atZone: " + ldt.atZone( ZoneId.of( "-08:00" ) ).withZoneSameInstant( ZoneId.of( desiredZoneId ) ).format( toF ) );

/* Prints -> "odt: 6:55 AM" */ 
/* 3. Using OffsetDateTime. */
OffsetDateTime odt = OffsetDateTime.parse( string, inF );
System.out.println( "odt: " + odt.withOffsetSameInstant( ZoneOffset.ofHours( desiredOffset ) ).format( toF ) );

/* Prints -> "zdt: 6:55 AM" */
/* 4. Using ZonedDateTime. */
String zdt = ZonedDateTime.parse( string, DateTimeFormatter.ISO_DATE_TIME )
.withZoneSameInstant( ZoneId.of( desiredZoneId ) ).format( toF );
System.out.println( "zdt: " + zdt );
Sree Kumar
  • 766
  • 3
  • 6
  • There is a time zone offset, so `LocalDateTime` is the wrong class to use. Better to parse as `ZonedDateTime` then format back to string using a pattern without time zone. – Andreas Feb 11 '20 at 09:04
  • @Andreas is correct. Still better to use `OffsetDateTime` since the string has an offset, `-08:00`, and no time zone, like America/Tijuana, for example. But using java.time, the modern Java date and time API, as you are doing, is a very good idea. – Ole V.V. Feb 11 '20 at 17:56
  • @Andreas and OleV.V. You are right. Didn't realize that the question asked for the zone to be considered in the conversion. – Sree Kumar Feb 12 '20 at 07:54
  • Better. Thanks. If conversion to a specific time zone is required, no. 4. is the one to go with. – Ole V.V. Feb 12 '20 at 20:18