【问题标题】:Why SimpleDateFormat("MM/dd/yyyy") parses date to 10/20/20128?为什么 SimpleDateFormat("MM/dd/yyyy") 将日期解析为 10/20/20128?
【发布时间】:2013-09-03 06:25:57
【问题描述】:

我有这个代码:

  DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
  dateFormat.setLenient(false);
  Date date = dateFormat.parse("10/20/20128");

并且我希望 dateFormat.parse 调用抛出 ParseException 因为我提供的年份是 5 个字符,而不是像我定义的格式中的 4 个字符。但出于某种原因,即使将 lenient 设置为 false,此调用也会返回 10/20/20128 的 Date 对象。

这是为什么呢?这对我来说没有多大意义。是否有其他设置使其更加严格?

【问题讨论】:

  • 如果将其解析为 4 位数字而不是 5 位数字,那将不是正确的年份。
  • 我相信如果您担心将其设置为 4 以防用户输入或输入错误,您必须在设置日期值之前进行检查。
  • 我不希望它解析为 4 位数字,我希望它失败(给我一个例外),因为我提供的值与确切的日期格式不匹配。跨度>
  • 您必须自己检查该值并在 Try 块中捕获您自己的异常。形成答案,Java 似乎支持 2 种格式的年份..yy 和全年。在将日期字符串分配给日期之前检查它。
  • 不会的。您必须对其进行编码。更新了我的答案。

标签: java date simpledateformat date-parsing


【解决方案1】:

参见java.text.SimpleDateFormat API,模式字母y:解析时,如果模式字母的个数超过2个,则按字面解释年份,而不考虑位数。

【讨论】:

    【解决方案2】:

    20128 年是有效的年份,Java 希望世界能活那么久。

    如果图案字母的个数超过2个,则年份为 按字面意思解释,与位数无关。

    Reference.

    如果你想验证一个日期是否有限制,你可以定义一个并检查-

    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
    Date maxDate = sdf.parse("01/01/2099");  // This is the limit
    
    if(someDate.after(maxDate)){            
        System.out.println("Invalid date");            
    }
    

    【讨论】:

    • 我明白,但提供的值与我提供的 MM/dd/yyyy 格式不匹配,所以它仍然应该失败。
    • @goe java 将超过 2 年的任何内容解释为提供的全年。我无法验证,但如果你做了 yyy,你仍然会得到所有 5 个数字。
    • @goe 这就是他们决定实施的方式。试试这个: System.out.println(new SimpleDateFormat("yyyyyyyyyy dddddd MMMMMMMM").format(new Date()));你会得到“0000002013 000030 August”
    • @goe 如果你想验证,你可以创建一个日历(比如 2150 年 1 月 1 日)和 getTimeInMillis() 或者只是硬编码并存储值并检查你验证的日期是否在之后存储的日期。如果是,则日期无效。
    • 如果您正在编写需要 4 位数年份的软件,使用“12/31/9999”作为您的maxDate。我们不需要另一个 Y21K 问题。
    【解决方案3】:

    javadoc

    年份:如果格式化程序的日历是公历,则 应用以下规则。

    • 对于格式化,如果模式字母个数为2,则年份截断为2位;否则它被解释为一个数字。
    • 对于解析,如果模式字母的数量超过 2 个,则按字面意思解释年份,无论位数如何。 所以使用模式 "MM/dd/yyyy", "01/11/12" 解析到 Jan 11, 12 公元
    • 为了使用缩写年份模式(“y”或“yy”)进行解析,SimpleDateFormat 必须解释相对于某些年份的缩写年份 世纪。它通过将日期调整到 80 年前 以及创建 SimpleDateFormat 实例后的 20 年。 例如,使用“MM/dd/yy”模式和 SimpleDateFormat
      实例创建于 1997 年 1 月 1 日,字符串“01/11/12”将是
      解释为 2012 年 1 月 11 日,而字符串“05/04/64”将是
      解释为 1964 年 5 月 4 日。在解析过程中,只有包含
      由 Character.isDigit(char) 定义的恰好是两位数 解析为默认世纪。任何其他数字字符串,例如 a
      一位数字字符串、三位或更多数字字符串或两位数字字符串 并非所有数字(例如,“-1”)都是按字面解释的。
      所以“01/02/3”或“01/02/003”被解析,使用相同的模式,如
      公元 3 年 1 月 2 日。同样,“01/02/-3”被解析为 Jan 2, 4 BC。
    • 否则,将应用日历系统特定的表格。对于格式化和解析,如果模式字母的数量为 4 或
      此外,使用了特定于日历的长格式。否则,日历
      使用了特定的缩写形式。

    因此,它将读取最后一个 / 之后的所有字符作为年份。

    【讨论】:

      【解决方案4】:

      在java中使用SimpleDateFormat,可以实现多种人类可读的格式。考虑这段代码 sn-p:

              Date curDate = new Date();
      
              SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
      
              String DateToStr = format.format(curDate);
              System.out.println(DateToStr);
      
              format = new SimpleDateFormat("dd-M-yyyy hh:mm:ss");
              DateToStr = format.format(curDate);
              System.out.println(DateToStr);
      
              format = new SimpleDateFormat("dd MMMM yyyy zzzz", Locale.ENGLISH);
              DateToStr = format.format(curDate);
              System.out.println(DateToStr);
      
              format = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
              DateToStr = format.format(curDate);
              System.out.println(DateToStr);
      

      查看各种格式生成的各种输出:

      2014/05/11

      11-5-2014 11:11:51

      2014 年 5 月 11 日东欧夏令时

      2014 年 5 月 11 日星期日 23:11:51 EEST

      随意修改格式字符串,你可能会得到想要的结果。

      【讨论】:

        【解决方案5】:

        java.time

        我始终建议您使用现代 Java 日期和时间 API java.time 进行日期工作。此外,java.time 会为您提供您要求的异常。

            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/uuuu")
                    .withResolverStyle(ResolverStyle.LENIENT);
            LocalDate date = LocalDate.parse("10/20/20128", dateFormatter);
        

        结果:

        线程“主”java.time.format.DateTimeParseException 中的异常: 无法在索引 6 处解析文本“10/20/20128”

        提到的 index 6 是 5 位数年份在您的字符串中的位置。

        这就是说,其他人建议的范围检查仍然是一个好主意。使用 java.time 这很容易。例如,我们不想接受任何未来的日期:

            LocalDate date = LocalDate.parse("10/20/8012", dateFormatter);
            if (date.isAfter(LocalDate.now(ZoneId.systemDefault()))) {
                System.err.println("Date must not be in the future, was " + date);
            }
        

        日期不得在未来,为 8012-10-20

        教程链接:Trail: Date Time (The Java™ Tutorials)解释如何使用java.time。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-12-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-22
          • 2020-07-29
          相关资源
          最近更新 更多