我检查了SimpleDateFormat
javadoc,但我无法找到以这样的日期格式解析ordinal indicator的方法:
Feb 13th 2015 9:00AM
我试过了"MMM dd yyyy hh:mma"
,但为了正确,日期必须是多少?
是否可以使用SimpleDateFormat
解析“第13个”日期而不必截断字符串?
答案 0 :(得分:19)
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}+", ""));
有些语言,例如普通话,在它们的顺序指示符之前,但也可以使用替换处理 - 左侧作为读者的练习:)
答案 1 :(得分:5)
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);
使用此问题的样本日期:
2015-02-13T09:00
格式模式[]
表示可选部分,''
表示文字部分。因此,该模式表示该数字可能后跟st
,nd
,rd
或th
。
要在Java 6或7中使用它,您需要ThreeTen Backport。或者针对Android ThreeTenABP。
由于这些后缀对于英语是特殊的,并且其他语言/语言环境完全有其他用于写日期和时间的用法(也不使用AM / PM),我相信除非您有其他要求,否则您应该尝试仅针对英语日期和时间实施此操作。此外,您应该明确地提供一个说英语的语言环境,以便它可以独立于您的计算机或JVM的语言环境设置工作。
我尝试将Hugo和myself的答案的最佳部分组合成一个重复的问题。在that duplicate question下,还有更多的java 8答案。上述代码的一个限制是它没有非常严格的验证:您将获得Feb 13rd
甚至Feb 13stndrdth
。
答案 2 :(得分:2)
如果有人发现它有用:DateTimeFormatter构建器。此格式化程序允许您使用序数后缀格式化和解析英国日期(例如,&#34; 2017年1月1日和#34;):
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<Long, String> monthOfYearMapping()
{
Map<Long, String> 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<Long, String> dayOfMonthMapping()
{
Map<Long, String> 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&#39;这里有序后缀的解决方案: How do you format the day of the month to say "11th", "21st" or "23rd" in Java? (ordinal indicator)
答案 3 :(得分:0)
您应该使用RuleBasedNumberFormat。它完美无缺,并且尊重Locale。