【问题标题】:How do I simply parse a date without a year specified?如何简单地解析没有指定年份的日期?
【发布时间】:2011-12-15 17:06:29
【问题描述】:

我有一个工具,它似乎给了我日期,但没有指定我需要转换的年份,我正在使用 Java 来完成这项任务(实际上是 Groovy,但在这种情况下足够接近)。一个示例日期是“13 Dec 12:00:00”,它应该是指 2011 年 12 月 13 日,因为没有指定年份,它是 2011 年。下面的 Groovy 脚本可以解决问题:

import java.text.*
println new SimpleDateFormat("dd MMM HH:mm:ss").parse("13 Dec 12:00:00")

我对这个脚本的问题是 SimpleDateFormat 似乎在转换后将年份设置为 1970 年。我可以将其明确设置为 2011 年,因为那是当前年份,但当前年份和设置的日期之间似乎存在一些滞后,所以当新年到来时,这个脚本会因为滞后时间而出错。我怎样才能简单地修复它?一种解决方案是检查当前年份的日期是否在现在之后,然后使用去年,但我希望存在一个更简单的解决方案(或者说如果没有,那就是最简单的)。

【问题讨论】:

  • 显然我并不清楚这个问题。我们的想法是将其设置为最近的一年,这样结果日期就不会在现在之后。假设您在 2012 年 2 月 1 日运行此脚本。在这种情况下,您希望返回日期为 2011 年 12 月 13 日,但如果您要求 2012 年 1 月 1 日,则返回日期为 1/1/ 2012.
  • 看看我的编辑,这就是你想要的。
  • 我推荐 Ole VV 的答案,然后根据 karakuricoder 的答案进行调整。
  • 仅供参考,非常麻烦的旧日期时间类,例如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle

标签: java groovy


【解决方案1】:

现代答案(从 2014 年起生效):我首先声明一个辅助方法:

private static LocalDateTime parseWithDefaultYear(String stringWithoutYear, int defaultYear) {
    DateTimeFormatter parseFormatter = new DateTimeFormatterBuilder()
            .appendPattern("dd MMM HH:mm:ss")
            .parseDefaulting(ChronoField.YEAR, defaultYear)
            .toFormatter(Locale.ENGLISH);
    LocalDateTime dateTime = LocalDateTime.parse(stringWithoutYear, parseFormatter);
    return dateTime;
}

那么我们可以这样做:

    ZoneId zone = ZoneId.of("America/Argentina/Jujuy");
    String stringWithoutYear = "13 Dec 12:00:00";
    LocalDateTime now = LocalDateTime.now(zone);
    int defaultYear = now.getYear();
    LocalDateTime dateTime = parseWithDefaultYear(stringWithoutYear, defaultYear);
    if (dateTime.isAfter(now)) { // in the future
        defaultYear--;
        dateTime = parseWithDefaultYear(stringWithoutYear, defaultYear);
    }
    System.out.println(dateTime);

今天(2018 年 6 月)运行时,会打印:

2017-12-13T12:00

您可能会问我为什么要使用新的格式化程序进行第二次解析,以防第一次尝试给出将来的日期。减去1年不是更简单吗?虽然java.time 对此当然有很好的支持,但我选择了第二个解析来更好地处理闰年 2 月 29 日的极端情况。想象一下,代码在 2021 年的前几个月运行,字符串为 29 Feb 12:00:00。由于 2021 年不是闰年,Java 将选择 2 月 28 日,即当月的最后一天。减去 1 年将得到 2020 年 2 月 28 日,这是不正确的,因为 2020 年是闰年。相反,我将 2020 年作为默认年份的新解析给出了正确的 2020 年 2 月 29 日。

消息:

  • 虽然其他答案在 2011 年是不错的答案,但 DateSimpleDateFormatCalendarGregorianCalendar 类现在早已过时,因此我建议避免使用它们。现代课程通常对程序员更友好,带来的不愉快惊喜也更少。
  • 始终提供正确的时区。使用不正确的时区可能会导致对未来日期时间的测试不准确,从而有可能导致结果延迟 1 年。
  • 始终提供明确的语言环境。在这种情况下,我选择了英文“Dec”并提供了Locale.ENGLISH
  • 既然您认为假设日期在过去一年内是安全的,我邀请您考虑一下假设它在过去 4、6 或 8 个月内是否也安全?如果是这样,测试是否是这种情况将为您提供更好的验证:

    if (dateTime.isBefore(now.minusMonths(5))) {
        throw new IllegalArgumentException("Not within the last 5 months: " + stringWithoutYear);
    }
    

【讨论】:

    【解决方案2】:

    您可以使用java.util.Calendar 获取当前年份,如下例所示。

    Calendar.getInstance ().get (Calendar.YEAR);
    

    示例 sn-p

    OP 详细阐述了他的问题,因此这个 sn-p 已被重写以满足他的需要。

      public static String fixDate (String data) throws Exception {
        SimpleDateFormat dateInputFormat  = new SimpleDateFormat("dd MMM HH:mm:ss");
        SimpleDateFormat dateOutputFormat = new SimpleDateFormat("yyyy dd MMM HH:mm:ss");
    
        Calendar currentCal = Calendar.getInstance ();
        Calendar parsedCal  = Calendar.getInstance ();
    
        int CAL_YEAR  = Calendar.YEAR;
    
        parsedCal.setTime  (dateInputFormat.parse (data));
        parsedCal.set      (CAL_YEAR, currentCal.get (CAL_YEAR));
    
        if (parsedCal.after (currentCal))
          parsedCal.set (CAL_YEAR, parsedCal.get (CAL_YEAR) - 1);
    
        // Date parsedDate = parsedCal.getTime ();
    
        return dateOutputFormat.format (parsedCal.getTime ());
      }
    
      public static void main (String[] args) throws Exception {
        System.out.println (fixDate ("13 Dec 12:00:00"));
        System.out.println (fixDate ("31 Dec 12:00:00"));
      }
    

    输出

    2011 年 12 月 13 日 12:00:00

    2010 年 12 月 31 日 12:00:00


    记得提防被抛出的Exceptions

    【讨论】:

    • 我知道我没有创建格式。我什至不确定格式是什么,所以我不得不猜测。它由 TeamCity 提供,用于不受服务器或代理管理的 CVS 构建。有一个关于它的错误报告,但尚未修复,作为一种解决方法,他们建议我同​​时将日期转换为合法日期。
    • 好的,上面的解决方案应该对你有用,如果给 Date 一个错误的日期,它会抛出一个异常,所以要小心。
    • 仅供参考,非常麻烦的旧日期时间类,例如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat,现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle
    【解决方案3】:
      String date_string = "13 Dec 12:00:00";
      SimpleDateFormat sdf = new SimpleDateFormat("dd MMM HH:mm:ss yyyy");
      Date date = sdf.parse(date_string + " " + Calendar.getInstance ().get(Calendar.YEAR));          
      System.out.println(date);
    

    输出:

    Tue Dec 13 12:00:00 IST 2011
    

    【讨论】:

      【解决方案4】:

      最简单的解决方案是在解析日期字符串之前将当前年份附加到日期字符串中。例如:

      def currentYear = new Date()[Calendar.YEAR]
      def d = Date.parse("dd MMM HH:mm:ss yyyy", "13 Dec 12:00:00 " + currentYear)
      println d
      ===> Tue Dec 13 12:00:00 MST 2011
      

      【讨论】:

        【解决方案5】:
            GregorianCalendar now = new GregorianCalendar();
        GregorianCalendar goal= new GregorianCalendar(now.get(Calendar.YEAR), month-1, day);
        if (goal.after(now)) {
            goal.add(Calendar.YEAR, -1);
        }
        

        目标有你更正的日期。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-30
          • 2012-08-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多