【问题标题】:Parsing a date with short month without dot解析没有点的短月日期
【发布时间】:2010-12-10 18:39:41
【问题描述】:

我有一个字符串,它代表 法语 语言环境中的日期:09-oct-08:

我需要解析那个字符串,所以我想出了这个 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

【问题讨论】:

  • 您是如何在不付出任何额外努力的情况下获得oct 中的小o 的,顺便说一句?
  • -) 只是一些离题的评论。有趣的是,说英语的人是如何忘记了诸如特定于语言环境的转换、编码抓取符号等这些漂亮而有趣的问题的。伙计们,只是为了好玩 - 尝试将此消息输出到 Windows 控制台 - System.out.println("こんにちは!")。我保证 - 你会有很多有趣的麻烦:)
  • Vinegar,我认为它只是在这里工作的语言环境。法语语言环境显示并期望“oct”。英语语言环境显示并期望“Oct”的位置这是我的问题的一部分,因为我使用法语语言环境但不想使用点。
  • 抱歉标签,Esko。下次明白了。

标签: java locale date-format


【解决方案1】:

我遇到了同样的问题(法语和多余的点),我相信解决这个问题的正确方法是像这样全局覆盖法语语言环境:

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 法语对象有类似的点 janv._févr._mars_avr._...,所以我们只是删除它们。

这是link to docs,您可以在其中检查可以覆盖的内容。

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

【讨论】:

    【解决方案2】:

    java.time

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

    关于java.time

    Java 8 中内置的 java.time 框架取代了麻烦的旧 java.util.Date/.Calendar 类。新类的灵感来自非常成功的Joda-Time 框架,该框架旨在作为其继任者,在概念上相似但经过重新架构。由JSR 310 定义。由ThreeTen-Extra 项目扩展。请参阅Tutorial

    LocalDate

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

    法语缩写

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

    我们可以循环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

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

    修复数据源

    我的第一个建议是修复您的数据源。该来源显然没有遵循适当的法语缩写规则。 Yale 同意 Java 8 对法语的理解。顺便说一句,如果修复您的数据源,我强烈建议您使用四位数的年份作为两位,这会导致混乱和模棱两可。

    修复输入

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

    我会进行初始解析尝试。在尝试修复之前捕获DateTimeParseException。如果抛出异常,则修复输入。

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

    当我们找到匹配项时,将输入修正为正确缩写为区域设置规则(在我们的例子中是法语规则)。然后解析固定输入。这将是我们的第二次解析尝试,因为我们在顶部进行了初步尝试。如果第二次尝试失败,则如在此处看到的 FIXME: 中所述,出现了非常错误的情况。但通常第二次解析尝试会成功,我们可以跳出Month 枚举的for 循环。

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

    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

    【讨论】:

      【解决方案3】:

      这似乎有效:

          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")));
      

      编辑:看起来 St. Shadow 打败了我。

      【讨论】:

      • Yeeee...不要为法语工作:对于法语语言环境,我们将使用“févr”。作为 2 月,当你截断它时 - 我们将有“févr” - 这不能解析“fév”(正如作者在他的问题中所指导的那样)。需要 maula 设置所有 12 个月的日期,需要解析。
      • *如果您尝试解析日期,而不是格式化
      • 在我看来这是一个非常优雅的解决方案!但我不明白你为什么使用 dfEn ?为什么不直接使用您构建的 dfFr 来解析 String ?还有 St.Shadow,为什么这行不通?
      • 因为我看一下法语 shortMonthNames:它们是 [janv., févr., dontknow, avr., mai, juin, juil., août, sept., oct., nov., déc ., ] - 如您所见,这里的 ferbryary 是“févr”。如果您删除最后一个点 - 您将得到“févr”,这不是“fév”,并且您无法解析字符串“fév”(因为您在 cmets 中指导了这样的 febryary 名称)。如果您将收到正确的短月份名称,但仅没有点 - 此解决方案将很好用。
      • 对于截断月份,我使用«less letters» for (int i = 0; i
      【解决方案4】:

      好的,那就试试«brute force» :)

      DateFormatSymbols dfs = new DateFormatSymbols(Locale.FRENCH);
      String[] months = new String[13]
      <fill with correct month names or just replace these month, that are not fully correct>
      dfs.setMonths(months);
      SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yy", dfs);
      Date nweDate = sdf.parse("09-fév-08");
      

      【讨论】:

      • :-( 这不适用于“02-mai-10”,原因有两个:首先,“Mai”在英文中是“May”。其次,“Mai”没有点法语语言环境,作为 NOT 缩短的月份名称。
      • “March”、“June”、“July”和“August”也不是“标准化”(它们是“mars”、“juin”、“juil.”和“août”)。
      • 你可以用带/不带点的任何月份名称填充月份数组,依此类推。您甚至可以填写假名:例如:month[0] = "dontknow"。它会解析 sdf.parse("09-dontknow-08")。
      • 我喜欢 DateFormatSymbols 解决方案!我不知道这个类,API 不推荐直接使用它:-( 我可以看到你的月份数组大小为 13。为什么不是 12?
      • 另外我觉得我更应该使用 setShortMonths() 方法。
      【解决方案5】:

      您可以简单地删除“。”:

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

      编辑,关于 lemon 答案:

      使用 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[] 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 类,我看不到任何其他解决方案。

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

      【讨论】:

      • 我虽然想到了这一点,但我正试图做相反的事情......因为我的字符串没有点,而 SimpleDateFormat 似乎需要它来解析字符串......也许我可以添加我的输入字符串的点,但在我看来这不是非常“干净”的编码......
      • 我同意这不是一个好的解决方案,但它似乎是法国语言环境的 SimpleDateFormat 类中的一个“错误”(?)......
      • 请注意,我不是试图将日期格式化作为字符串,而是将传入的字符串解析作为日期。所以我传入的字符串中没有点,但是 SimpleDateFormat 需要这个点来“理解”月份......这是我的问题。
      • sdf = new SimpleDateFormat(format2, Locale.ENGLISH); System.out.println(sdf.parse("09-oct-08"));会好的。
      • 圣。 Shadow,正如我对 Lemon 所说,我无法使用英语语言环境,因为我收到的是法语日期。虽然 10 月份可以(法语为 10 月),但例如 2 月份会有问题(我会收到“fév”而不是“Feb”)。
      【解决方案6】:
      String format2 = "dd-MMM-yy";
      Date date = Calendar.getInstance().getTime();
      SimpleDateFormat sdf = new SimpleDateFormat(format2);
      System.out.println(sdf.format(date));
      

      输出28-Oct-09

      先生,我没有看到任何点。您是否尝试过重新检查您的打印件?也许你不小心在你的MMM旁边放了一个.


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

      【讨论】:

      • 那么语言环境呢?你设置法语语言环境 Locale.setDefault(Locale.FRANCE) 吗?
      • 我只是运行你的例子,我得到了输出28-oct.-09。您确定您使用的是法语语言环境吗? (我使用 Java 6)
      • 正如我所说,这是因为我正在使用法语语言环境...试试这个:DateFormat df2 = new SimpleDateFormat(format2,Locale.FRENCH);
      • Romaintaz,我的代码是:String format2 = "dd-MMM-yy"; DateFormat df2 = new SimpleDateFormat(format2,Locale.FRENCH);日期 = df2.parse("09-oct-08");这给了我: java.text.ParseException: Unparseable date: "09-oct-08" 如果我​​然后尝试记录: df2.format(new Date());我得到:28-oct.-09
      • 在 cmets 中格式化很糟糕,我正在更新我的主要问题。
      猜你喜欢
      • 1970-01-01
      • 2016-11-28
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-06
      相关资源
      最近更新 更多