From 0d4fcd0cb4330e1895def6f5daa8996f8e23a6de Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 27 Feb 2017 11:25:39 -0700 Subject: [PATCH] Fixes #1357 - Refactored date/time handling and added tests for RolloverFileOutputStream --- .../jetty/util/RolloverFileOutputStream.java | 42 +++-- .../util/RolloverFileOutputStreamTest.java | 147 ++++++++++++++++++ 2 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java index a4da91858ea9..3ee1f124c5cc 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java @@ -24,7 +24,9 @@ import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; -import java.util.Calendar; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.Locale; import java.util.TimeZone; @@ -173,26 +175,44 @@ public RolloverFileOutputStream(String filename, _rollTask=new RollTask(); - midnight = Calendar.getInstance(); - midnight.setTimeZone(zone); - // set to midnight - midnight.set(Calendar.HOUR, 0); - midnight.set(Calendar.MINUTE, 0); - midnight.set(Calendar.SECOND, 0); - midnight.set(Calendar.MILLISECOND, 0); + midnight = toMidnight(ZonedDateTime.now(), zone.toZoneId()); scheduleNextRollover(); } } - private void scheduleNextRollover() + /** + * Get the "start of day" for the provided DateTime at the zone specified. + * + * @param dateTime the date time to calculate from + * @param zone the zone to return the date in + * @return start of the day of the date provided + */ + public static ZonedDateTime toMidnight(ZonedDateTime dateTime, ZoneId zone) + { + return dateTime.toLocalDate().atStartOfDay(zone); + } + + /** + * Get the next "start of day" for the provided date. + * + * @param dateTime the date to calculate from + * @return the start of the next day + */ + public static ZonedDateTime nextMidnight(ZonedDateTime dateTime) { // Increment to next day. // Using Calendar.add(DAY, 1) takes in account Daylights Savings // differences, and still maintains the "midnight" settings for // Hour, Minute, Second, Milliseconds - midnight.add(Calendar.DAY_OF_MONTH, 1); - __rollover.schedule(_rollTask,midnight.getTime()); + return dateTime.toLocalDate().plus(1, ChronoUnit.DAYS).atStartOfDay(dateTime.getZone()); + } + + private void scheduleNextRollover() + { + long lastMs = midnight.toInstant().toEpochMilli(); + midnight = nextMidnight(midnight); + __rollover.schedule(_rollTask,midnight.toInstant().toEpochMilli() - lastMs); } /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java new file mode 100644 index 000000000000..829f255b5275 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/RolloverFileOutputStreamTest.java @@ -0,0 +1,147 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.TimeZone; + +import org.junit.Test; + +public class RolloverFileOutputStreamTest +{ + private static ZoneId toZoneId(String timezoneId) + { + ZoneId zone = TimeZone.getTimeZone(timezoneId).toZoneId(); + // System.out.printf(".toZoneId(\"%s\") = [id=%s,normalized=%s]%n", timezoneId, zone.getId(), zone.normalized()); + return zone; + } + + private static ZonedDateTime toDateTime(String timendate, ZoneId zone) + { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-hh:mm:ss.S a z") + .withZone(zone); + return ZonedDateTime.parse(timendate, formatter); + } + + private static String toString(TemporalAccessor date) + { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-hh:mm:ss.S a z"); + return formatter.format(date); + } + + private void assertSequence(ZonedDateTime midnight, Object[][] expected) + { + ZonedDateTime nextEvent = midnight; + + for (int i = 0; i < expected.length; i++) + { + long lastMs = nextEvent.toInstant().toEpochMilli(); + nextEvent = RolloverFileOutputStream.nextMidnight(nextEvent); + assertThat("Next Event", toString(nextEvent), is(expected[i][0])); + long duration = (nextEvent.toInstant().toEpochMilli() - lastMs); + assertThat("Duration to next event", duration, is((long) expected[i][1])); + } + } + + @Test + public void testMidnightRolloverCalc_PST_DST_Start() + { + ZoneId zone = toZoneId("PST"); + ZonedDateTime initialDate = toDateTime("2016.03.11-01:23:45.0 PM PST", zone); + + ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone); + assertThat("Midnight", toString(midnight), is("2016.03.11-12:00:00.0 AM PST")); + + Object expected[][] = { + {"2016.03.12-12:00:00.0 AM PST", 86_400_000L}, + {"2016.03.13-12:00:00.0 AM PST", 86_400_000L}, + {"2016.03.14-12:00:00.0 AM PDT", 82_800_000L}, // the short day + {"2016.03.15-12:00:00.0 AM PDT", 86_400_000L}, + {"2016.03.16-12:00:00.0 AM PDT", 86_400_000L}, + }; + + assertSequence(midnight, expected); + } + + @Test + public void testMidnightRolloverCalc_PST_DST_End() + { + ZoneId zone = toZoneId("PST"); + ZonedDateTime initialDate = toDateTime("2016.11.04-11:22:33.0 AM PDT", zone); + + ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone); + assertThat("Midnight", toString(midnight), is("2016.11.04-12:00:00.0 AM PDT")); + + Object expected[][] = { + {"2016.11.05-12:00:00.0 AM PDT", 86_400_000L}, + {"2016.11.06-12:00:00.0 AM PDT", 86_400_000L}, + {"2016.11.07-12:00:00.0 AM PST", 90_000_000L}, // the long day + {"2016.11.08-12:00:00.0 AM PST", 86_400_000L}, + {"2016.11.09-12:00:00.0 AM PST", 86_400_000L}, + }; + + assertSequence(midnight, expected); + } + + @Test + public void testMidnightRolloverCalc_Sydney_DST_Start() + { + ZoneId zone = toZoneId("Australia/Sydney"); + ZonedDateTime initialDate = toDateTime("2016.10.01-01:23:45.0 PM AEST", zone); + + ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone); + assertThat("Midnight", toString(midnight), is("2016.10.01-12:00:00.0 AM AEST")); + + Object expected[][] = { + {"2016.10.02-12:00:00.0 AM AEST", 86_400_000L}, + {"2016.10.03-12:00:00.0 AM AEDT", 82_800_000L}, // the short day + {"2016.10.04-12:00:00.0 AM AEDT", 86_400_000L}, + {"2016.10.05-12:00:00.0 AM AEDT", 86_400_000L}, + {"2016.10.06-12:00:00.0 AM AEDT", 86_400_000L}, + }; + + assertSequence(midnight, expected); + } + + @Test + public void testMidnightRolloverCalc_Sydney_DST_End() + { + ZoneId zone = toZoneId("Australia/Sydney"); + ZonedDateTime initialDate = toDateTime("2016.04.02-11:22:33.0 AM AEDT", zone); + + ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone); + assertThat("Midnight", toString(midnight), is("2016.04.02-12:00:00.0 AM AEDT")); + + Object expected[][] = { + {"2016.04.03-12:00:00.0 AM AEDT", 86_400_000L}, + {"2016.04.04-12:00:00.0 AM AEST", 90_000_000L}, // The long day + {"2016.04.05-12:00:00.0 AM AEST", 86_400_000L}, + {"2016.04.06-12:00:00.0 AM AEST", 86_400_000L}, + {"2016.04.07-12:00:00.0 AM AEST", 86_400_000L}, + }; + + assertSequence(midnight, expected); + } +}