如何简单地解析没有指定年份的日期?

dro*_*del 5 java groovy

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

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

我对这个脚本的问题是SimpleDateFormat似乎在转换后的1970年没有设置.我可以明确地将它设置为2011年,因为那是当前年份,但是当前年份与设定的日期之间似乎存在一些滞后,因此当新年到来时,此脚本将在该滞后时间出错.我该如何解决这个问题呢?一种解决方案是检查当前年份的日期是否已经过去,然后使用去年,但我希望有一个更简单的解决方案(或者说最简单的解决方案).

Ole*_*.V. 7

现代答案(从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;
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以这样做:

    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);
Run Code Online (Sandbox Code Playgroud)

今天(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年,班Date,SimpleDateFormat,CalendarGregorianCalendar现在早已过时的,所以我建议避免它们.现代课程通常对程序员更友好,并且带来更少的令人不快的惊喜.
  • 始终给出正确的时区.使用不正确的时区可能会导致未来日期时间的测试不准确,从而导致结果中断1年.
  • 始终提供明确的区域设置.在这种情况下,我把"Dec"用于英语并提供Locale.ENGLISH.
  • 由于您认为可以安全地假设日期在去年之内,我邀请您考虑是否也可以安全地假设它在过去的4,6或8个月内,例如?如果是这样,测试是否是这种情况将为您提供更好的验证:

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


Fil*_*efp 5

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

Calendar.getInstance ().get (Calendar.YEAR);
Run Code Online (Sandbox Code Playgroud)

示例代码段

OP详细阐述了他的问题,因此这个片段已被重写以符合他的需要.

  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"));
  }
Run Code Online (Sandbox Code Playgroud)

产量

2011年12月13日12:00:00

2010年12月31日12:00:00


记得要注意抛出Exceptions!