/*******************************************************************************
* 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;
/**
* Corresponds to SessionTime in C++ code
*/
class SessionSchedule {
private static final int NOT_SET = -1;
private static final Pattern TIME_PATTERN = Pattern.compile("(\\d{2}):(\\d{2}):(\\d{2})(.*)");
private final TimeEndPoint startTime;
private final TimeEndPoint endTime;
private final boolean nonStopSession;
protected final static Logger log = LoggerFactory.getLogger(SessionSchedule.class);
SessionSchedule(SessionSettings settings, SessionID sessionID) throws ConfigError,
FieldConvertError {
if (settings.isSetting(SessionConstants.SETTING_NON_STOP_SESSION)) {
nonStopSession = settings
.getBool(SessionConstants.SETTING_NON_STOP_SESSION);
} else {
nonStopSession = false;
}
boolean startDayPresent = settings.isSetting(SessionConstants.SETTING_START_DAY);
boolean endDayPresent = settings.isSetting(SessionConstants.SETTING_END_DAY);
if (startDayPresent && !endDayPresent) {
throw new ConfigError("Session " + sessionID + ": StartDay used without EndDay");
}
if (endDayPresent && !startDayPresent) {
throw new ConfigError("Session " + sessionID + ": EndDay used without StartDay");
}
TimeZone defaultTimeZone = getDefaultTimeZone(settings, sessionID);
startTime = getTimeEndPoint(settings, sessionID, defaultTimeZone, SessionConstants.SETTING_START_TIME, SessionConstants.SETTING_START_DAY);
endTime = getTimeEndPoint(settings, sessionID, defaultTimeZone, SessionConstants.SETTING_END_TIME, SessionConstants.SETTING_END_DAY);
if (!nonStopSession) log.info("["+sessionID+"] "+toString());
}
private TimeEndPoint getTimeEndPoint(SessionSettings settings, SessionID sessionID,
TimeZone defaultTimeZone, String timeSetting, String daySetting) throws ConfigError,
FieldConvertError {
Matcher matcher = TIME_PATTERN.matcher(settings.getString(timeSetting));
if (!matcher.find()) {
throw new ConfigError("Session " + sessionID + ": could not parse time '"
+ settings.getString(timeSetting) + "'.");
}
TimeZone timeZone = getTimeZone(matcher.group(4), defaultTimeZone);
Calendar localTime = SystemTime.getUtcCalendar();
localTime.setTimeZone(timeZone);
localTime.set(Calendar.HOUR_OF_DAY, Integer.parseInt(matcher.group(1)));
localTime.set(Calendar.MINUTE, Integer.parseInt(matcher.group(2)));
localTime.set(Calendar.SECOND, Integer.parseInt(matcher.group(3)));
int scheduleDay = getDay(settings, sessionID, daySetting, NOT_SET);
if (scheduleDay != NOT_SET) {
localTime.set(Calendar.DAY_OF_WEEK, scheduleDay);
}
Calendar utcTime = SystemTime.getUtcCalendar();
utcTime.setTime(localTime.getTime());
return new TimeEndPoint(scheduleDay == NOT_SET ? NOT_SET : utcTime.get(Calendar.DAY_OF_WEEK), utcTime, utcTime.getTimeZone());
}
private TimeZone getDefaultTimeZone(SessionSettings settings, SessionID sessionID)
throws ConfigError, FieldConvertError {
TimeZone sessionTimeZone;
if (settings.isSetting(SessionConstants.SETTING_TIMEZONE)) {
String sessionTimeZoneID = settings.getString(SessionConstants.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 weekDay;
private final int hour;
private final int minute;
private final int second;
private final int timeInSeconds;
private final TimeZone tz;
public TimeEndPoint(int day, Calendar c, TimeZone tz) {
this(day, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND), tz);
}
public TimeEndPoint(int day, int hour, int minute, int second, TimeZone tz) {
weekDay = day;
this.hour = hour;
this.minute = minute;
this.second = second;
this.tz = tz;
timeInSeconds = timeInSeconds(hour, minute, second);
}
public int getHour() {
return hour;
}
public int getMinute() {
return minute;
}
public int getSecond() {
return second;
}
public String toString() {
try {
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 (isSet(weekDay) ? DayConverter.toString(weekDay) + "," : "")
+ utc.format(calendar.getTime()) + "-" + utc.getTimeZone().getID();
} catch (ConfigError e) {
return "ERROR: " + e;
}
}
public int getDay() {
return weekDay;
}
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;
}
public TimeZone getTimeZone() {
return tz;
}
}
private TimeInterval theMostRecentIntervalBefore(Calendar t) {
TimeInterval timeInterval = new TimeInterval();
Calendar intervalStart = timeInterval.getStart();
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);
if (isSet(startTime.getDay())) {
intervalStart.set(Calendar.DAY_OF_WEEK, startTime.getDay());
if (intervalStart.getTimeInMillis() > t.getTimeInMillis()) {
intervalStart.add(Calendar.WEEK_OF_YEAR, -1);
}
} else if (intervalStart.getTimeInMillis() > t.getTimeInMillis()) {
intervalStart.add(Calendar.DAY_OF_YEAR, -1);
}
Calendar intervalEnd = timeInterval.getEnd();
intervalEnd.setTimeInMillis(intervalStart.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);
if (isSet(endTime.getDay())) {
intervalEnd.set(Calendar.DAY_OF_WEEK, endTime.getDay());
if (intervalEnd.getTimeInMillis() <= intervalStart.getTimeInMillis()) {
intervalEnd.add(Calendar.WEEK_OF_MONTH, 1);
}
} else if (intervalEnd.getTimeInMillis() <= intervalStart.getTimeInMillis()) {
intervalEnd.add(Calendar.DAY_OF_WEEK, 1);
}
return timeInterval;
}
private static class TimeInterval {
private Calendar start = SystemTime.getUtcCalendar();
private Calendar end = SystemTime.getUtcCalendar();
public boolean isContainingTime(Calendar t) {
return t.equals(start) || t.equals(end) || (t.after(start) && t.before(end));
}
public String toString() {
return start.getTime() + " --> " + end.getTime();
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof TimeInterval == false) {
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;
}
public Calendar getStart() {
return start;
}
public Calendar getEnd() {
return end;
}
}
public boolean isSameSession(Calendar time1, Calendar time2) {
if (nonStopSession) return true;
TimeInterval interval1 = theMostRecentIntervalBefore(time1);
if (!interval1.isContainingTime(time1)) {
return false;
}
TimeInterval interval2 = theMostRecentIntervalBefore(time2);
if (!interval2.isContainingTime(time2)) {
return false;
}
return interval1.equals(interval2);
}
private boolean isDailySession() {
return !isSet(startTime.getDay()) && !isSet(endTime.getDay());
}
public boolean isSessionTime() {
Calendar now = SystemTime.getUtcCalendar();
TimeInterval interval = theMostRecentIntervalBefore(now);
return interval.isContainingTime(now);
}
public String toString() {
StringBuffer buf = new StringBuffer();
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() != SystemTime.UTC_TIMEZONE
|| endTime.getTimeZone() != SystemTime.UTC_TIMEZONE) {
buf.append(" (");
formatTimeInterval(buf, ti, timeFormat, true);
buf.append(")");
}
return buf.toString();
}
private void formatTimeInterval(StringBuffer buf, TimeInterval timeInterval,
SimpleDateFormat timeFormat, boolean local) {
if (!isDailySession()) {
buf.append("weekly, ");
formatDayOfWeek(buf, startTime.getDay());
buf.append(" ");
} else {
buf.append("daily, ");
}
if (local) {
timeFormat.setTimeZone(startTime.getTimeZone());
}
buf.append(timeFormat.format(timeInterval.getStart().getTime()));
buf.append(" - ");
if (!isDailySession()) {
formatDayOfWeek(buf, endTime.getDay());
buf.append(" ");
}
if (local) {
timeFormat.setTimeZone(endTime.getTimeZone());
}
buf.append(timeFormat.format(timeInterval.getEnd().getTime()));
}
private void formatDayOfWeek(StringBuffer buf, int dayOfWeek) {
try {
String dayName = DayConverter.toString(dayOfWeek).toUpperCase();
if (dayName.length() > 3) {
dayName = dayName.substring(0, 3);
}
buf.append(dayName);
} catch (ConfigError e) {
buf.append("[Error: unknown day " + dayOfWeek + "]");
}
}
private int getDay(SessionSettings settings, SessionID sessionID, String key, int defaultValue)
throws ConfigError, FieldConvertError {
return settings.isSetting(key) ?
DayConverter.toInteger(settings.getString(key))
: NOT_SET;
}
private boolean isSet(int value) {
return value != NOT_SET;
}
private int timeInSeconds(int hour, int minute, int second) {
return (hour * 3600) + (minute * 60) + second;
}
}