/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.systemservices.impl.jobs.backupscheduler; import java.util.Calendar; import java.util.Date; /** * Class to manage time computation for Backup Scheduler */ public class ScheduleTimeRange { public static enum ScheduleInterval { MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, DISABLED } private Calendar start; private ScheduleInterval interval; private int multiple; public ScheduleTimeRange(ScheduleInterval interval, int multiple, Calendar now) { this.interval = interval; this.multiple = multiple; if (interval != ScheduleInterval.DISABLED) { this.start = (Calendar) now.clone(); adjustToIntervalLowerBound(this.start, interval, multiple); } } public ScheduleInterval getInterval() { return this.interval; } public Date start() { return this.start.getTime(); } public Date end() { return shiftDate(this.start, this.interval, this.multiple).getTime(); } public ScheduleTimeRange next() { return new ScheduleTimeRange(this.interval, this.multiple, shiftDate(this.start, this.interval, this.multiple)); } public Date minuteOffset(int minutes) { Calendar cal = (Calendar) this.start.clone(); cal.add(Calendar.MINUTE, minutes); return cal.getTime(); } // Test if the backup at specified time is contained in this time range public boolean contains(Date backupDateTime) { return (backupDateTime.equals(start()) || backupDateTime.after(start())) && backupDateTime.before(end()); } @Override public String toString() { return String.format("%s@%s", this.interval.toString(), this.start == null ? "NA" : ScheduledBackupTag.toTimestamp(this.start.getTime())); } public static Date getExpectedMostRecentBackupDateTime(Calendar now, ScheduleInterval interval, int multiple, int offset) { Calendar expected = (Calendar) now.clone(); adjustToIntervalLowerBound(expected, interval, multiple); // Then add the start offset to the start boundary expected.add(Calendar.MINUTE, offset); return expected.getTime(); } public static ScheduleInterval parseInterval(String intervalStr) { return Enum.valueOf(ScheduleInterval.class, intervalStr.toUpperCase()); } private static int alignDown(int value, int granularity, int minValue) { int remaining = value % granularity; value -= remaining; return value < minValue ? minValue : value; } /** * Adjust the Calendar object to the start boundary of specified time interval that it's currently in. * NOTE: If the multiple parameter exceeded the next level interval, it will be same as specifying next * level interval with multiple = 1. * * @param now * @param interval * @param multiple */ private static void adjustToIntervalLowerBound(Calendar now, ScheduleInterval interval, int multiple) { now.set(Calendar.SECOND, 0); now.set(Calendar.MILLISECOND, 0); switch (interval) { case MINUTE: now.set(Calendar.MINUTE, alignDown(now.get(Calendar.MINUTE), multiple, 0)); break; case HOUR: now.set(Calendar.HOUR_OF_DAY, alignDown(now.get(Calendar.HOUR_OF_DAY), multiple, 0)); now.set(Calendar.MINUTE, 0); break; case DAY: now.set(Calendar.DAY_OF_MONTH, alignDown(now.get(Calendar.DAY_OF_MONTH), multiple, 1)); now.set(Calendar.HOUR_OF_DAY, 0); now.set(Calendar.MINUTE, 0); break; case WEEK: now.set(Calendar.WEEK_OF_YEAR, alignDown(now.get(Calendar.WEEK_OF_YEAR), multiple, 1)); now.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); now.set(Calendar.HOUR_OF_DAY, 0); now.set(Calendar.MINUTE, 0); break; case MONTH: now.set(Calendar.MONTH, alignDown(now.get(Calendar.MONTH), multiple, Calendar.JANUARY)); now.set(Calendar.DAY_OF_MONTH, 1); now.set(Calendar.HOUR_OF_DAY, 0); now.set(Calendar.MINUTE, 0); break; case YEAR: now.set(Calendar.YEAR, alignDown(now.get(Calendar.YEAR), multiple, 1970)); now.set(Calendar.MONTH, Calendar.JANUARY); now.set(Calendar.DAY_OF_MONTH, 1); now.set(Calendar.HOUR_OF_DAY, 0); now.set(Calendar.MINUTE, 0); break; default: throw new IllegalArgumentException(String.format("Invalid interval: %s", interval.toString())); } } private static Calendar shiftDate(Calendar cal, ScheduleInterval interval, int multiple) { cal = (Calendar) cal.clone(); adjustToIntervalLowerBound(cal, interval, multiple); switch (interval) { case MINUTE: cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + multiple); break; case HOUR: cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) + multiple); break; case DAY: cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + multiple); break; case WEEK: cal.set(Calendar.WEEK_OF_YEAR, cal.get(Calendar.WEEK_OF_YEAR) + multiple); break; case MONTH: cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + multiple); break; case YEAR: cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + multiple); break; default: throw new IllegalArgumentException(String.format("Invalid interval: %s", interval.toString())); } return cal; } }