如何简单地解析没有指定年份的日期? [英] How do I simply parse a date without a year specified?

查看:18
本文介绍了如何简单地解析没有指定年份的日期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got a tool which seems to be giving me dates without specifying the year that I need to convert and I'm using Java for the task (actually Groovy but close enough in this case). An example date is "13 Dec 12:00:00", which should refer to 12/13/2011 since the year is unspecified and it is 2011. The following Groovy script does the trick:

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

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

My problem with this script is that SimpleDateFormat seems to be leaving the year unset at 1970 after conversion. I could explicitly set it to 2011 because that is the current year but there seems to be some lag between current year and the dates set so when New Years comes this script will get it wrong for that lag time. How can I fix it up simply? One solution would be to check if the date with current year is after now then use the last year but I'm hoping a simpler solution exists (or state that's the simplest if not).

推荐答案

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

Modern answer (valid from 2014 and on): I first declare an auxiliary method:

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 月)运行时会打印:

When running today (June 2018) this prints:

2017-12-13T12:00

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 日.

You may ask why I want to parse a second time with a new formatter in case the first attempt gives a date in the future. Wouldn’t it be simpler just to subtract 1 year? While java.time certainly has good support for this, I chose a second parsing to handle the corner case of February 29 in leap years better. Imagine the code is run in the first couple of months of 2021 and the string is 29 Feb 12:00:00. Since 2021 is not a leap year, Java will choose February 28, the last day of the month. Subtracting 1 year would give February 28, 2020, which would be incorrect since 2020 is a leap year. Instead my new parsing with 2020 as default year gives the correct February 29, 2020.

消息:

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

  • While the other answers were good answers in 2011, the classes Date, SimpleDateFormat, Calendar and GregorianCalendar are now long outdated, so I recommend avoiding them. The modern classes generally are more programmer friendly and come with fewer unpleasant surprises.
  • Always give correct time zone. Using an incorrect time zone is likely to cause the test for a future date-time to be inaccurate, risking that the result is off by 1 year.
  • Always give explicit locale. In this case I took "Dec" for English and provided Locale.ENGLISH.
  • Since you consider it safe to assume that the date is within the last year, I invite you to consider whether is it also safe to assume it’s within the last 4, 6 or 8 months, for example? If so, testing whether this is the case will give you a better validation:

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

这篇关于如何简单地解析没有指定年份的日期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆