/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2013, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.rolling.helper; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.spi.ContextAwareBase; /** * RollingCalendar is a helper class to * {@link ch.qos.logback.core.rolling.TimeBasedRollingPolicy } or similar * timed-based rolling policies. Given a periodicity type and the current time, * it computes the start of the next interval (i.e. the triggering date). * * @author Ceki Gülcü */ public class RollingCalendar extends GregorianCalendar { private static final long serialVersionUID = -5937537740925066161L; // The gmtTimeZone is used only in computeCheckPeriod() method. static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT"); PeriodicityType periodicityType = PeriodicityType.ERRONEOUS; public RollingCalendar() { super(); } public RollingCalendar(TimeZone tz, Locale locale) { super(tz, locale); } public void init(String datePattern) { periodicityType = computePeriodicityType(datePattern); } private void setPeriodicityType(PeriodicityType periodicityType) { this.periodicityType = periodicityType; } public PeriodicityType getPeriodicityType() { return periodicityType; } public long getNextTriggeringMillis(Date now) { return getNextTriggeringDate(now).getTime(); } // This method computes the roll over period by looping over the // periods, starting with the shortest, and stopping when the r0 is // different from from r1, where r0 is the epoch formatted according // the datePattern (supplied by the user) and r1 is the // epoch+nextMillis(i) formatted according to datePattern. All date // formatting is done in GMT and not local format because the test // logic is based on comparisons relative to 1970-01-01 00:00:00 // GMT (the epoch). public PeriodicityType computePeriodicityType(String datePattern) { RollingCalendar rollingCalendar = new RollingCalendar(GMT_TIMEZONE, Locale .getDefault()); // set sate to 1970-01-01 00:00:00 GMT Date epoch = new Date(0); if (datePattern != null) { for (PeriodicityType i : PeriodicityType.VALID_ORDERED_LIST) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); simpleDateFormat.setTimeZone(GMT_TIMEZONE); // all date formatting done // in GMT String r0 = simpleDateFormat.format(epoch); rollingCalendar.setPeriodicityType(i); Date next = new Date(rollingCalendar.getNextTriggeringMillis(epoch)); String r1 = simpleDateFormat.format(next); // System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); if ((r0 != null) && (r1 != null) && !r0.equals(r1)) { return i; } } } // we failed return PeriodicityType.ERRONEOUS; } public void printPeriodicity(ContextAwareBase cab) { switch (periodicityType) { case TOP_OF_MILLISECOND: cab.addInfo("Roll-over every millisecond."); break; case TOP_OF_SECOND: cab.addInfo("Roll-over every second."); break; case TOP_OF_MINUTE: cab.addInfo("Roll-over every minute."); break; case TOP_OF_HOUR: cab.addInfo("Roll-over at the top of every hour."); break; case HALF_DAY: cab.addInfo("Roll-over at midday and midnight."); break; case TOP_OF_DAY: cab.addInfo("Roll-over at midnight."); break; case TOP_OF_WEEK: cab.addInfo("Rollover at the start of week."); break; case TOP_OF_MONTH: cab.addInfo("Rollover at start of every month."); break; default: cab.addInfo("Unknown periodicity."); } } public long periodsElapsed(long start, long end) { if (start > end) throw new IllegalArgumentException("Start cannot come before end"); long diff = end - start; switch (periodicityType) { case TOP_OF_MILLISECOND: return diff; case TOP_OF_SECOND: return diff / CoreConstants.MILLIS_IN_ONE_SECOND; case TOP_OF_MINUTE: return diff / CoreConstants.MILLIS_IN_ONE_MINUTE; case TOP_OF_HOUR: return (int) diff / CoreConstants.MILLIS_IN_ONE_HOUR; case TOP_OF_DAY: return diff / CoreConstants.MILLIS_IN_ONE_DAY; case TOP_OF_WEEK: return diff / CoreConstants.MILLIS_IN_ONE_WEEK; case TOP_OF_MONTH: return diffInMonths(start, end); default: throw new IllegalStateException("Unknown periodicity type."); } } public static int diffInMonths(long startTime, long endTime) { if (startTime > endTime) throw new IllegalArgumentException("startTime cannot be larger than endTime"); Calendar startCal = Calendar.getInstance(); startCal.setTimeInMillis(startTime); Calendar endCal = Calendar.getInstance(); endCal.setTimeInMillis(endTime); int yearDiff = endCal.get(Calendar.YEAR) - startCal.get(Calendar.YEAR); int monthDiff = endCal.get(Calendar.MONTH) - startCal.get(Calendar.MONTH); return yearDiff * 12 + monthDiff; } public Date getRelativeDate(Date now, int periods) { this.setTime(now); switch (periodicityType) { case TOP_OF_MILLISECOND: this.add(Calendar.MILLISECOND, periods); break; case TOP_OF_SECOND: this.set(Calendar.MILLISECOND, 0); this.add(Calendar.SECOND, periods); break; case TOP_OF_MINUTE: this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MINUTE, periods); break; case TOP_OF_HOUR: this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.HOUR_OF_DAY, periods); break; case TOP_OF_DAY: this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.DATE, periods); break; case TOP_OF_WEEK: this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek()); this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.WEEK_OF_YEAR, periods); break; case TOP_OF_MONTH: this.set(Calendar.DATE, 1); this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MONTH, periods); break; default: throw new IllegalStateException("Unknown periodicity type."); } return getTime(); } public Date getNextTriggeringDate(Date now) { return getRelativeDate(now, 1); } }