在日期时间字符串中解析日期的序数指示符(st,nd,rd,th)

我检查了SimpleDateFormat javadoc ,但是我无法找到一种方法来解析日期格式的序数指示符 ,如下所示:

  Feb 13th 2015 9:00AM 

我试过"MMM dd yyyy hh:mma" ,但是这些日子必须在数量上才能正确吗?

是否可以使用SimpleDateFormat解析“第13”日期而不必截断字符串?

Java的SimpleDateFormat不支持序数后缀,但序数后缀只是眼睛糖果 – 它是多余的,可以轻松删除以允许简单的解析:

 Date date = new SimpleDateFormat("MMM dd yyyy hh:mma") .parse(str.replaceAll("(?<=\\d)(st|nd|rd|th)", "")); 

替换正则表达式非常简单,因为这些序列不会出现在有效日期的任何其他位置。


要处理任何语言,将任何语言的任意长度的序数指示符作为后缀附加:

 Date date = new SimpleDateFormat("MMM dd yyyy hh:mma") .parse(str.replaceAll("(?<=\\d)(?=\\D* \\d+ )\\p{L}+", "")); 

有些语言,例如普通话,会在它们的顺序指示符前面加上,但也可以使用替换来处理 - 左边作为读者的练习:)

Java 8答案(和Java 6和7)(因为在2015年提出这个问题时, SimpleDateFormat的替代品已经出来了):

  DateTimeFormatter parseFormatter = DateTimeFormatter .ofPattern("MMM d['st']['nd']['rd']['th'] uuuu h:mma", Locale.ENGLISH); LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, parseFormatter); 

来自问题的样本日期,这是yiedls:

 2015-02-13T09:00 

格式为[]表示可选部分, ''表示文字部分。 因此模式表明该数字可以跟随stndrdth

要在Java 6或7中使用它,您需要ThreeTen Backport 。 或者对于Android ThreeTenABP 。

由于这些后缀对于英语是特殊的,并且其他语言/语言环境完全有其他用于编写日期和时间的用法(也不使用AM / PM),我相信除非您有其他要求,否则您应该尝试实现此英语日期和时间。 此外,您应该明确地提供一个说英语的语言环境,以便它可以独立于您的计算机或JVM的语言环境设置工作。

我试图将Hugo和我自己的答案的最佳部分组合成一个重复的问题。 在那个重复的问题下,还有更多的java 8答案。 上述代码的一个限制是它没有非常严格的validation:你将在Feb 13rd Feb 13stndrdth甚至Feb 13stndrdth Feb 13rd Feb 13stndrdth

如果有人发现它有用:DateTimeFormatter构建器。 此格式化程序允许您使用序数后缀格式化和解析英国日期(例如“2017年1月1日”):

 public class UkDateFormatterBuilder { /** * The UK date formatter that formats a date without an offset, such as '14th September 2020' or '1st January 2017'. * @return an immutable formatter which uses the {@link ResolverStyle#SMART SMART} resolver style. It has no override chronology or zone. */ public DateTimeFormatter build() { return new DateTimeFormatterBuilder() .parseCaseInsensitive() .parseLenient() .appendText(DAY_OF_MONTH, dayOfMonthMapping()) .appendLiteral(' ') .appendText(MONTH_OF_YEAR, monthOfYearMapping()) .appendLiteral(' ') .appendValue(YEAR, 4) .toFormatter(Locale.UK); } private Map monthOfYearMapping() { Map monthOfYearMapping = new HashMap<>(); monthOfYearMapping.put(1L, "January"); monthOfYearMapping.put(2L, "February"); monthOfYearMapping.put(3L, "March"); monthOfYearMapping.put(4L, "April"); monthOfYearMapping.put(5L, "May"); monthOfYearMapping.put(6L, "June"); monthOfYearMapping.put(7L, "July"); monthOfYearMapping.put(8L, "August"); monthOfYearMapping.put(9L, "September"); monthOfYearMapping.put(10L, "October"); monthOfYearMapping.put(11L, "November"); monthOfYearMapping.put(12L, "December"); return monthOfYearMapping; } private Map dayOfMonthMapping() { Map suffixes = new HashMap<>(); for (int day=1; day<=31; day++) { suffixes.put((long)day, String.format("%s%s", (long) day, dayOfMonthSuffix(day))); } return suffixes; } private String dayOfMonthSuffix(final int day) { Preconditions.checkArgument(day >= 1 && day <= 31, "Illegal day of month: " + day); if (day >= 11 && day <= 13) { return "th"; } switch (day % 10) { case 1: return "st"; case 2: return "nd"; case 3: return "rd"; default: return "th"; } } } 

加上测试类的片段:

 public class UkDateFormatterBuilderTest { DateTimeFormatter formatter = new UkDateFormatterBuilder().build(); @Test public void shouldFormat1stJanuaryDate() { final LocalDate date = LocalDate.of(2017, 1, 1); final String formattedDate = date.format(formatter); Assert.assertEquals("1st January 2017", formattedDate); } @Test public void shouldParse1stJanuaryDate() { final String formattedDate = "1st January 2017"; final LocalDate parsedDate = LocalDate.parse(formattedDate, formatter); Assert.assertEquals(LocalDate.of(2017, 1, 1), parsedDate); } } 

PS。 我在这里使用了Greg Mattes的序数后缀解决方案: 你如何格式化月份在Java中说“11th”,“21st”或“23rd”? (序数指标)

您应该使用RuleBasedNumberFormat 。 它完美运行,并且尊重Locale。