diff --git a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html index c048af0ce..c41a4b1eb 100644 --- a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html +++ b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html @@ -202,18 +202,32 @@

QuickFIX Settings

StartDay - For week long sessions, the starting day of week for the session. Use in combination with StartTime. + For week long sessions, the starting day of week for the session. +
Use in combination with StartTime. +
Incompatible with Weekdays Day of week in the default locale (e.g. Monday, mon, lundi, lun. etc.)   EndDay - For week long sessions, the ending day of week for the session. Use in combination with EndTime. + For week long sessions, the ending day of week for the session. +
Use in combination with EndTime. +
Incompatible with Weekdays Day of week in the default locale (e.g. Monday, mon, lundi, lun. etc.)   + + Weekdays + For daily sessions that are active on specific days of the week. +
Use in combination with StartTime and EndTime. +
Incompatible with StartDay and EndDay. +
If StartTime is before EndTime then the day corresponds to the StartTime. + Comma-delimited list of days of the week in the default locale (e.g. "Sun,Mon,Tue", "Dimanche,Lundi,Mardi" etc.) + +   + NonStopSession If set the session will never reset. This is effectively the same as setting 00:00:00 as StartTime and EndTime. diff --git a/quickfixj-core/src/main/java/quickfix/Session.java b/quickfixj-core/src/main/java/quickfix/Session.java index 21ebf2cd9..ec23718a7 100644 --- a/quickfixj-core/src/main/java/quickfix/Session.java +++ b/quickfixj-core/src/main/java/quickfix/Session.java @@ -147,6 +147,11 @@ public class Session implements Closeable { */ public static final String SETTING_END_TIME = "EndTime"; + /** + * Session scheduling setting to specify active days of the week when using a WeekdaySessionSchedule. + */ + public static final String SETTING_WEEKDAYS = "Weekdays"; + /** * Session setting to indicate whether a data dictionary should be used. If * a data dictionary is not used then message validation is not possble. diff --git a/quickfixj-core/src/main/java/quickfix/WeekdaySessionSchedule.java b/quickfixj-core/src/main/java/quickfix/WeekdaySessionSchedule.java new file mode 100644 index 000000000..da0ffefa5 --- /dev/null +++ b/quickfixj-core/src/main/java/quickfix/WeekdaySessionSchedule.java @@ -0,0 +1,339 @@ +/******************************************************************************* + * Copyright (c) quickfixengine.org All rights reserved. + * + * This file is part of the QuickFIX FIX Engine + * + * This file may be distributed under the terms of the quickfixengine.org + * license as defined by quickfixengine.org and appearing in the file + * LICENSE included in the packaging of this file. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING + * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. + * + * See http://www.quickfixengine.org/LICENSE for licensing information. + * + * Contact ask@quickfixengine.org if any conditions of this licensing + * are not clear to you. + ******************************************************************************/ + +package quickfix; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Daily schedule that can be configured to only login on specific days e.g. Mon -> Fri + * The day specified corresponds to the day that the StartTime is valid, + * this is relevant when StartTime is after EndTime + */ +public class WeekdaySessionSchedule implements SessionSchedule { + private static final Pattern TIME_PATTERN = Pattern.compile("(\\d{2}):(\\d{2}):(\\d{2})(.*)"); + + private final int[] weekdayOffsets; + private final TimeEndPoint startTime; + private final TimeEndPoint endTime; + + protected final static Logger log = LoggerFactory.getLogger(WeekdaySessionSchedule.class); + + WeekdaySessionSchedule(SessionSettings settings, SessionID sessionID) throws ConfigError, + FieldConvertError { + + TimeZone defaultTimeZone = getDefaultTimeZone(settings, sessionID); + + if (!settings.isSetting(sessionID, Session.SETTING_WEEKDAYS)) + throw new ConfigError("Session " + sessionID + ": does not have " + Session.SETTING_WEEKDAYS + " specified"); + + String weekdayNames = settings.getString(sessionID, Session.SETTING_WEEKDAYS); + if (weekdayNames.isEmpty()) + throw new ConfigError("Session " + sessionID + ": " + Session.SETTING_WEEKDAYS + " is empty"); + + String[] weekdayNameArray = weekdayNames.split(","); + weekdayOffsets = new int[weekdayNameArray.length]; + for (int i = 0; i < weekdayNameArray.length; i++) { + if (weekdayNameArray[i].length() != 3) + throw new ConfigError("Session " + sessionID + ": " + Session.SETTING_WEEKDAYS + " has an illegal weekday: [" + weekdayNameArray[i] + "] in " + weekdayNames); + + weekdayOffsets[i] = DayConverter.toInteger(weekdayNameArray[i]); + } + + startTime = getTimeEndPoint(settings, sessionID, defaultTimeZone, Session.SETTING_START_TIME); + endTime = getTimeEndPoint(settings, sessionID, defaultTimeZone, Session.SETTING_END_TIME); + + log.info("{} using schedule: {}", sessionID, toString()); + } + + private TimeEndPoint getTimeEndPoint(SessionSettings settings, SessionID sessionID, + TimeZone defaultTimeZone, String timeSetting) throws ConfigError, + FieldConvertError { + + Matcher matcher = TIME_PATTERN.matcher(settings.getString(sessionID, timeSetting)); + if (!matcher.find()) { + throw new ConfigError("Session " + sessionID + ": could not parse time '" + + settings.getString(sessionID, timeSetting) + "'."); + } + + return new TimeEndPoint( + Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3)), getTimeZone(matcher.group(4), defaultTimeZone)); + } + + private TimeZone getDefaultTimeZone(SessionSettings settings, SessionID sessionID) + throws ConfigError, FieldConvertError { + TimeZone sessionTimeZone; + if (settings.isSetting(sessionID, Session.SETTING_TIMEZONE)) { + String sessionTimeZoneID = settings.getString(sessionID, Session.SETTING_TIMEZONE); + sessionTimeZone = TimeZone.getTimeZone(sessionTimeZoneID); + if ("GMT".equals(sessionTimeZone.getID()) && !"GMT".equals(sessionTimeZoneID)) { + throw new ConfigError("Unrecognized time zone '" + sessionTimeZoneID + + "' for session " + sessionID); + } + } else { + sessionTimeZone = TimeZone.getTimeZone("UTC"); + } + return sessionTimeZone; + } + + private TimeZone getTimeZone(String tz, TimeZone defaultZone) { + return "".equals(tz) ? defaultZone : TimeZone.getTimeZone(tz.trim()); + } + + private class TimeEndPoint { + private final int hour; + private final int minute; + private final int second; + private final int timeInSeconds; + private final TimeZone tz; + + TimeEndPoint(int hour, int minute, int second, TimeZone tz) { + this.hour = hour; + this.minute = minute; + this.second = second; + this.tz = tz; + timeInSeconds = timeInSeconds(hour, minute, second); + } + + int getHour() { + return hour; + } + + int getMinute() { + return minute; + } + + int getSecond() { + return second; + } + + public String toString() { + Calendar calendar = Calendar.getInstance(tz); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, second); + final SimpleDateFormat utc = new SimpleDateFormat("HH:mm:ss"); + utc.setTimeZone(TimeZone.getTimeZone("UTC")); + return utc.format(calendar.getTime()) + "-" + utc.getTimeZone().getID(); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof TimeEndPoint) { + TimeEndPoint otherTime = (TimeEndPoint) o; + return timeInSeconds == otherTime.timeInSeconds; + } + return false; + } + + public int hashCode() { + assert false : "hashCode not supported"; + return 0; + } + + TimeZone getTimeZone() { + return tz; + } + } + + /** + * find the most recent session date/time range on or before t + * if t is in a session then that session will be returned + * @param t specific date/time + * @return relevant session date/time range + */ + private TimeInterval theMostRecentIntervalBefore(Calendar t) { + TimeInterval timeInterval = new TimeInterval(); + Calendar intervalStart = timeInterval.getStart(); + intervalStart.setTimeZone(startTime.getTimeZone()); + intervalStart.setTimeInMillis(t.getTimeInMillis()); + intervalStart.set(Calendar.HOUR_OF_DAY, startTime.getHour()); + intervalStart.set(Calendar.MINUTE, startTime.getMinute()); + intervalStart.set(Calendar.SECOND, startTime.getSecond()); + intervalStart.set(Calendar.MILLISECOND, 0); + + Calendar intervalEnd = timeInterval.getEnd(); + intervalEnd.setTimeZone(endTime.getTimeZone()); + intervalEnd.setTimeInMillis(t.getTimeInMillis()); + intervalEnd.set(Calendar.HOUR_OF_DAY, endTime.getHour()); + intervalEnd.set(Calendar.MINUTE, endTime.getMinute()); + intervalEnd.set(Calendar.SECOND, endTime.getSecond()); + intervalEnd.set(Calendar.MILLISECOND, 0); + + while (intervalStart.getTimeInMillis() > t.getTimeInMillis() || + !validDayOfWeek(intervalStart)) { + intervalStart.add(Calendar.DAY_OF_WEEK, -1); + intervalEnd.add(Calendar.DAY_OF_WEEK, -1); + } + + if (intervalEnd.getTimeInMillis() <= intervalStart.getTimeInMillis()) { + intervalEnd.add(Calendar.DAY_OF_WEEK, 1); + } + + return timeInterval; + } + + /** + * is the startDateTime a valid day based on the permitted days of week + * @param startDateTime time to test + * @return flag indicating if valid + */ + private boolean validDayOfWeek(Calendar startDateTime) { + int dow = startDateTime.get(Calendar.DAY_OF_WEEK); + for (int i = 0; i < weekdayOffsets.length; i++) + if (weekdayOffsets[i] == dow) + return true; + return false; + } + + private static class TimeInterval { + private final Calendar start = SystemTime.getUtcCalendar(); + private final Calendar end = SystemTime.getUtcCalendar(); + + boolean isContainingTime(Calendar t) { + return t.compareTo(start) >= 0 && t.compareTo(end) <= 0; + } + + public String toString() { + return start.getTime() + " --> " + end.getTime(); + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof TimeInterval)) { + return false; + } + TimeInterval otherInterval = (TimeInterval) other; + return start.equals(otherInterval.start) && end.equals(otherInterval.end); + } + + public int hashCode() { + assert false : "hashCode not supported"; + return 0; + } + + Calendar getStart() { + return start; + } + + Calendar getEnd() { + return end; + } + } + + /** + * Predicate for determining if the two times are in the same session + * @param time1 test time 1 + * @param time2 test time 2 + * @return return true if in the same session + */ + @Override + public boolean isSameSession(Calendar time1, Calendar time2) { + TimeInterval interval1 = theMostRecentIntervalBefore(time1); + if (!interval1.isContainingTime(time1)) { + return false; + } + TimeInterval interval2 = theMostRecentIntervalBefore(time2); + return interval2.isContainingTime(time2) && interval1.equals(interval2); + } + + @Override + public boolean isNonStopSession() { + return false; + } + + /** + * Predicate for determining if the session should be active at the current time. + * + * @return true if session should be active, false otherwise. + */ + @Override + public boolean isSessionTime() { + return isSessionTime(SystemTime.getUtcCalendar()); + } + + boolean isSessionTime(Calendar time) { + TimeInterval interval = theMostRecentIntervalBefore(time); + return interval.isContainingTime(time); + } + + public String toString() { + StringBuilder buf = new StringBuilder(); + + SimpleDateFormat dowFormat = new SimpleDateFormat("EEEE"); + dowFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + + SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss-z"); + timeFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + + TimeInterval ti = theMostRecentIntervalBefore(SystemTime.getUtcCalendar()); + + formatTimeInterval(buf, ti, timeFormat, false); + + // Now the localized equivalents, if necessary + if (!startTime.getTimeZone().equals(SystemTime.UTC_TIMEZONE) + || !endTime.getTimeZone().equals(SystemTime.UTC_TIMEZONE)) { + buf.append(" ("); + formatTimeInterval(buf, ti, timeFormat, true); + buf.append(")"); + } + + return buf.toString(); + } + + private void formatTimeInterval(StringBuilder buf, TimeInterval timeInterval, + SimpleDateFormat timeFormat, boolean local) { + try { + for (int i = 0; i < weekdayOffsets.length; i++) { + buf.append(DayConverter.toString(weekdayOffsets[i])); + buf.append(", "); + } + } catch (ConfigError ex) { + // this can't happen as these are created using DayConverter.toInteger + } + + if (local) { + timeFormat.setTimeZone(startTime.getTimeZone()); + } + buf.append(timeFormat.format(timeInterval.getStart().getTime())); + + buf.append(" - "); + + if (local) { + timeFormat.setTimeZone(endTime.getTimeZone()); + } + buf.append(timeFormat.format(timeInterval.getEnd().getTime())); + } + + private int timeInSeconds(int hour, int minute, int second) { + return (hour * 3600) + (minute * 60) + second; + } +} diff --git a/quickfixj-core/src/main/java/quickfix/WeekdaySessionScheduleFactory.java b/quickfixj-core/src/main/java/quickfix/WeekdaySessionScheduleFactory.java new file mode 100644 index 000000000..d81902dc9 --- /dev/null +++ b/quickfixj-core/src/main/java/quickfix/WeekdaySessionScheduleFactory.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) quickfixengine.org All rights reserved. + * + * This file is part of the QuickFIX FIX Engine + * + * This file may be distributed under the terms of the quickfixengine.org + * license as defined by quickfixengine.org and appearing in the file + * LICENSE included in the packaging of this file. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING + * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. + * + * See http://www.quickfixengine.org/LICENSE for licensing information. + * + * Contact ask@quickfixengine.org if any conditions of this licensing + * are not clear to you. + ******************************************************************************/ + +package quickfix; + +/** + * Factory for creating weekday session schedules. + */ +public class WeekdaySessionScheduleFactory implements SessionScheduleFactory { + + public SessionSchedule create(SessionID sessionID, SessionSettings settings) throws ConfigError + { + try { + return new WeekdaySessionSchedule(settings, sessionID); + } catch (final FieldConvertError e) { + throw new ConfigError(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/quickfixj-core/src/test/java/quickfix/WeekdaySessionScheduleTest.java b/quickfixj-core/src/test/java/quickfix/WeekdaySessionScheduleTest.java new file mode 100644 index 000000000..d59220937 --- /dev/null +++ b/quickfixj-core/src/test/java/quickfix/WeekdaySessionScheduleTest.java @@ -0,0 +1,602 @@ +/******************************************************************************* + * Copyright (c) quickfixengine.org All rights reserved. + * + * This file is part of the QuickFIX FIX Engine + * + * This file may be distributed under the terms of the quickfixengine.org + * license as defined by quickfixengine.org and appearing in the file + * LICENSE included in the packaging of this file. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING + * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. + * + * See http://www.quickfixengine.org/LICENSE for licensing information. + * + * Contact ask@quickfixengine.org if any conditions of this licensing + * are not clear to you. + ******************************************************************************/ + +package quickfix; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.ParseException; +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 org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import quickfix.field.converter.UtcTimeOnlyConverter; + +public class WeekdaySessionScheduleTest { + private MockSystemTimeSource mockSystemTimeSource; + private Locale defaultLocale; + + @Before + public void setUp() throws Exception { + mockSystemTimeSource = new MockSystemTimeSource(); + SystemTime.setTimeSource(mockSystemTimeSource); + defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.GERMANY); + } + + @After + public void tearDown() throws Exception { + SystemTime.setTimeSource(null); + Locale.setDefault(defaultLocale); + } + + private SessionSchedule newSessionSchedule(Date startTime, Date endTime, String weekdays) throws Exception { + SessionSettings settings = new SessionSettings(); + if (weekdays != null && weekdays.length() > 0) { + settings.setString(Session.SETTING_WEEKDAYS, weekdays); + } + if (startTime != null) { + settings.setString(Session.SETTING_START_TIME, UtcTimeOnlyConverter.convert(startTime, false)); + } + if (endTime != null) { + settings.setString(Session.SETTING_END_TIME, UtcTimeOnlyConverter.convert(endTime, false)); + } + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + return new WeekdaySessionSchedule(settings, sessionID); + } + + private void doIsSessionTimeTest(SessionSchedule schedule, boolean expectedInSession, int year, + int month, int day, int hour, int minute, int second) { + doIsSessionTimeTest(schedule, expectedInSession, year, month, day, hour, minute, second, + TimeZone.getTimeZone("UTC")); + } + + private void doIsSessionTimeTest(SessionSchedule schedule, boolean expectedInSession, int year, + int month, int day, int hour, int minute, int second, TimeZone timeZone) { + mockSystemTimeSource + .setTime(getTimeStamp(year, month, day, hour, minute, second, timeZone)); + assertEquals("in session expectation incorrect", expectedInSession, schedule + .isSessionTime()); + } + + private void doIsSessionTimeTest(SessionSettings settings, SessionID sessionID, + boolean expectedInSession, int year, int month, int day, int hour, int minute, + int second, String timeZoneID) throws ConfigError, FieldConvertError { + mockSystemTimeSource.setTime(getTimeStamp(year, month, day, hour, minute, second, TimeZone + .getTimeZone(timeZoneID))); + SessionSchedule schedule = new WeekdaySessionSchedule(settings, sessionID); + assertEquals("schedule is wrong", expectedInSession, schedule.isSessionTime()); + } + + @Test + public void testMissingStartTime() throws Exception { + Calendar end = getUtcTime(18, 0, 0); + try { + newSessionSchedule(null, end.getTime(), "Mon"); + fail("no exception"); + } catch (ConfigError e) { + // do nothing + } + } + + @Test + public void testMissingEndTime() throws Exception { + Calendar start = getUtcTime(3, 0, 0); + try { + newSessionSchedule(start.getTime(), null, "Mon"); + fail("no exception"); + } catch (ConfigError e) { + // do nothing + } + } + + @Test + public void testMissingWeekdays() throws Exception { + Calendar start = getUtcTime(3, 0, 0); + Calendar end = getUtcTime(18, 0, 0); + try { + newSessionSchedule(start.getTime(), end.getTime(), null); + fail("no exception"); + } catch (ConfigError e) { + // do nothing + } + } + + @Test + public void testInvalidWeekdays1() throws Exception { + Calendar start = getUtcTime(3, 0, 0); + Calendar end = getUtcTime(18, 0, 0); + try { + newSessionSchedule(start.getTime(), end.getTime(), "AAA"); + fail("no exception"); + } catch (ConfigError e) { + // do nothing + } + } + + @Test + public void testInvalidWeekdays2() throws Exception { + Calendar start = getUtcTime(3, 0, 0); + Calendar end = getUtcTime(18, 0, 0); + try { + newSessionSchedule(start.getTime(), end.getTime(), ",Mon"); + fail("no exception"); + } catch (ConfigError e) { + // do nothing + } + } + + @Test + public void testInvalidWeekdays3() throws Exception { + Calendar start = getUtcTime(3, 0, 0); + Calendar end = getUtcTime(18, 0, 0); + try { + newSessionSchedule(start.getTime(), end.getTime(), "Mon,,Tue"); + fail("no exception"); + } catch (ConfigError e) { + // do nothing + } + } + + @Test + public void testSessionTimeStartBeforeEnd() throws Exception { + Calendar start = getUtcTime(3, 0, 0); + Calendar end = getUtcTime(18, 0, 0); + SessionSchedule schedule = newSessionSchedule(start.getTime(), end.getTime(), "Mon,Tue,Wed,Thu,Fri"); + // Sunday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 2, 59, 59); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 18, 0, 1); + // Monday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 2, 2, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 2, 3, 0, 0); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 2, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 2, 18, 0, 1); + // Tuesday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 3, 2, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 3, 3, 0, 0); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 3, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 3, 18, 0, 1); + // Wednesday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 4, 2, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 4, 3, 0, 0); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 4, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 4, 18, 0, 1); + // Thursday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 5, 2, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 5, 3, 0, 0); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 5, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 5, 18, 0, 1); + // Friday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 6, 2, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 6, 3, 0, 0); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 6, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 6, 18, 0, 1); + // Saturday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 2, 59, 59); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 18, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 18, 0, 1); + } + @Test + public void testSessionTimeEndBeforeStart() throws Exception { + Calendar start = getUtcTime(18, 0, 0); + Calendar end = getUtcTime(3, 0, 0); + SessionSchedule schedule = newSessionSchedule(start.getTime(), end.getTime(), "Mon,Tue,Wed,Thu,Fri"); + // Sunday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 17, 59, 59); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 1, 18, 0, 0); + // Monday + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 2, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 2, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 2, 17, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 2, 18, 0, 0); + // Tuesday + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 3, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 3, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 3, 17, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 3, 18, 0, 0); + // Wednesday + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 4, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 4, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 4, 17, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 4, 18, 0, 0); + // Thursday + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 5, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 5, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 5, 17, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 5, 18, 0, 0); + // Friday + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 6, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 6, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 6, 17, 59, 59); + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 6, 18, 0, 0); + // Saturday + doIsSessionTimeTest(schedule, true, 2017, Calendar.JANUARY, 7, 3, 0, 0); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 3, 0, 1); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 17, 59, 59); + doIsSessionTimeTest(schedule, false, 2017, Calendar.JANUARY, 7, 18, 0, 0); + } + + @Test + public void testIsSameSession() throws Exception { + // ===================================================== + // start time is less than end time + Calendar start = getUtcTime(3, 0, 0); + Calendar end = getUtcTime(18, 0, 0); + SessionSchedule schedule = newSessionSchedule(start.getTime(), end.getTime(), "Mon,Tue,Wed,Thu,Fri"); + + // same time + Calendar t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + Calendar t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + doIsSameSessionTest(schedule, t1, t2, true); + + // time 2 in same session but greater + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + doIsSameSessionTest(schedule, t1, t2, true); + + // time 2 in same session but less + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + doIsSameSessionTest(schedule, t1, t2, true); + + // time 1 not in session + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 1); + doIsSameSessionTest(schedule, t1, t2, false); + + // time 2 not in session + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 1); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + doIsSameSessionTest(schedule, t1, t2, false); + + // same time (in session window) two different days + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 3, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 3, 3, 0, 0); + doIsSameSessionTest(schedule, t1, t2, false); + + // ===================================================== + // start time is greater than end time + start = getUtcTime(18, 0, 0); + end = getUtcTime(13, 0, 0); + schedule = newSessionSchedule(start.getTime(), end.getTime(), "Mon,Tue,Wed,Thu,Fri"); + + // same session same day + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 19, 0, 0); + doIsSameSessionTest(schedule, t1, t2, true); + + // same time (in session window) two different days + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 3, 18, 0, 0); + doIsSameSessionTest(schedule, t1, t2, false); + + // same session time 2 is in next day + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 3, 3, 0, 0); + doIsSameSessionTest(schedule, t1, t2, true); + + // same session time 1 is in next day + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 3, 3, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + doIsSameSessionTest(schedule, t1, t2, true); + + // time 1 is 25 hours greater than time 2 + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 3, 19, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + doIsSameSessionTest(schedule, t1, t2, false); + + // ===================================================== + // start time is equal to end time + start = getUtcTime(6, 0, 0); + end = getUtcTime(6, 0, 0); + schedule = newSessionSchedule(start.getTime(), end.getTime(), "Mon,Tue,Wed,Thu,Fri"); + + t1 = getUtcTimeStamp(2017, Calendar.JANUARY, 3, 18, 0, 0); + t2 = getUtcTimeStamp(2017, Calendar.JANUARY, 2, 18, 0, 0); + doIsSameSessionTest(schedule, t1, t2, false); + } + + @Test + public void testSettingsWithoutStartEndDayWithTimeZone() throws Exception { + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_TIMEZONE, " US/Eastern "); + settings.setString(Session.SETTING_START_TIME, "01:00:00"); + settings.setString(Session.SETTING_END_TIME, "15:00:00"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri"); + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + SessionSchedule schedule = new WeekdaySessionSchedule(settings, sessionID); + TimeZone tz = TimeZone.getTimeZone("US/Eastern"); + doIsSessionTimeTest(schedule, false, 2002, 5, 5, 0, 59, 0, tz); + doIsSessionTimeTest(schedule, true, 2002, 7, 5, 14, 30, 0, tz); + doIsSessionTimeTest(schedule, false, 2003, 5, 5, 16, 30, 0, tz); + } + + @Test + public void testSettingsWithTimeZoneInTime() throws Exception { + // This test is very susceptible to whether the system time starts + // in daylight time or not, so we just force it that way. Otherwise + // the first time the mock time source gets set to a time with daylight time + // then the schedule appears to change 1 hr. + TimeZone tz = TimeZone.getTimeZone("US/Eastern"); + mockSystemTimeSource.setTime(getTimeStamp(2002, 5, 5, 0, 0, 0, tz)); + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_START_TIME, "01:00:00 US/Eastern"); + settings.setString(Session.SETTING_END_TIME, "15:00:00 US/Central"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri"); + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + SessionSchedule schedule = new WeekdaySessionSchedule(settings, sessionID); + doIsSessionTimeTest(schedule, false, 2002, 5, 5, 0, 59, 0, tz); + doIsSessionTimeTest(schedule, true, 2002, 7, 5, 14, 30, 0, tz); + + // The end time is actually 16:00 Eastern time but specified as + // 15:00 Central time. + doIsSessionTimeTest(schedule, true, 2003, 5, 5, 15, 59, 0, tz); + doIsSessionTimeTest(schedule, true, 2003, 5, 5, 16, 0, 0, tz); + doIsSessionTimeTest(schedule, false, 2003, 5, 5, 16, 1, 0, tz); + } + + /** + * From 1968 to 1971, GMT was an hour ahead of UTC. If we perform all our calculations in 1970, + * someone in GMT (e.g. London) will see sessions ending an hour later than expected. This test + * demonstrates the 1970 behavior and verifies that calculations on current dates give the proper results. + * + *

+ * More details at: + *

+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4644278 + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4832236 + */ + @Test + public void testThatUTCAndGMTAreTheSameNow() throws ConfigError, FieldConvertError, + ParseException { + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_START_TIME, "00:01:00"); + settings.setString(Session.SETTING_END_TIME, "21:50:00"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri,Sat,Sun"); + + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + + // In 1970, a session configured to end at 21:50 UTC would end at + // 22:50 London time any time of year. + doIsSessionTimeTest(settings, sessionID, true, 1970, Calendar.JANUARY, 27, 22, 50, 0, + "Europe/London"); + doIsSessionTimeTest(settings, sessionID, false, 1970, Calendar.JANUARY, 27, 22, 50, 1, + "Europe/London"); + doIsSessionTimeTest(settings, sessionID, true, 1970, Calendar.JULY, 27, 22, 50, 0, + "Europe/London"); + doIsSessionTimeTest(settings, sessionID, false, 1970, Calendar.JULY, 27, 22, 50, 1, + "Europe/London"); + + // Now, at least in winter, it should end at 21:50 in both zones -- + // if the end time session setting is being parsed correctly. + doIsSessionTimeTest(settings, sessionID, true, 2006, Calendar.FEBRUARY, 27, 21, 50, 0, + "Europe/London"); + doIsSessionTimeTest(settings, sessionID, false, 2006, Calendar.FEBRUARY, 27, 21, 50, 1, + "Europe/London"); + + // When summer time (BST) is in effect, London time will be an hour + // ahead again, and the session will end at 22:50 there. + doIsSessionTimeTest(settings, sessionID, true, 2006, Calendar.JULY, 27, 22, 50, 0, + "Europe/London"); + doIsSessionTimeTest(settings, sessionID, false, 2006, Calendar.JULY, 27, 22, 50, 1, + "Europe/London"); + + settings.setString(Session.SETTING_TIMEZONE, "Europe/London"); + + // In 1970, a session configured to end at 21:50 GMT would end at + // 20:50 UTC any time of year. + doIsSessionTimeTest(settings, sessionID, true, 1970, Calendar.JANUARY, 27, 20, 50, 0, "UTC"); + doIsSessionTimeTest(settings, sessionID, false, 1970, Calendar.JANUARY, 27, 20, 50, 1, + "UTC"); + doIsSessionTimeTest(settings, sessionID, true, 1970, Calendar.JULY, 27, 20, 50, 0, "UTC"); + doIsSessionTimeTest(settings, sessionID, false, 1970, Calendar.JULY, 27, 20, 50, 1, "UTC"); + + // Now, at least in winter, it should end at 21:50 in both zones -- + // if the end time session setting is being parsed correctly. + doIsSessionTimeTest(settings, sessionID, true, 2006, Calendar.FEBRUARY, 27, 21, 50, 0, + "UTC"); + doIsSessionTimeTest(settings, sessionID, false, 2006, Calendar.FEBRUARY, 27, 21, 50, 1, + "UTC"); + + // When summer time (BST) is in effect, London time will be an hour + // ahead again, and the session will end at 20:50 UTC there. + doIsSessionTimeTest(settings, sessionID, true, 2006, Calendar.JULY, 27, 20, 50, 0, "UTC"); + doIsSessionTimeTest(settings, sessionID, false, 2006, Calendar.JULY, 27, 20, 50, 1, "UTC"); + } + + @Test + public void testBadTimeSpecification() throws Exception { + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_TIMEZONE, "US/Eastern"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri"); + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + + try { + settings.setString(Session.SETTING_START_TIME, "01:xx:00"); + settings.setString(Session.SETTING_END_TIME, "15:00:00"); + new WeekdaySessionSchedule(settings, sessionID); + fail("no exception"); + } catch (ConfigError e) { + assertTrue(e.getMessage().contains("could not parse")); + } + + try { + settings.setString(Session.SETTING_START_TIME, "01:00:00"); + settings.setString(Session.SETTING_END_TIME, "15:00:yy"); + new WeekdaySessionSchedule(settings, sessionID); + fail("no exception"); + } catch (ConfigError e) { + assertTrue(e.getMessage().contains("could not parse")); + } + } + + @Test + public void testBadTimeZone() throws Exception { + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_TIMEZONE, "US/BOGUS"); + settings.setString(Session.SETTING_START_TIME, "01:00:00"); + settings.setString(Session.SETTING_END_TIME, "15:00:00"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri"); + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + + try { + new WeekdaySessionSchedule(settings, sessionID); + fail("no exception"); + } catch (ConfigError e) { + assertTrue(e.getMessage().contains("Unrecognized time zone")); + } + } + + @Test + public void testWeekdayToString() throws ConfigError, FieldConvertError { + // Just be sure it doesn't throw exceptions + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_TIMEZONE, "US/Eastern"); + settings.setString(Session.SETTING_START_TIME, "01:00:00"); + settings.setString(Session.SETTING_END_TIME, "15:00:00"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri"); + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + SessionSchedule schedule = new WeekdaySessionSchedule(settings, sessionID); + assertNotNull(schedule.toString()); + } + + @Test + public void testSettingsWithDST() throws Exception { + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_TIMEZONE, "Europe/Zurich"); + settings.setString(Session.SETTING_START_TIME, "01:30:00"); + settings.setString(Session.SETTING_END_TIME, "03:15:00"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri,Sat,Sun"); + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + // + SessionSchedule schedule = new WeekdaySessionSchedule(settings, sessionID); + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 20, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 20, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 20, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 20, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 20, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + // + schedule = new WeekdaySessionSchedule(settings, sessionID); + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 27, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 27, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 27, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 27, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 27, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + // + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 28, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 28, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 28, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 28, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 28, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + // + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 29, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 29, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 29, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2012, Calendar.OCTOBER, 29, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2012, Calendar.OCTOBER, 29, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + // + schedule = new WeekdaySessionSchedule(settings, sessionID); + doIsSessionTimeTest(schedule, false, 2013, Calendar.MARCH, 30, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.MARCH, 30, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.MARCH, 30, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.MARCH, 30, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2013, Calendar.MARCH, 30, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + // + doIsSessionTimeTest(schedule, false, 2013, Calendar.MARCH, 31, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.MARCH, 31, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2013, Calendar.MARCH, 31, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); // -> this is 03:16 ! + doIsSessionTimeTest(schedule, true, 2013, Calendar.MARCH, 31, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2013, Calendar.MARCH, 31, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + // + doIsSessionTimeTest(schedule, false, 2013, Calendar.APRIL, 1, 1, 29, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.APRIL, 1, 1, 31, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.APRIL, 1, 2, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, true, 2013, Calendar.APRIL, 1, 3, 14, 0, TimeZone.getTimeZone("Europe/Zurich")); + doIsSessionTimeTest(schedule, false, 2013, Calendar.APRIL, 1, 3, 16, 0, TimeZone.getTimeZone("Europe/Zurich")); + } + + @Test + public void testSettingsWithStartEndDayWithDSTMocked() throws Exception { + SessionSettings settings = new SessionSettings(); + settings.setString(Session.SETTING_TIMEZONE, "America/New_York"); + settings.setString(Session.SETTING_START_TIME, "20:00:00"); + settings.setString(Session.SETTING_END_TIME, "17:00:00"); + settings.setString(Session.SETTING_WEEKDAYS, "Mon,Tue,Wed,Thu,Fri,Sat,Sun"); + + mockSystemTimeSource.setTime(getTimeStamp(2008, Calendar.NOVEMBER, 2, 18, 0, 0, TimeZone.getTimeZone("America/New_York"))); + + SessionID sessionID = new SessionID("FIX.4.2", "SENDER", "TARGET"); + SessionSchedule schedule = new WeekdaySessionSchedule(settings, sessionID); + + //System.out.println(schedule); + + // November,2 -> Sunday + doIsSessionTimeTest(schedule, true, 2008, Calendar.NOVEMBER, 2, 20, 0, 0, + TimeZone.getTimeZone("America/New_York")); + // November,7 -> Friday + doIsSessionTimeTest(schedule, true, 2008, Calendar.NOVEMBER, 7, 17, 0, 0, + TimeZone.getTimeZone("America/New_York")); + } + + private final SimpleDateFormat dateFormat = new SimpleDateFormat("E M/d HH:mm:ss"); + + { + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + private void doIsSameSessionTest(SessionSchedule schedule, Calendar time1, Calendar time2, + boolean isSameSession) { + assertEquals("isSameSession is wrong", isSameSession, schedule.isSameSession(time1, time2)); + assertEquals("isSameSession is wrong", isSameSession, schedule.isSameSession(time2, time1)); + } + + private Calendar getTimeStamp(int year, int month, int day, int hour, int minute, int second, + TimeZone timeZone) { + Calendar c = new GregorianCalendar(year, month, day, hour, minute, second); + c.setTimeZone(timeZone); + return c; + } + + private Calendar getUtcTimeStamp(int year, int month, int day, int hour, int minute, int second) { + return getTimeStamp(year, month, day, hour, minute, second, TimeZone.getTimeZone("UTC")); + } + + private Calendar getUtcTime(int hour, int minute, int second) { + // Monday + return getUtcTimeStamp(2017, Calendar.JANUARY, 2, hour, minute, second); + } +}