/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.client.util; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.model.uimodels.ExecutionWindow; import com.emc.storageos.db.client.model.uimodels.ExecutionWindowLengthType; import com.emc.storageos.db.client.model.uimodels.ExecutionWindowType; public class ExecutionWindowHelper { private static final Logger log = LoggerFactory.getLogger(ExecutionWindowHelper.class); private ExecutionWindow window; static final int INFINITE_WINDOW_ORDER_EXECUTION_TIMEOUT = 1; // 1 hour public ExecutionWindowHelper(ExecutionWindow window) { this.window = window; } public boolean isActive(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return isActive(cal); } public boolean isActive(Calendar fromDate) { fromDate = inUTC(fromDate); Calendar startTime = getWindowStartTime(fromDate); Calendar endTime = getWindowEndTime(startTime); boolean duringWindow = (fromDate.compareTo(startTime) >= 0) && (fromDate.compareTo(endTime) < 0); return duringWindow; } /** * Check if order's scheduled time + schedule window already expire. * @param fromDate * @return */ public boolean isExpired(Calendar fromDate) { Calendar endTime = (Calendar) fromDate.clone(); if (window == null) { endTime.add(Calendar.HOUR_OF_DAY, INFINITE_WINDOW_ORDER_EXECUTION_TIMEOUT); } else { endTime.add(getWindowLengthCalendarField(), window.getExecutionWindowLength()); } Calendar currTime = Calendar.getInstance(); log.debug("currTime:{}, endTime: {}", currTime, endTime); return currTime.compareTo(endTime) > 0; } public Calendar calculateCurrentOrNext() { return calculateCurrentOrNext(Calendar.getInstance()); } public Calendar calculateCurrentOrNext(Calendar fromDate) { return calculate(fromDate, true); } public Calendar calculateNext() { return calculateNext(Calendar.getInstance()); } public Calendar calculateNext(Calendar fromDate) { return calculate(fromDate, false); } protected Calendar calculate(Calendar fromDate, boolean includeCurrent) { fromDate = inUTC(fromDate); Calendar startTime = getWindowStartTime(fromDate); Calendar endTime = getWindowEndTime(startTime); boolean duringWindow = (fromDate.compareTo(startTime) >= 0) && (fromDate.compareTo(endTime) < 0); boolean afterWindow = fromDate.compareTo(endTime) >= 0; if (afterWindow || (duringWindow && !includeCurrent)) { nextWindow(startTime); } return startTime; } /** * Determines if the window is a daily window. * * @return true if the window is a daily window. */ private boolean isDaily() { return ExecutionWindowType.DAILY.name().equals(window.getExecutionWindowType()); } /** * Determines if the window is a weekly window. * * @return true if the window is a weekly window. */ private boolean isWeekly() { return ExecutionWindowType.WEEKLY.name().equals(window.getExecutionWindowType()); } /** * Determines if the window is a monthly window. * * @return true if the window is a monthly window. */ private boolean isMonthly() { return ExecutionWindowType.MONTHLY.name().equals(window.getExecutionWindowType()); } /** * Gets the window's start time immediately before the given date. * * @return the window time calendar. */ private Calendar getWindowStartTime(Calendar fromDate) { int year = fromDate.get(Calendar.YEAR); int month = fromDate.get(Calendar.MONTH); int day = fromDate.get(Calendar.DAY_OF_MONTH); int hour = window.getHourOfDayInUTC() != null ? window.getHourOfDayInUTC() : 0; int minute = window.getMinuteOfHourInUTC() != null ? window.getMinuteOfHourInUTC() : 0; Calendar startTime = Calendar.getInstance(TimeZone.getTimeZone("UTC")); startTime.set(year, month, day, hour, minute, 0); startTime.set(Calendar.MILLISECOND, 0); if (isWeekly()) { adjustDayOfWeek(startTime); } else if (isMonthly()) { adjustDayOfMonth(startTime, month); } if (startTime.after(fromDate)) { previousWindow(startTime); } return startTime; } /** * Gets the end time of the window from the given window start time. * * @param startTime * the start time. * @return the end time. */ private Calendar getWindowEndTime(Calendar startTime) { Calendar endTime = (Calendar) startTime.clone(); endTime.add(getWindowLengthCalendarField(), window.getExecutionWindowLength()); return endTime; } /** * 48 hours time representation * @return */ private int getHourMin(int hour, int min) { return Integer.valueOf(String.format("%02d%02d", hour, min)); } private int getWindowStartHourMin() { int hour = window.getHourOfDayInUTC() != null ? window.getHourOfDayInUTC() : 0; int min = window.getMinuteOfHourInUTC() != null ? window.getMinuteOfHourInUTC() : 0; return getHourMin(hour, min); } private int getWindowEndHourMin() { int hour = window.getHourOfDayInUTC() != null ? window.getHourOfDayInUTC() : 0; int min = window.getMinuteOfHourInUTC() != null ? window.getMinuteOfHourInUTC() : 0; switch (ExecutionWindowLengthType.valueOf(window.getExecutionWindowLengthType())) { case DAYS: // maximum execution window is 24hours hour += 24; break; case HOURS: hour += window.getExecutionWindowLength(); break; case MINUTES: hour += window.getExecutionWindowLength()/60; min += window.getExecutionWindowLength()%60; if (min>=60) { hour ++; min -= 60; } break; } return getHourMin(hour, min); } public boolean inHourMinWindow(int hour, int min) { int targetTime = getHourMin(hour, min); log.debug("target HourMin:{}", targetTime); int startTime, endTime; startTime = getWindowStartHourMin(); log.debug("window start HourMin:{}", startTime); endTime = getWindowEndHourMin(); log.debug("window end HourMin:{}", endTime); if (targetTime >= startTime && targetTime <= endTime) { return true; } // might in the 2nd day. targetTime += 2400; if (targetTime >= startTime && targetTime <= endTime) { return true; } return false; } /** * Adjusts the start time to the correct day of the week for a weekly window. * * @param startTime * the start time. */ private void adjustDayOfWeek(Calendar startTime) { // Adjust the window time within the current week int daysDiff = getDayOfWeek() - startTime.get(Calendar.DAY_OF_WEEK); startTime.add(Calendar.DAY_OF_WEEK, daysDiff); } /** * Adjust the day of the month for the given month for a monthly window. If the day of the month is after the last * day of the month, it is set to the last day of the month. * * @param startTime * the start time. * @param month * the month. */ private void adjustDayOfMonth(Calendar startTime, int month) { // Set to the last day of the month applyLastDayOfMonth(startTime, month); // If this isn't a last day of month window, back up to the requested day if (!window.getLastDayOfMonth()) { int lastDayOfMonth = startTime.get(Calendar.DAY_OF_MONTH); if (lastDayOfMonth > getDayOfMonth()) { startTime.set(Calendar.DAY_OF_MONTH, getDayOfMonth()); } } } /** * Sets the calendar to the last day of the given month. * * @param cal * the calendar. * @param month * the month. */ private void applyLastDayOfMonth(Calendar cal, int month) { cal.set(Calendar.MONTH, month + 1); cal.set(Calendar.DAY_OF_MONTH, 1); cal.add(Calendar.DAY_OF_MONTH, -1); } /** * Changes to the previous window start time. * * @param startTime * the window start time. */ private void previousWindow(Calendar startTime) { if (isDaily()) { startTime.add(Calendar.DAY_OF_MONTH, -1); } else if (isWeekly()) { startTime.add(Calendar.WEEK_OF_MONTH, -1); } else if (isMonthly()) { int month = startTime.get(Calendar.MONTH); adjustDayOfMonth(startTime, month + -1); } } /** * Changes to the next window start time. * * @param startTime * the window start time. */ private void nextWindow(Calendar startTime) { if (isDaily()) { startTime.add(Calendar.DAY_OF_MONTH, 1); } else if (isWeekly()) { startTime.add(Calendar.WEEK_OF_MONTH, 1); } else if (isMonthly()) { int month = startTime.get(Calendar.MONTH); adjustDayOfMonth(startTime, month + 1); } } /** * Gets the calendar in UTC time. * * @param cal * the input calendar. * @return a calendar instance in UTC. */ private Calendar inUTC(Calendar cal) { Calendar utc = Calendar.getInstance(TimeZone.getTimeZone("UTC")); utc.setTimeInMillis(cal.getTimeInMillis()); return utc; } /** * Gets the calendar field that is used for the window length. * * @return the window length calendar field. */ private int getWindowLengthCalendarField() { switch (ExecutionWindowLengthType.valueOf(window.getExecutionWindowLengthType())) { case DAYS: return Calendar.DAY_OF_MONTH; case HOURS: return Calendar.HOUR_OF_DAY; case MINUTES: return Calendar.MINUTE; } throw new IllegalStateException("Invalid window length"); } /** * Gets the day of the week used for Calendar objects. The stored value treats Monday as the first day of the week, * but Java's Calendar uses Sunday as the first day of the week. * * @return the day of the week. */ private int getDayOfWeek() { // JODA time starts day of week on mondays (1) int dayOfWeek = window.getDayOfWeek(); if (dayOfWeek == 7) { dayOfWeek = 1; } else { dayOfWeek++; } return dayOfWeek; } /** * Gets the day of the month used for Calendar objects. * * @return the day of the month. */ private int getDayOfMonth() { return window.getDayOfMonth(); } /** * Get expected schedule time based on execution window * @return */ public Calendar getScheduledTime() { Calendar currTime = Calendar.getInstance(TimeZone.getTimeZone("UTC")); log.debug("currTime: {}", currTime.toString()); if (isActive(currTime)) { log.debug("currTime {} is in active window, set it as scheduled time.", currTime.toString()); return currTime; } int year = currTime.get(Calendar.YEAR); int month = currTime.get(Calendar.MONTH); int day = currTime.get(Calendar.DAY_OF_MONTH); int hour = window.getHourOfDayInUTC() != null ? window.getHourOfDayInUTC() : 0; int min = window.getMinuteOfHourInUTC() != null ? window.getMinuteOfHourInUTC() : 0; Calendar scheduledTime = Calendar.getInstance(TimeZone.getTimeZone("UTC")); scheduledTime.set(year, month, day, hour, min, 0); if (window.getExecutionWindowType().equals(ExecutionWindowType.MONTHLY.name())) { scheduledTime.set(Calendar.DAY_OF_MONTH, window.getDayOfMonth()); } else if (window.getExecutionWindowType().equals(ExecutionWindowType.WEEKLY.name())) { int daysDiff = (window.getDayOfWeek()%7 + 1) - scheduledTime.get(Calendar.DAY_OF_WEEK); // java dayOfWeek starts from Sun. scheduledTime.add(Calendar.DAY_OF_WEEK, daysDiff); } while (scheduledTime.before(currTime)) { scheduledTime = getNextScheduledTime(scheduledTime, window); log.debug("scheduledTime in loop: {}", scheduledTime.toString()); } log.debug("scheduledTime: {}", scheduledTime.toString()); return scheduledTime; } /** * Get next desired schedule time based on the previous one and execution window * @param scheduledTime previous schedule time * @param window execution window * @return */ private Calendar getNextScheduledTime(Calendar scheduledTime, ExecutionWindow window) { if (window.getExecutionWindowType().equals(ExecutionWindowType.MONTHLY.name())) { scheduledTime.add(Calendar.MONTH, 1); } else if (window.getExecutionWindowType().equals(ExecutionWindowType.WEEKLY.name())) { scheduledTime.add(Calendar.WEEK_OF_MONTH, 1); } else if (window.getExecutionWindowType().equals(ExecutionWindowType.DAILY.name())) { scheduledTime.add(Calendar.DAY_OF_MONTH, 1); } return scheduledTime; } }