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);
+ }
+}