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

我有一个工具似乎给了我日期而没有指定我需要转换的年份,我正在使用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") 

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

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

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

示例代码段

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

产量

2011年12月13日12:00:00

2010年12月31日12:00:00


记得要小心抛出Exceptions

现代答案(从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个月内,例如? 如果是这样,测试是否是这种情况将为您提供更好的validation:

     if (dateTime.isBefore(now.minusMonths(5))) { throw new IllegalArgumentException("Not within the last 5 months: " + stringWithoutYear); } 
  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); 

OUTPUT:

 Tue Dec 13 12:00:00 IST 2011 

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

 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 
  GregorianCalendar now = new GregorianCalendar(); GregorianCalendar goal= new GregorianCalendar(now.get(Calendar.YEAR), month-1, day); if (goal.after(now)) { goal.add(Calendar.YEAR, -1); } 

目标有你更正的日期。