如何使用 Java 8 次 API 将两位数年份转换为完整年份

How to convert two digit year to full year using Java 8 time API

我希望从我的项目中删除 Joda-Time 库。

我正在尝试将两位数年份转换为全年。以下来自 Joda-Time 的代码可以实现这个目的。下面是joda-time

的以下代码
DateTimeFormatter TWO_YEAR_FORMATTER = DateTimeFormat.forPattern("yy");
int year = LocalDate.parse("99"", TWO_YEAR_FORMATTER).getYear();
System.out.println(year);

产量:1999

这是我期望的输出,在我的情况下是有意义的。但是,当我使用 java.time API 尝试相同的过程时,它会产生 DatetimeParseException。以下是 java.time API 的以下代码:

DateTimeFormatter TWO_YEAR_FORMATTER = DateTimeFormatter.ofPattern("yy");
int year = LocalDate.parse("99", TWO_YEAR_FORMATTER).getYear();
System.out.println(year);

堆栈跟踪:

Exception in thread "main" java.time.format.DateTimeParseException: Text '99' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {Year=2099},ISO of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.LocalDate.parse(LocalDate.java:428)
    at scratch.main(scratch.java:10)
Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {Year=2099},ISO of type java.time.format.Parsed
    at java.base/java.time.LocalDate.from(LocalDate.java:396)
    at java.base/java.time.format.Parsed.query(Parsed.java:235)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    ... 2 more

我没看到堆栈跟踪的原因。如果有人可以帮助我理解以下场景并解释如何使用 Java 8 次 API.

将两位数年份转换为全年,那就太好了

您无法创建 LocalDate,因为您提供的 String 没有提供填写所有必填字段所需的信息。

虽然

您可以创建Year
Year parsed = Year.parse("99", TWO_YEAR_FORMATTER);
int year = parsed.getValue();

问题是您无法将年份单独解析为 LocalDate。 LocalDate 需要比这更多的信息。

您可以使用格式化程序的 parse 方法,它会给您一个 TemporalAccessor,然后从中获取年份字段:

int year = TWO_YEAR_FORMATTER.parse("99").get(ChronoField.YEAR);
System.out.println(year);

解决两者之间的差异:这是两个不同的 API。是的,它们非常相似,并且 java.time 包是由 JodaTime 的设计决策所决定的,但它从未打算成为它的直接替代品。

如果您想更改基准年,请参阅 (默认情况下,“99”将解析为 2099 而不是 1999)。

您需要为 DAY_OF_MONTHMONTH_OF_YEAR

提供默认值
DateTimeFormatter TWO_YEAR_FORMATTER = new DateTimeFormatterBuilder()
                .appendPattern("yy")
                .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .toFormatter();

此外,

Java-8 uses the range 2000-2099 per default, not like SimpleDateFormat the range -80 years until +20 years relative to today.

由于您只解析年份,为了将“99”作为“1999”,您的代码应如下所示:

DateTimeFormatter TWO_YEAR_FORMATTER = new DateTimeFormatterBuilder()
                .appendPattern("")
                .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .appendValueReduced(ChronoField.YEAR_OF_ERA, 2, 2, LocalDate.now().minusYears(80))
                .toFormatter();

为什么它不起作用

java.time 不像 Joda-Time 那样提供月份和日期的默认值。该消息表示它无法仅从一年中获取 LocalDate,或者相反,它缺少月份和日期(不过,您可以提供自己的默认值,如 中所示)。通常这种行为是为了帮助我们:它提醒我们提供所有需要的值,并从代码中清楚地说明是否使用了任何默认值,以及使用了哪些默认值。

该怎么做

只需使用 java.timeYear class。先声明

public static final DateTimeFormatter TWO_YEAR_FORMATTER = new DateTimeFormatterBuilder()
        .appendValueReduced(ChronoField.YEAR, 2, 2, 1950)
        .toFormatter();

然后使用

    int year = Year.parse("99", TWO_YEAR_FORMATTER).getValue();
    System.out.println(year);

输出

1999

在我输入 1950 的地方插入你想要的基值,以指定从那一年算起的 99 年(含)内的年份。如果你想要过去的年份包括今年,你可以使用:

private static final Year base = Year.now(ZoneId.of("Asia/Kolkata")).minusYears(99);
public static final DateTimeFormatter TWO_YEAR_FORMATTER = new DateTimeFormatterBuilder()
        .appendValueReduced(ChronoField.YEAR, 2, 2, base.getValue())
        .toFormatter();

顺便说一句,不要相信我的话,但我认为 Joda-Time 使用当前年份减去 30 年作为基准或基准。如果是这样,在最后一个片段中使用 30 而不是 99 将是最兼容的(因此也会为您提供您期望的 1999)。

您可以直接使用 formatter.parse(input, Year::from) 解析年份:

import java.time.*;
import java.time.format.*;
class Main {
  public static void main(String[] args) {
    DateTimeFormatter TWO_YEAR_FORMATTER = DateTimeFormatter.ofPattern("yy");
    Year year = TWO_YEAR_FORMATTER.parse("99", Year::from);
    System.out.println(year);
  }
}

请注意,这将输出 2099。要输出 1999,您应该使用特定的格式化程序,例如 .