解析一个没有点的短月份的日期

我有一个字符串,表示法语区域的日期:09-oct-08:

我需要解析那个String,所以我想出了这个SimpleDateFormat:

String format2 = "dd-MMM-yy"; 

但是我对月份部分有一个问题,似乎预计会有一个结束点:

 df2.format(new Date()); 

给我 :

  28-oct.-09 

现在什么是让SimpleDateFormat理解的最佳方式(“09-oct-08”)?

完整代码:

 String format2 = "dd-MMM-yy"; DateFormat df2 = new SimpleDateFormat(format2,Locale.FRENCH); date = df2.parse("09-oct-08"); 

这给了我:java.text.ParseException:Unparseable date:“09-oct-08”

如果我然后尝试记录:

 df2.format(new Date()); 

我明白了:28-oct.-09

这似乎有效:

  DateFormatSymbols dfsFr = new DateFormatSymbols(Locale.FRENCH); String[] oldMonths = dfsFr.getShortMonths(); String[] newMonths = new String[oldMonths.length]; for (int i = 0, len = oldMonths.length; i < len; ++ i) { String oldMonth = oldMonths[i]; if (oldMonth.endsWith(".")) { newMonths[i] = oldMonth.substring(0, oldMonths[i].length() - 1); } else { newMonths[i] = oldMonth; } } dfsFr.setShortMonths(newMonths); DateFormat dfFr = new SimpleDateFormat( "dd-MMM-yy", dfsFr); // English date parser for creating some test data. DateFormat dfEn = new SimpleDateFormat( "dd-MMM-yy", Locale.ENGLISH); System.out.println(dfFr.format(dfEn.parse("10-Oct-09"))); System.out.println(dfFr.format(dfEn.parse("10-May-09"))); System.out.println(dfFr.format(dfEn.parse("10-Feb-09"))); 

编辑:看起来圣影打败了我。

您只需删除“。”:

 df2.format(new Date()).replaceAll("\\.", "")); 

编辑,关于柠檬答案:

使用Locale French时,格式似乎有问题。 因此,我建议你只使用. 正如我解释的那样去除

确实,以下代码:

  String format2 = "dd-MMM-yy"; Date date = Calendar.getInstance().getTime(); SimpleDateFormat sdf = new SimpleDateFormat(format2, Locale.FRENCH); System.out.println(sdf.format(date)); sdf = new SimpleDateFormat(format2, Locale.ENGLISH); System.out.println(sdf.format(date)); 

显示以下输出:

 28-oct.-09 28-Oct-09 

再次编辑

好的,我现在遇到了你的问题。

我不知道如何在不首先处理String的情况下解决这个问题。 我们的想法是将原始字符串中的月份替换为综合月份:

  String[] givenMonths = { "jan", "fév", "mars", "avr.", "mai", "juin", "juil", "août", "sept", "oct", "nov", "déc" }; String[] realMonths = { "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc." }; String original = "09-oct-08"; for (int i = 0; i < givenMonths.length; i++) { original = original.replaceAll(givenMonths[i], realMonths[i]); } String format2 = "dd-MMM-yy"; DateFormat df2 = new SimpleDateFormat(format2, Locale.FRENCH); Date date = df2.parse(original); System.out.println("--> " + date); 

我同意,这很糟糕,但如果您使用SimpleDateFormatDate类,我看不到任何其他解决方案。

另一种解决方案是使用真实的日期和时间库而不是原始的JDK,例如Joda Time 。

 String format2 = "dd-MMM-yy"; Date date = Calendar.getInstance().getTime(); SimpleDateFormat sdf = new SimpleDateFormat(format2); System.out.println(sdf.format(date)); 

产出于2009 28-Oct-09

先生,我没有看到任何点。 你试过重新检查你的照片吗? 也许你不小心放了一个. 在你的MMM旁边?


你得到java.text.ParseException: Unparseable date: "09-oct-08"因为"09-oct-08"Locale.FRENCH的格式不匹配要么使用默认语言环境(US我认为),要么添加一个. 在你的oct旁边

好的,然后尝试«暴力»:)

 DateFormatSymbols dfs = new DateFormatSymbols(Locale.FRENCH); String[] months = new String[13]  dfs.setMonths(months); SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yy", dfs); Date nweDate = sdf.parse("09-fév-08"); 

java.time

让我们看看java.time框架是否可以提供帮助。

关于java.time

Java 8及更高版本中内置的java.time框架取代了麻烦的旧java.util.Date/.Calendar类。 新课程的灵感来自非常成功的Joda-Time框架,旨在作为其inheritance者,在概念上类似但重新设计。 由JSR 310定义。 由ThreeTen-Extra项目扩展。 请参阅教程 。

LocalDate

与旧类不同,java.time提供LocalDate类来表示仅限日期的值,没有时间或时区。

法语缩略语

看一下java.time中格式化程序对enFrançais中缩写月份名称的期望。

我们可以循环Month枚举以获得Month列表。 此枚举提供了getDisplayName方法,用于生成月份的本地化名称。 此代码演示该方法生成与java.time格式化程序相同的输出。

 DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "dd-MMM-yyyy" ).withLocale ( Locale.FRENCH ); for ( Month month : Month.values () ) { LocalDate localDate = LocalDate.of ( 2015 , month.getValue () , 1 ); String output = formatter.format ( localDate ); String displayName = month.getDisplayName ( TextStyle.SHORT , Locale.FRENCH ); System.out.println ( "output: " + output + " | displayName: " + displayName );// System.out.println ( "input: " + input + " → " + localDate + " → " + output ); } 
 output: 01-janv.-2015 | displayName: janv. output: 01-févr.-2015 | displayName: févr. output: 01-mars-2015 | displayName: mars output: 01-avr.-2015 | displayName: avr. output: 01-mai-2015 | displayName: mai output: 01-juin-2015 | displayName: juin output: 01-juil.-2015 | displayName: juil. output: 01-août-2015 | displayName: août output: 01-sept.-2015 | displayName: sept. output: 01-oct.-2015 | displayName: oct. output: 01-nov.-2015 | displayName: nov. output: 01-déc.-2015 | displayName: déc. 

我们发现3到4个字母拼写的组合。 较长的名称缩写为四个字符加一个句点( FULL STOP )。 四个月的名字很短,可以不加缩写使用: marsmaijuinaoût

因此,正如其他答案中所讨论的,没有简单的解决方案。

修复数据源

我的第一个建议是修复您的数据源。 该来源显然无法遵循法国缩写的正确规则。 耶鲁同意Java 8对法语的理解。 顺便说一句,如果修复你的数据源,我强烈建议使用四位数的年份作为两个导致无法混淆和模糊的结束。

修复输入

当然,来源可能不受你的控制/影响。 在这种情况下,与其他答案一样,你可能需要做一个暴力替换而不是尝试任何聪明。 另一方面,如果输入的唯一问题是错过了句点(FULL STOP),那么您可以使用Month枚举进行软编码,而不是硬编码不正确的值。

我会做一个初步的解析尝试。 在尝试修复之前捕获DateTimeParseException陷阱。 如果抛出exception,则修复输入。

要修复输入,请通过循环可能的枚举实例集来尝试一年中的每个月。 每个月,获取其缩写名称。 从该缩写中删除句点(FULL STOP),以匹配我们怀疑的不正确传入值。 测试是否确实匹配输入。 如果没有,请转到下个月。

当我们得到匹配时,修改输入以适当缩写Locale的规则(在我们的例子中为法语规则)。 然后解析固定输入。 这将是我们的第二次解析尝试,因为我们初步尝试了。 如果第二次尝试失败,那么FIXME:指出的是非常错误的FIXME:在这里看到。 但通常第二次解析尝试会成功,我们可以摆脱Month枚举的for循环。

最后,您可以通过测试结果是否仍然是最初设置的伪标志值( LocalDate.MIN )来validation成功。

 String input = "09-oct-08"; // Last two digits are Year. DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "dd-MMM-yy" ).withLocale ( Locale.FRENCH ); LocalDate localDate = LocalDate.MIN; // Some folks prefer a bogus default value as a success/failure flag rather than using a NULL. try { localDate = LocalDate.parse ( input , formatter ); } catch ( DateTimeParseException e ) { // Look for any month name abbreviation improperly missing the period (FULL STOP). for ( Month month : Month.values () ) { String abbreviation = month.getDisplayName ( TextStyle.SHORT , Locale.FRENCH ); String abbreviationWithoutFullStop = abbreviation.replace ( "." , "" ); // Get short abbreviation, but drop any period (FULL STOP). String proper = "-" + abbreviation + "-"; String improper = "-" + abbreviationWithoutFullStop + "-"; if ( input.contains ( improper ) ) { String inputFixed = input.replace ( improper , proper ); try { localDate = LocalDate.parse ( inputFixed , formatter ); } catch ( DateTimeParseException e2 ) { // FIXME: Handle this error. We expected this second parse attempt to succeed. } break; // Bail-out of the loop as we got a hit, matching input with a particular improper value. } } } Boolean success = ! ( localDate.equals ( LocalDate.MIN ) ); String formatted = formatter.format ( localDate );; String outputImproper = formatted.replace ( "." , "" ); // Drop any period (FULL STOP). 

转储到控制台。

 System.out.println ( "success: " + success + ". input: " + input + " → localDate: " + localDate + " → formatted: " + formatted + " → outputImproper: " + outputImproper ); 

成功:真实。 输入:09-oct-08→localDate:2008-10-09→格式化:09-oct.-08→outputImproper:09-oct-08

我遇到了同样的问题(法语和额外的点)我相信解决这个问题的正确方法是全局覆盖法语区域设置,如下所示:

 import moment from 'moment'; moment.locale('fr', { monthsShort: 'janv_févr_mars_avr_mai_juin_juil_août_sept_oct_nov_déc'.split('_') }); 

原来的monthsShort法国对象monthsShortjanv._févr._mars_avr._... ,所以我们只是删除它们。

这是一个指向文档的链接 ,您可以在其中查看可以覆盖的内容。

请注意,如果我们只想覆盖ie: monthsShort ,我们不需要传递完整的语言环境对象。