【发布时间】:2011-01-18 09:10:00
【问题描述】:
我需要在 Java 中解析格式为“2010 年 1 月 10 日”的日期。我该怎么做?
如何处理日期编号后面的ordinal indicators、st、nd、rd 或th?
【问题讨论】:
-
问题是“th”(大概是“rd”和“nd”),对吧?
-
谢谢大家...它通过转义字符来工作。
我需要在 Java 中解析格式为“2010 年 1 月 10 日”的日期。我该怎么做?
如何处理日期编号后面的ordinal indicators、st、nd、rd 或th?
【问题讨论】:
这行得通:
String s = "January 10th, 2010";
DateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy");
System.out.println("" + dateFormat.parse(s.replaceAll("(?:st|nd|rd|th),", "")));
但您需要确保使用正确的Locale 来正确解析月份名称。
我知道您可以在 SimpleDateFormat 模式中包含一般文本。但是在这种情况下,文本取决于信息,实际上与解析过程无关。
这实际上是我能想到的最简单的解决方案。但我很想被证明是错误的。
您可以通过执行类似以下操作来避免其中一个 cmets 中暴露的陷阱:
String s = "January 10th, 2010";
DateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy");
System.out.println("" + dateFormat.parse(s.replaceAll("(?<= \\d+)(?:st|nd|rd|th),(?= \\d+$)", "")));
这将允许您不匹配 Jath,uary 10 2010 例如。
【讨论】:
SimpleDataFormat 模式中包含一般文本。但是在这种情况下,文本取决于信息,实际上与解析过程无关。我知道的最简单的解决方案是这个。但我很想被错误地展示出来。
"January th,10 2010","Janth,uary th,10 2010" 等,这可能不是我们想要的行为。
dateStr.replaceAll("(?<=\\d)(?:st|nd|rd|th)", "")
java.util.Date、java.util.Calendar 和 java.text.SimpleDateFormat 等非常麻烦的日期时间类现在是 legacy,被 Java 8 及更高版本中内置的 java.time 类所取代.见Tutorial by Oracle。
我想提供现代答案。而不是今天两个投票最多的答案中使用的 SimpleDateFormat 类,您应该使用 java.time,现代 Java 日期和时间 API。它提供了几个不错的解决方案。
我们首先定义一个用于解析的格式化程序:
private static final DateTimeFormatter PARSING_FORMATTER = DateTimeFormatter.ofPattern(
"MMMM d['st']['nd']['rd']['th'], uuuu", Locale.ENGLISH);
然后我们这样使用它:
String dateString = "January 10th, 2010";
LocalDate date = LocalDate.parse(dateString, PARSING_FORMATTER);
System.out.println("Parsed date: " + date);
输出是:
解析日期:2010-01-10
格式模式字符串中的方括号[] 包含可选部分,单引号包含文字文本。所以d['st']['nd']['rd']['th'] 意味着在一个月的某一天之后可能有st、nd、rd 和/或th。
上述方法的一些限制是
10st 甚至10stndrdth。January 10stndrdth, 2010)。如果您想要更好地验证序数指示符,或者您希望将日期格式化回字符串,您可以通过这种方式构建格式化程序:
private static final DateTimeFormatter FORMATTING_AND_PARSING_FORMATTER;
static {
Map<Long, String> ordinalNumbers = new HashMap<>(42);
ordinalNumbers.put(1L, "1st");
ordinalNumbers.put(2L, "2nd");
ordinalNumbers.put(3L, "3rd");
ordinalNumbers.put(21L, "21st");
ordinalNumbers.put(22L, "22nd");
ordinalNumbers.put(23L, "23rd");
ordinalNumbers.put(31L, "31st");
for (long d = 1; d <= 31; d++) {
ordinalNumbers.putIfAbsent(d, "" + d + "th");
}
FORMATTING_AND_PARSING_FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("MMMM ")
.appendText(ChronoField.DAY_OF_MONTH, ordinalNumbers)
.appendPattern(", uuuu")
.toFormatter(Locale.ENGLISH);
}
这将解析与上面相同的日期字符串。让我们也试试它的格式:
System.out.println("Formatted back using the same formatter: "
+ date.format(FORMATTING_AND_PARSING_FORMATTER));
使用相同的格式化程序重新格式化:2010 年 1 月 10 日
【讨论】:
您可以将nd 等设置为SimpleDateFormat 中的文字。您可以定义四种需要的格式并尝试它们。首先从th 开始,因为我猜这会更频繁地发生。如果 ParseException 失败,请尝试下一个。如果全部失败,则抛出 ParseException。这里的代码只是一个概念。在现实生活中,您可能不会每次都生成新的格式,并且可能会考虑线程安全。
public static Date hoolaHoop(final String dateText) throws ParseException
{
ParseException pe=null;
String[] sss={"th","nd","rd","st"};
for (String special:sss)
{
SimpleDateFormat sdf=new SimpleDateFormat("MMMM d'"+special+",' yyyy");
try{
return sdf.parse(dateText);
}
catch (ParseException e)
{
// remember for throwing later
pe=e;
}
}
throw pe;
}
public static void main (String[] args) throws java.lang.Exception
{
String[] dateText={"January 10th, 2010","January 1st, 2010","January 2nd, 2010",""};
for (String dt:dateText) {System.out.println(hoolaHoop(dt))};
}
输出:
2010 年 1 月 10 日星期日 00:00:00 GMT
格林威治标准时间 2010 年 1 月 1 日星期五 00:00:00
2010 年 1 月 2 日星期六 00:00:00 GMT
线程“main”java.text.ParseException 中的异常:无法解析的日期:“”
"th","nd","rd","st" 当然只适用于使用英语的语言环境。记在脑子里。在法国,我猜是"re","nd" 等等。
【讨论】:
这是另一种简单的方法,但需要包含 apache commons jar。
import org.apache.commons.lang.time.*;
String s = "January 10th, 2010";
String[] freakyFormat = {"MMM dd'st,' yyyy","MMM dd'nd,' yyyy","MMM dd'th,' yyyy","MMM dd'rd,' yyyy"};
DateUtils du = new DateUtils();
System.out.println("" + du.parseDate(s,freakyFormat));
【讨论】: