问题
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!
回答1:
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);
回答2:
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.
来源:https://stackoverflow.com/questions/57174218/how-to-parse-a-utc-offset-dateformat-string-into-the-resulting-date-separated-by