在Java中解析法国日期

8
我收到了以下日期字符串。
10 juil 2014

在查找法语月份名称时,我发现juiljuillet的缩写,表示7月份。

我尝试使用French区域设置和SimpleDateFormat进行解析:

System.out.println(new SimpleDateFormat("dd MMM yyyy", Locale.FRENCH).parse("11 juil 2014"));

但是它会抛出一个异常

java.text.ParseException: Unparseable date: "11 juil 2014"
    at java.text.DateFormat.parse(DateFormat.java:357)

我尝试在月份名称后面添加一个句号。
System.out.println(new SimpleDateFormat("dd MMM yyyy", Locale.FRENCH).parse("11 juil. 2014"));

现在我得到了以下输出。
Fri Jul 11 00:00:00 EDT 2014

看起来我需要一个句号,但是当我尝试解析三月的日期(mars)时,如果加上句号,它将无法被识别。

我应该如何解析法国日期?我可以分两步进行:首先用句号,然后再不用句号,希望其中之一能奏效,但是有更好的方法吗?


1
你可能想要维护一个包含法语月份的表格。如果你的日期使用缩写,请加上句号,否则不用加。 - PM 77-1
5
也可以查看 DateFormatSymbols.getInstance(Locale.FRENCH).getShortMonths()。一些缩写名称甚至带有重音符号。 - tobias_k
2
也许法语缩写的月份名称必须有句号? - Raedwald
@tobias_k 谢谢,目前我只是按照PM77-1的建议硬编码一个地图,并根据需要进行转换。由于我没有太多的样本数据可用,这似乎是一个灵活的解决方案。 - MxLDevs
2个回答

5
在法语中,缩写的月份名称后面有一个句点。
请参见耶鲁大学图书馆的这个页面,月份名称缩写。列出了几十种语言。
“mars”是三月的全名(四个字母)。该名称太短,不需要缩写。没有缩写,所以没有句点。五月,“mai”,六月,“juin”,和八月,“août”也是一样。
此外,正如您可能已经注意到的那样,在法语中首字母是小写的,而在英语中是大写的。

Joda-Time

我在Mac OS X Mountain Lion上的Java 8中尝试了Joda-Time 2.4。[跳转到java.time,Joda-Time的替代品]
LocalDate localDate = DateTimeFormat.forPattern( "dd MMM yyyy" ).withLocale( java.util.Locale.FRENCH ).parseLocalDate( "10 juil 2014" );

同样的问题:缺少句号

juilletjuil.都可以成功解析为法语,但juil失败并抛出异常。月份缩写应该有一个句号终止符。

解决方法:插入句号

让我们使用substringlastIndexOf来拆分字符串,添加句号并重新构建字符串。

测试字符串是否包含:"janv"、"févr"、"avr"、"juil"、"sept"、"oct"、"nov"、"déc"。注意在两侧使用空格以防止获取到完整的月份名称而不是缩写。

String inputRaw = "10 juil 2014";
int indexOfSecondSpace = inputRaw.lastIndexOf( " " );
String input = inputRaw.substring( 0, indexOfSecondSpace ) + "." + inputRaw.substring( indexOfSecondSpace );
DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd MMM yyyy" ).withLocale( java.util.Locale.FRENCH );
LocalDate localDate = formatter.parseLocalDate( input );

System.out.println( inputRaw + " → " + input + " → " + localDate );

运行时。

10 juil 2014 → 10 juil. 2014 → 2014-07-10

或者调用replace进行替换:

  • " janv " → " janv. "
  • " févr " → " févr. "
  • " avr " → " avr. "
  • " juil " → " juil. "
  • " sept " → " sept. "
  • " oct " → " oct. "
  • " nov " → " nov. "
  • " déc " → " déc. "

合理性检查

在实际环境中,我会添加一些合理性检查来确保输入符合我们的期望,例如在中间有两个空格,在开头和结尾没有空格。

java.time

Java 8及更高版本内置了java.time框架。这些新类替代了已被证明设计不良、令人困惑且麻烦的旧java.util.Date/.Calendar和相关类。新的java.time类受到Joda-Time的启发,由JSR 310定义,由ThreeTen-Extra项目扩展,解释在Oracle教程中,并被回溯到Java 6和7,以及回溯到Android

java.time类包括方便的Month枚举getDisplayName生成本地化月份名称。

同样,DateTimeFormatter类也生成本地化文本。调用ofLocalized…方法。

System.out.println ( "US | Québec | France" );
for ( Month month : Month.values () ) {
    TextStyle style = TextStyle.SHORT;
    String us = month.getDisplayName ( style , Locale.US );
    String quebec = month.getDisplayName ( style , Locale.CANADA_FRENCH );
    String france = month.getDisplayName ( style , Locale.FRANCE );
    System.out.println ( us + " | " + quebec + " | " + france );
}

我们在java.time中得到了与Joda-Time相同的行为: 在法语中,缩写月份后面有一个句点。 月份名称全部小写。
US | Québec | France
Jan | janv. | janv.
Feb | févr. | févr.
Mar | mars | mars
Apr | avr. | avr.
May | mai | mai
Jun | juin | juin
Jul | juil. | juil.
Aug | août | août
Sep | sept. | sept.
Oct | oct. | oct.
Nov | nov. | nov.
Dec | déc. | déc.

亲爱的点踩者:请在您的点踩意见中留下建设性的批评。 - Basil Bourque

1

在 @tobias_k 的评论基础上,以下代码可以找到日期字符串中所有法语短月份缩写预期以句号结尾但未结尾的月份,并将其替换为正确的带句号缩写。

import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.text.DateFormatSymbols;

    public String fixFrenchMonths(String date) {
        for (String mois : DateFormatSymbols
                    .getInstance(Locale.FRENCH).getShortMonths()) {
            if (mois.endsWith(".")) {
                Pattern sansDot = Pattern.compile("(" +
                    Pattern.quote(mois.substring(0, mois.length()-1)) +
                    "(?!\\.))");
                Matcher matcher = sansDot.matcher(date);
                if (matcher.find()) {
                    date = matcher.replaceFirst(mois);
                }
            }
        }
        return date;
    }

注意:“mois”是法语中的“月份”,“sansDot”意思是“去掉点号”。这可能有些过于聪明了。它使用零宽度负向先行断言来确保不会替换已经包含点号缩写。它还在DateFormatSymbols的数据上使用Pattern.quote。这可能有些过度,因为我们不希望包括任何正则表达式元字符的字符(除了点本身,我们将其去掉),但是当从某些我们无法控制的地方传递数据到Pattern.compile时,最好保险一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接