8236903: ZoneRules#getOffset throws DateTimeException for rules with last rules
Reviewed-by: scolebourne, rriggs
This commit is contained in:
parent
b7cdc097e3
commit
d517220df6
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -23,17 +23,20 @@
|
||||
|
||||
package test.java.time.zone;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.Month;
|
||||
import java.time.Year;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.zone.ZoneOffsetTransition;
|
||||
import java.time.zone.ZoneOffsetTransitionRule;
|
||||
import java.time.zone.ZoneRules;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.annotations.DataProvider;
|
||||
@ -41,11 +44,9 @@ import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @summary Test ZoneRules whether the savings are positive in time zones that have
|
||||
* negative savings in the source TZ files. Also, check the transition cutover
|
||||
* time beyond 24:00, which should translate into the next day.
|
||||
* @summary Tests for ZoneRules class.
|
||||
*
|
||||
* @bug 8212970
|
||||
* @bug 8212970 8236903
|
||||
*/
|
||||
@Test
|
||||
public class TestZoneRules {
|
||||
@ -107,6 +108,11 @@ public class TestZoneRules {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ZoneRules whether the savings are positive in time zones that have
|
||||
* negative savings in the source TZ files.
|
||||
* @bug 8212970
|
||||
*/
|
||||
@Test(dataProvider="negativeDST")
|
||||
public void test_NegativeDST(ZoneId zid, LocalDate ld, ZoneOffset offset, ZoneOffset stdOffset, boolean isDST) {
|
||||
Instant i = Instant.from(ZonedDateTime.of(ld, LocalTime.MIN, zid));
|
||||
@ -116,10 +122,53 @@ public class TestZoneRules {
|
||||
assertEquals(zr.isDaylightSavings(i), isDST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the transition cutover time beyond 24:00, which should translate into the next day.
|
||||
* @bug 8212970
|
||||
*/
|
||||
@Test(dataProvider="transitionBeyondDay")
|
||||
public void test_TransitionBeyondDay(ZoneId zid, LocalDateTime ldt, ZoneOffset before, ZoneOffset after) {
|
||||
ZoneOffsetTransition zot = ZoneOffsetTransition.of(ldt, before, after);
|
||||
ZoneRules zr = zid.getRules();
|
||||
assertTrue(zr.getTransitions().contains(zot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure ZoneRules.findYear() won't throw out-of-range DateTimeException for
|
||||
* year calculation.
|
||||
* @bug 8236903
|
||||
*/
|
||||
@Test
|
||||
public void test_TransitionLastRuleYear() {
|
||||
Instant maxLocalDateTime = LocalDateTime.of(Year.MAX_VALUE,
|
||||
12,
|
||||
31,
|
||||
23,
|
||||
59,
|
||||
59,
|
||||
999999999).toInstant(ZoneOffset.UTC);
|
||||
ZoneOffset offsetZero = ZoneOffset.ofHours(0);
|
||||
ZoneOffset offsetPlusOneHour = ZoneOffset.ofHours(1);
|
||||
ZoneRules zoneRulesA = ZoneRules.of(offsetPlusOneHour);
|
||||
ZoneOffsetTransition transition = ZoneOffsetTransition.of(LocalDateTime.ofEpochSecond(0, 0, offsetZero),
|
||||
offsetZero,
|
||||
offsetPlusOneHour);
|
||||
ZoneOffsetTransitionRule transitionRule = ZoneOffsetTransitionRule.of(Month.JANUARY,
|
||||
1,
|
||||
DayOfWeek.SUNDAY,
|
||||
LocalTime.MIDNIGHT,
|
||||
true,
|
||||
ZoneOffsetTransitionRule.TimeDefinition.STANDARD,
|
||||
offsetZero,
|
||||
offsetZero,
|
||||
offsetPlusOneHour);
|
||||
ZoneRules zoneRulesB = ZoneRules.of(offsetZero,
|
||||
offsetZero,
|
||||
Collections.singletonList(transition),
|
||||
Collections.singletonList(transition),
|
||||
Collections.singletonList(transitionRule));
|
||||
ZoneOffset offsetA = zoneRulesA.getOffset(maxLocalDateTime);
|
||||
ZoneOffset offsetB = zoneRulesB.getOffset(maxLocalDateTime);
|
||||
assertEquals(offsetA, offsetB);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user