3

I have a very peculiar question where I am trying to parse "2019-12-25T17:00:00-05:00" such that it should give me the result DEC 12 | Thursday | 5:00pm

I tried the following code by using DateTimeFormatter and LocalDate

DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssz", Locale.US);
                DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("MM d | E | hh:mm a", Locale.US);
                LocalDate date = LocalDate.parse("2019-12-25T17:00:00-05:00", inputFormatter);
                String formattedDate = outputFormatter.format(date);
                contentTextView.setText(formattedDate);

but it crashes with DateTimeParseException: Text '2019-12-25T17:00:00-05:00' could not be parsed at index 19

Any idea why it's crashing and if my output will render the expected result? Thanks!

Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • try with this one "yyyy-MM-dd'T'HH:mm'Z'" DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) – Rofie Sagara Jul 24 '19 at 02:01
  • with that change , I get this crash : DateTimeParseException: Text '2019-12-25T17:00:00-05:00' could not be parsed at index 16 –  Jul 24 '19 at 02:03
  • how about this "yyyy-MM-dd'T'HH:mm:ss:SSSXXX" or "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" – Rofie Sagara Jul 24 '19 at 02:08
  • same issue with that one : DateTimeParseException: Text '2019-12-25T17:00:00-05:00' could not be parsed at index 19 –  Jul 24 '19 at 02:10
  • 1
    I'm wondering close votes on this question, is this really unclear? – Ryuzaki L Jul 24 '19 at 04:48

2 Answers2

7

Your String 2019-12-25T17:00:00-05:00 represents UTC timezone with offset UTC offset, so use OffsetDateTime for parsing that string

OffsetDateTime odt = OffsetDateTime.parse("2019-12-25T17:00:00-05:00");

System.out.println(odt.format(DateTimeFormatter.ofPattern("MMM d | E | hh:mm a", Locale.US)));

If you want to set particular time zone you can use atZoneSameInstant to pass ZoneId for eaxmple

ZoneId zone = ZoneId.of("America/Chicago");
ZonedDateTime zdt = odt.atZoneSameInstant(zone);
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • this seems to work, however ,I get the result : 112 25 | Wed | 05:00 PM, instead of the Month Dec , is MM the right format for this? –  Jul 24 '19 at 02:34
  • awesome, just one last issue, is there a way to convert AM | PM To lowercase am | pm, without using replace? –  Jul 24 '19 at 02:40
  • i do see only upper case here `https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html, is that a problem? @AngelaHeely – Ryuzaki L Jul 24 '19 at 02:42
  • not a big issue, just curious, as the ideal format I was looking for was "DEC 12|Wed| 5:00 pm" where am /pm is in lowercase and month is in uppercase. if its not straightforward, no big deal –  Jul 24 '19 at 02:43
  • I don't think there is a direct way until you replace in output string https://stackoverflow.com/questions/13581608/displaying-am-and-pm-in-lower-case-after-date-formatting @AngelaHeely – Ryuzaki L Jul 24 '19 at 02:45
  • 1
    Good link, @Deadpool. In particular [this answer](https://stackoverflow.com/a/42466448/5772882). – Ole V.V. Jul 24 '19 at 13:22
  • any idea about this one? : https://stackoverflow.com/questions/57695243/how-do-i-set-timezone-on-a-specific-date?noredirect=1#comment101834800_57695243 –  Aug 28 '19 at 15:25
3

A way to format uppercase DEC and lowercase pm

I’m just giving a small supplement to @Deadpool’s good answer. A way of getting the month abbreviation in all caps like DEC and am/pm in lowercase is a DateTimeFormatterBuilder and its two-arg appendText​(TemporalField, Map<Long,String>) method.

    Locale userLocale = Locale.US;
    Map<Long, String> monthNames = new HashMap<>();
    for (Month m : Month.values()) {
        monthNames.put(Long.valueOf(m.getValue()),
                m.getDisplayName(TextStyle.SHORT, userLocale).toUpperCase(userLocale));
    }
    Map<Long, String> amPmNames = new HashMap<>(3);
    amPmNames.put(0L, "am");
    amPmNames.put(1L, "pm");
    DateTimeFormatter outputFormatter = new DateTimeFormatterBuilder()
            .appendText(ChronoField.MONTH_OF_YEAR, monthNames)
            .appendPattern(" d | E | hh:mm ")
            .appendText(ChronoField.AMPM_OF_DAY, amPmNames)
            .toFormatter(userLocale);

    OffsetDateTime odt = OffsetDateTime.parse("2019-12-25T17:00:00-05:00");
    String formattedDate = odt.format(outputFormatter);
    System.out.println(formattedDate);

Output:

DEC 25 | Wed | 05:00 pm

I have assumed that you are programming for Android under API level 26 and using ThreeTenABP, so I have tested on my desktop Java 7 with ThreeTen Backport. From Java 8 and on newer API levels and even more from Java 9 more elegant ways to populate the two maps exist.

What went wrong in your code?

The lowercase z that comes last in your format pattern string matches a time zone name. The documentation gives examples Pacific Standard Time and PST. It doesn’t match an offset like -05:00. If you ever need a format pattern letter for this, use x, X or uppercase Z (they are different and all well documented). However, as already shown in Deadpool’s answer, you need no explicit formatter and no format pattern string in this case.

Another issue in your code: The LocalDate class that you used in the question is a date without time of day. You can parse your string into a LocalDate, but the time of day and offset get lost. Hence you cannot format the LocalDate into a string that contains time of day. For that you need a class that has DateTime in its name, for example OffsetDateTime used in the two answers.

Link

DateTimeFormatter documentation spelling out all the possible format pattern letters.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161