undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
424
test/jdk/java/util/TimeZone/TimeZoneBoundaryTest.java
Normal file
424
test/jdk/java/util/TimeZone/TimeZoneBoundaryTest.java
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8347841
|
||||
* @summary test Time Zone Boundary
|
||||
* @run junit TimeZoneBoundaryTest
|
||||
*/
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.SimpleTimeZone;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* A test which discovers the boundaries of DST programmatically and verifies
|
||||
* that they are correct.
|
||||
*/
|
||||
public class TimeZoneBoundaryTest
|
||||
{
|
||||
static final int ONE_SECOND = 1000;
|
||||
static final int ONE_MINUTE = 60*ONE_SECOND;
|
||||
static final int ONE_HOUR = 60*ONE_MINUTE;
|
||||
static final long ONE_DAY = 24*ONE_HOUR;
|
||||
static final long ONE_YEAR = (long)(365.25 * ONE_DAY);
|
||||
static final long SIX_MONTHS = ONE_YEAR / 2;
|
||||
|
||||
static final int MONTH_LENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
|
||||
|
||||
// These values are empirically determined to be correct
|
||||
static final long PST_1997_BEG = 860320800000L;
|
||||
static final long PST_1997_END = 877856400000L;
|
||||
|
||||
// Minimum interval for binary searches in ms; should be no larger
|
||||
// than 1000.
|
||||
static final long INTERVAL = 10; // Milliseconds
|
||||
|
||||
static final String AUSTRALIA = "Australia/Adelaide";
|
||||
static final long AUSTRALIA_1997_BEG = 877797000000L;
|
||||
static final long AUSTRALIA_1997_END = 859653000000L;
|
||||
|
||||
/**
|
||||
* Date.toString().substring() Boundary Test
|
||||
* Look for a DST changeover to occur within 6 months of the given Date.
|
||||
* The initial Date.toString() should yield a string containing the
|
||||
* startMode as a SUBSTRING. The boundary will be tested to be
|
||||
* at the expectedBoundary value.
|
||||
*/
|
||||
void findDaylightBoundaryUsingDate(Date d, String startMode, long expectedBoundary)
|
||||
{
|
||||
// Given a date with a year start, find the Daylight onset
|
||||
// and end. The given date should be 1/1/xx in some year.
|
||||
|
||||
if (d.toString().indexOf(startMode) == -1)
|
||||
{
|
||||
System.out.println("Error: " + startMode + " not present in " + d);
|
||||
}
|
||||
|
||||
// Use a binary search, assuming that we have a Standard
|
||||
// time at the midpoint.
|
||||
long min = d.getTime();
|
||||
long max = min + SIX_MONTHS;
|
||||
|
||||
while ((max - min) > INTERVAL)
|
||||
{
|
||||
long mid = (min + max) >> 1;
|
||||
String s = new Date(mid).toString();
|
||||
// logln(s);
|
||||
if (s.indexOf(startMode) != -1)
|
||||
{
|
||||
min = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = mid;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Date Before: " + showDate(min));
|
||||
System.out.println("Date After: " + showDate(max));
|
||||
long mindelta = expectedBoundary - min;
|
||||
long maxdelta = max - expectedBoundary;
|
||||
if (mindelta >= 0 && mindelta <= INTERVAL &&
|
||||
mindelta >= 0 && mindelta <= INTERVAL)
|
||||
System.out.println("PASS: Expected boundary at " + expectedBoundary);
|
||||
else
|
||||
fail("FAIL: Expected boundary at " + expectedBoundary);
|
||||
}
|
||||
|
||||
void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST, long expectedBoundary)
|
||||
{
|
||||
findDaylightBoundaryUsingTimeZone(d, startsInDST, expectedBoundary,
|
||||
TimeZone.getDefault());
|
||||
}
|
||||
|
||||
void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST,
|
||||
long expectedBoundary, TimeZone tz)
|
||||
{
|
||||
// Given a date with a year start, find the Daylight onset
|
||||
// and end. The given date should be 1/1/xx in some year.
|
||||
|
||||
// Use a binary search, assuming that we have a Standard
|
||||
// time at the midpoint.
|
||||
long min = d.getTime();
|
||||
long max = min + SIX_MONTHS;
|
||||
|
||||
if (tz.inDaylightTime(d) != startsInDST)
|
||||
{
|
||||
fail("FAIL: " + tz.getID() + " inDaylightTime(" +
|
||||
d + ") != " + startsInDST);
|
||||
startsInDST = !startsInDST; // Flip over; find the apparent value
|
||||
}
|
||||
|
||||
if (tz.inDaylightTime(new Date(max)) == startsInDST)
|
||||
{
|
||||
fail("FAIL: " + tz.getID() + " inDaylightTime(" +
|
||||
(new Date(max)) + ") != " + (!startsInDST));
|
||||
return;
|
||||
}
|
||||
|
||||
while ((max - min) > INTERVAL)
|
||||
{
|
||||
long mid = (min + max) >> 1;
|
||||
boolean isIn = tz.inDaylightTime(new Date(mid));
|
||||
if (isIn == startsInDST)
|
||||
{
|
||||
min = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = mid;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(tz.getID() + " Before: " + showDate(min, tz));
|
||||
System.out.println(tz.getID() + " After: " + showDate(max, tz));
|
||||
|
||||
long mindelta = expectedBoundary - min;
|
||||
long maxdelta = max - expectedBoundary;
|
||||
if (mindelta >= 0 && mindelta <= INTERVAL &&
|
||||
mindelta >= 0 && mindelta <= INTERVAL)
|
||||
System.out.println("PASS: Expected boundary at " + expectedBoundary);
|
||||
else
|
||||
fail("FAIL: Expected boundary at " + expectedBoundary);
|
||||
}
|
||||
|
||||
private static String showDate(long l)
|
||||
{
|
||||
return showDate(new Date(l));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static String showDate(Date d)
|
||||
{
|
||||
return "" + d.getYear() + "/" + showNN(d.getMonth()+1) + "/" + showNN(d.getDate()) +
|
||||
" " + showNN(d.getHours()) + ":" + showNN(d.getMinutes()) +
|
||||
" \"" + d + "\" = " +
|
||||
d.getTime();
|
||||
}
|
||||
|
||||
private static String showDate(long l, TimeZone z)
|
||||
{
|
||||
return showDate(new Date(l), z);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static String showDate(Date d, TimeZone zone)
|
||||
{
|
||||
DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
|
||||
fmt.setTimeZone(zone);
|
||||
return "" + d.getYear() + "/" + showNN(d.getMonth()+1) + "/" + showNN(d.getDate()) +
|
||||
" " + showNN(d.getHours()) + ":" + showNN(d.getMinutes()) +
|
||||
" \"" + d + "\" = " +
|
||||
fmt.format(d);
|
||||
}
|
||||
|
||||
private static String showNN(int n)
|
||||
{
|
||||
return ((n < 10) ? "0" : "") + n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a date, a TimeZone, and expected values for inDaylightTime,
|
||||
* useDaylightTime, zone and DST offset, verify that this is the case.
|
||||
*/
|
||||
void verifyDST(Date d, TimeZone time_zone,
|
||||
boolean expUseDaylightTime, boolean expInDaylightTime,
|
||||
int expZoneOffset, int expDSTOffset)
|
||||
{
|
||||
System.out.println("-- Verifying time " + d +
|
||||
" in zone " + time_zone.getID());
|
||||
|
||||
if (time_zone.inDaylightTime(d) == expInDaylightTime)
|
||||
System.out.println("PASS: inDaylightTime = " + time_zone.inDaylightTime(d));
|
||||
else
|
||||
fail("FAIL: inDaylightTime = " + time_zone.inDaylightTime(d));
|
||||
|
||||
if (time_zone.useDaylightTime() == expUseDaylightTime)
|
||||
System.out.println("PASS: useDaylightTime = " + time_zone.useDaylightTime());
|
||||
else
|
||||
fail("FAIL: useDaylightTime = " + time_zone.useDaylightTime());
|
||||
|
||||
if (time_zone.getRawOffset() == expZoneOffset)
|
||||
System.out.println("PASS: getRawOffset() = " + expZoneOffset/(double)ONE_HOUR);
|
||||
else
|
||||
fail("FAIL: getRawOffset() = " + time_zone.getRawOffset()/(double)ONE_HOUR +
|
||||
"; expected " + expZoneOffset/(double)ONE_HOUR);
|
||||
|
||||
GregorianCalendar gc = new GregorianCalendar(time_zone);
|
||||
gc.setTime(d);
|
||||
int offset = time_zone.getOffset(gc.get(gc.ERA), gc.get(gc.YEAR), gc.get(gc.MONTH),
|
||||
gc.get(gc.DAY_OF_MONTH), gc.get(gc.DAY_OF_WEEK),
|
||||
((gc.get(gc.HOUR_OF_DAY) * 60 +
|
||||
gc.get(gc.MINUTE)) * 60 +
|
||||
gc.get(gc.SECOND)) * 1000 +
|
||||
gc.get(gc.MILLISECOND));
|
||||
if (offset == expDSTOffset)
|
||||
System.out.println("PASS: getOffset() = " + offset/(double)ONE_HOUR);
|
||||
else
|
||||
fail("FAIL: getOffset() = " + offset/(double)ONE_HOUR +
|
||||
"; expected " + expDSTOffset/(double)ONE_HOUR);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void TestBoundaries()
|
||||
{
|
||||
TimeZone pst = TimeZone.getTimeZone("America/Los_Angeles");
|
||||
TimeZone save = TimeZone.getDefault();
|
||||
try {
|
||||
TimeZone.setDefault(pst);
|
||||
|
||||
// DST changeover for PST is 4/6/1997 at 2 hours past midnight
|
||||
Date d = new Date(97,Calendar.APRIL,6);
|
||||
|
||||
// i is minutes past midnight standard time
|
||||
for (int i=60; i<=180; i+=15)
|
||||
{
|
||||
boolean inDST = (i >= 120);
|
||||
Date e = new Date(d.getTime() + i*60*1000);
|
||||
verifyDST(e, pst, true, inDST, -8*ONE_HOUR,
|
||||
inDST ? -7*ONE_HOUR : -8*ONE_HOUR);
|
||||
}
|
||||
|
||||
System.out.println("========================================");
|
||||
findDaylightBoundaryUsingDate(new Date(97,0,1), "PST", PST_1997_BEG);
|
||||
System.out.println("========================================");
|
||||
findDaylightBoundaryUsingDate(new Date(97,6,1), "PDT", PST_1997_END);
|
||||
|
||||
// Southern hemisphere test
|
||||
System.out.println("========================================");
|
||||
TimeZone z = TimeZone.getTimeZone(AUSTRALIA);
|
||||
findDaylightBoundaryUsingTimeZone(new Date(97,0,1), true, AUSTRALIA_1997_END, z);
|
||||
|
||||
System.out.println("========================================");
|
||||
findDaylightBoundaryUsingTimeZone(new Date(97,0,1), false, PST_1997_BEG);
|
||||
System.out.println("========================================");
|
||||
findDaylightBoundaryUsingTimeZone(new Date(97,6,1), true, PST_1997_END);
|
||||
} finally {
|
||||
TimeZone.setDefault(save);
|
||||
}
|
||||
}
|
||||
|
||||
void testUsingBinarySearch(SimpleTimeZone tz, Date d, long expectedBoundary)
|
||||
{
|
||||
// Given a date with a year start, find the Daylight onset
|
||||
// and end. The given date should be 1/1/xx in some year.
|
||||
|
||||
// Use a binary search, assuming that we have a Standard
|
||||
// time at the midpoint.
|
||||
long min = d.getTime();
|
||||
long max = min + (long)(365.25 / 2 * ONE_DAY);
|
||||
|
||||
// First check the boundaries
|
||||
boolean startsInDST = tz.inDaylightTime(d);
|
||||
|
||||
if (tz.inDaylightTime(new Date(max)) == startsInDST)
|
||||
{
|
||||
System.out.println("Error: inDaylightTime(" + (new Date(max)) + ") != " + (!startsInDST));
|
||||
}
|
||||
|
||||
while ((max - min) > INTERVAL)
|
||||
{
|
||||
long mid = (min + max) >> 1;
|
||||
if (tz.inDaylightTime(new Date(mid)) == startsInDST)
|
||||
{
|
||||
min = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = mid;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Binary Search Before: " + showDate(min));
|
||||
System.out.println("Binary Search After: " + showDate(max));
|
||||
|
||||
long mindelta = expectedBoundary - min;
|
||||
long maxdelta = max - expectedBoundary;
|
||||
if (mindelta >= 0 && mindelta <= INTERVAL &&
|
||||
mindelta >= 0 && mindelta <= INTERVAL)
|
||||
System.out.println("PASS: Expected boundary at " + expectedBoundary);
|
||||
else
|
||||
fail("FAIL: Expected boundary at " + expectedBoundary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test new rule formats.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void TestNewRules() {
|
||||
// Doesn't matter what the default TimeZone is here, since we
|
||||
// are creating our own TimeZone objects.
|
||||
|
||||
SimpleTimeZone tz;
|
||||
|
||||
System.out.println("-----------------------------------------------------------------");
|
||||
System.out.println("Aug 2ndTues .. Mar 15");
|
||||
tz = new SimpleTimeZone(-8*ONE_HOUR, "Test_1",
|
||||
Calendar.AUGUST, 2, Calendar.TUESDAY, 2*ONE_HOUR,
|
||||
Calendar.MARCH, 15, 0, 2*ONE_HOUR);
|
||||
System.out.println("========================================");
|
||||
testUsingBinarySearch(tz, new Date(97,0,1), 858416400000L);
|
||||
System.out.println("========================================");
|
||||
testUsingBinarySearch(tz, new Date(97,6,1), 871380000000L);
|
||||
|
||||
System.out.println("-----------------------------------------------------------------");
|
||||
System.out.println("Apr Wed>=14 .. Sep Sun<=20");
|
||||
tz = new SimpleTimeZone(-8*ONE_HOUR, "Test_2",
|
||||
Calendar.APRIL, 14, -Calendar.WEDNESDAY, 2*ONE_HOUR,
|
||||
Calendar.SEPTEMBER, -20, -Calendar.SUNDAY, 2*ONE_HOUR);
|
||||
System.out.println("========================================");
|
||||
testUsingBinarySearch(tz, new Date(97,0,1), 861184800000L);
|
||||
System.out.println("========================================");
|
||||
testUsingBinarySearch(tz, new Date(97,6,1), 874227600000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find boundaries by stepping.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
void findBoundariesStepwise(int year, long interval, TimeZone z, int expectedChanges)
|
||||
{
|
||||
Date d = new Date(year - 1900, Calendar.JANUARY, 1);
|
||||
long time = d.getTime(); // ms
|
||||
long limit = time + ONE_YEAR + ONE_DAY;
|
||||
boolean lastState = z.inDaylightTime(d);
|
||||
int changes = 0;
|
||||
System.out.println("-- Zone " + z.getID() + " starts in " + year + " with DST = " + lastState);
|
||||
System.out.println("useDaylightTime = " + z.useDaylightTime());
|
||||
while (time < limit)
|
||||
{
|
||||
d.setTime(time);
|
||||
boolean state = z.inDaylightTime(d);
|
||||
if (state != lastState)
|
||||
{
|
||||
System.out.println((state ? "Entry " : "Exit ") +
|
||||
"at " + d);
|
||||
lastState = state;
|
||||
++changes;
|
||||
}
|
||||
time += interval;
|
||||
}
|
||||
if (changes == 0)
|
||||
{
|
||||
if (!lastState && !z.useDaylightTime()) System.out.println("No DST");
|
||||
else fail("FAIL: Timezone<" + z.getID() + "> DST all year, or no DST with true useDaylightTime");
|
||||
}
|
||||
else if (changes != 2)
|
||||
{
|
||||
fail("FAIL: Timezone<" + z.getID() + "> " + changes + " changes seen; should see 0 or 2");
|
||||
}
|
||||
else if (!z.useDaylightTime())
|
||||
{
|
||||
fail("FAIL: Timezone<" + z.getID() + "> useDaylightTime false but 2 changes seen");
|
||||
}
|
||||
if (changes != expectedChanges)
|
||||
{
|
||||
fail("FAIL: Timezone<" + z.getID() + "> " + changes + " changes seen; expected " + expectedChanges);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestStepwise()
|
||||
{
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("Australia/Darwin"), 0);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("Pacific/Honolulu"), 0);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("PST8PDT"), 2);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("SystemV/PST"), 0);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("SystemV/PST8PDT"), 2);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("Japan"), 0);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("Europe/Paris"), 2);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone("America/Los_Angeles"), 2);
|
||||
findBoundariesStepwise(1997, ONE_DAY, TimeZone.getTimeZone(AUSTRALIA), 2);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue