代码之家  ›  专栏  ›  技术社区  ›  Deb

如何使用Java 8时间API转换两位数年到全年

  •  9
  • Deb  · 技术社区  · 8 年前

    我想从我的项目中删除joda时间库。

    我正试着把两位数的年份转换成完整的年份。以下来自joda time的代码可以实现这个目的。以下是Joda时间的代码

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

    Stacktrace:

    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
    

    我没看到stacktrace的原因。如果有人能帮助我理解下面的情景,也可以解释如何使用Java 8 TimeAPI将两位数的年份转换成全年。

    5 回复  |  直到 8 年前
        1
  •  9
  •   Michael    8 年前

    问题是,不能将一年单独解析为localdate。localdate需要更多的信息。

    你可以使用 parse 格式化程序的方法,它将为您提供 TemporalAccessor ,然后从中获取年份字段:

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

    解决两者之间的差异:这是两个不同的api。是的,它们非常相似,而且 java.time 这个包是由jodatime的设计决定通知的,但它从来没有打算作为它的替代品。

    见 this answer 如果要更改数据透视年度(默认情况下,“99”将解析为2099,而不是1999)。

        2
  •  5
  •   daniu    8 年前

    你不能创建 LocalDate 因为 String 您提供的信息不足以填充所有必需的字段。

    您可以创建 Year 虽然

    Year parsed = Year.parse("99", TWO_YEAR_FORMATTER);
    int year = parsed.getValue();
    
        3
  •  5
  •   Anonymous    8 年前

    为什么不起作用

    java.time 不像joda time那样提供月和月的默认值。消息说它无法获得 LocalDate 仅从一年开始,或者相反,它缺少月和日(但是,您可以提供自己的默认值,如 Mikhail Kholodkov’s answer )一般来说,这种行为是为了帮助我们:它提醒我们提供所需的所有值,并从代码中明确说明是否使用了任何默认值,以及使用了哪些值。

    该怎么办

    只使用 Year 类 Java.时间 . 第一次申报

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

    产量

    一千九百九十九

    把你想要的基值插入我把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)。

        4
  •  1
  •   Mikhail Kholodkov    8 年前

    您需要为 DAY_OF_MONTH 和 MONTH_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使用的默认范围是2000-2099,而不是SimpleDateFormat 距离今天-80年到+20年。

    Full answer

    由于您只分析年份,为了将'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();
    
        5
  •  1
  •   Olivier Grégoire    8 年前

    你可以直接用 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,应该使用特定的格式化程序,如 the one suggested by Ole V.V. in their answer .