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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.io.Serializable;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
@ -162,6 +161,16 @@ public final class ZoneRules implements Serializable {
|
|||||||
* The zero-length ldt array.
|
* The zero-length ldt array.
|
||||||
*/
|
*/
|
||||||
private static final LocalDateTime[] EMPTY_LDT_ARRAY = new LocalDateTime[0];
|
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.
|
* Obtains an instance of a ZoneRules.
|
||||||
@ -921,10 +930,34 @@ public final class ZoneRules implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int findYear(long epochSecond, ZoneOffset offset) {
|
private int findYear(long epochSecond, ZoneOffset offset) {
|
||||||
// inline for performance
|
|
||||||
long localSecond = epochSecond + offset.getTotalSeconds();
|
long localSecond = epochSecond + offset.getTotalSeconds();
|
||||||
long localEpochDay = Math.floorDiv(localSecond, 86400);
|
long zeroDay = Math.floorDiv(localSecond, 86400) + DAYS_0000_TO_1970;
|
||||||
return LocalDate.ofEpochDay(localEpochDay).getYear();
|
|
||||||
|
// 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,17 +23,20 @@
|
|||||||
|
|
||||||
package test.java.time.zone;
|
package test.java.time.zone;
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import java.time.DayOfWeek;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.Month;
|
||||||
|
import java.time.Year;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.zone.ZoneOffsetTransition;
|
import java.time.zone.ZoneOffsetTransition;
|
||||||
|
import java.time.zone.ZoneOffsetTransitionRule;
|
||||||
import java.time.zone.ZoneRules;
|
import java.time.zone.ZoneRules;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.DataProvider;
|
||||||
@ -41,11 +44,9 @@ import static org.testng.Assert.assertEquals;
|
|||||||
import static org.testng.Assert.assertTrue;
|
import static org.testng.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Test ZoneRules whether the savings are positive in time zones that have
|
* @summary Tests for ZoneRules class.
|
||||||
* negative savings in the source TZ files. Also, check the transition cutover
|
|
||||||
* time beyond 24:00, which should translate into the next day.
|
|
||||||
*
|
*
|
||||||
* @bug 8212970
|
* @bug 8212970 8236903
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public class TestZoneRules {
|
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")
|
@Test(dataProvider="negativeDST")
|
||||||
public void test_NegativeDST(ZoneId zid, LocalDate ld, ZoneOffset offset, ZoneOffset stdOffset, boolean isDST) {
|
public void test_NegativeDST(ZoneId zid, LocalDate ld, ZoneOffset offset, ZoneOffset stdOffset, boolean isDST) {
|
||||||
Instant i = Instant.from(ZonedDateTime.of(ld, LocalTime.MIN, zid));
|
Instant i = Instant.from(ZonedDateTime.of(ld, LocalTime.MIN, zid));
|
||||||
@ -116,10 +122,53 @@ public class TestZoneRules {
|
|||||||
assertEquals(zr.isDaylightSavings(i), isDST);
|
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")
|
@Test(dataProvider="transitionBeyondDay")
|
||||||
public void test_TransitionBeyondDay(ZoneId zid, LocalDateTime ldt, ZoneOffset before, ZoneOffset after) {
|
public void test_TransitionBeyondDay(ZoneId zid, LocalDateTime ldt, ZoneOffset before, ZoneOffset after) {
|
||||||
ZoneOffsetTransition zot = ZoneOffsetTransition.of(ldt, before, after);
|
ZoneOffsetTransition zot = ZoneOffsetTransition.of(ldt, before, after);
|
||||||
ZoneRules zr = zid.getRules();
|
ZoneRules zr = zid.getRules();
|
||||||
assertTrue(zr.getTransitions().contains(zot));
|
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