8236903: ZoneRules#getOffset throws DateTimeException for rules with last rules

Reviewed-by: scolebourne, rriggs
This commit is contained in:
Naoto Sato 2020-01-22 12:35:24 -08:00
parent b7cdc097e3
commit d517220df6
2 changed files with 94 additions and 12 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -69,7 +69,6 @@ import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
@ -162,6 +161,16 @@ public final class ZoneRules implements Serializable {
* The zero-length ldt array.
*/
private static final LocalDateTime[] EMPTY_LDT_ARRAY = new LocalDateTime[0];
/**
* The number of days in a 400 year cycle.
*/
private static final int DAYS_PER_CYCLE = 146097;
/**
* The number of days from year zero to year 1970.
* There are five 400 year cycles from year zero to 2000.
* There are 7 leap years from 1970 to 2000.
*/
private static final long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L);
/**
* Obtains an instance of a ZoneRules.
@ -921,10 +930,34 @@ public final class ZoneRules implements Serializable {
}
private int findYear(long epochSecond, ZoneOffset offset) {
// inline for performance
long localSecond = epochSecond + offset.getTotalSeconds();
long localEpochDay = Math.floorDiv(localSecond, 86400);
return LocalDate.ofEpochDay(localEpochDay).getYear();
long zeroDay = Math.floorDiv(localSecond, 86400) + DAYS_0000_TO_1970;
// find the march-based year
zeroDay -= 60; // adjust to 0000-03-01 so leap day is at end of four year cycle
long adjust = 0;
if (zeroDay < 0) {
// adjust negative years to positive for calculation
long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1;
adjust = adjustCycles * 400;
zeroDay += -adjustCycles * DAYS_PER_CYCLE;
}
long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE;
long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
if (doyEst < 0) {
// fix estimate
yearEst--;
doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
}
yearEst += adjust; // reset any negative year
int marchDoy0 = (int) doyEst;
// convert march-based values back to january-based
int marchMonth0 = (marchDoy0 * 5 + 2) / 153;
yearEst += marchMonth0 / 10;
// Cap to the max value
return (int)Math.min(yearEst, Year.MAX_VALUE);
}
/**