package org.marketcetera.marketdata;
import static org.marketcetera.marketdata.Messages.INVALID_DATE;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.util.misc.ClassVersion;
/**
* Offers date translation utilities for {@link MarketDataRequest} objects.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: DateUtils.java 16841 2014-02-20 19:59:04Z colin $
* @since 1.5.0
*/
@ClassVersion("$Id: DateUtils.java 16841 2014-02-20 19:59:04Z colin $")
public class DateUtils
{
public static final DateTimeFormatter YEAR = new DateTimeFormatterBuilder().appendYear(4,
4).toFormatter();
public static final DateTimeFormatter MONTH = new DateTimeFormatterBuilder().appendMonthOfYear(2).toFormatter();
public static final DateTimeFormatter DAY = new DateTimeFormatterBuilder().appendDayOfMonth(2).toFormatter();
public static final DateTimeFormatter T = new DateTimeFormatterBuilder().appendLiteral('T').toFormatter();
public static final DateTimeFormatter HOUR = new DateTimeFormatterBuilder().appendHourOfDay(2).toFormatter();
public static final DateTimeFormatter MINUTE = new DateTimeFormatterBuilder().appendMinuteOfHour(2).toFormatter();
public static final DateTimeFormatter SECOND = new DateTimeFormatterBuilder().appendSecondOfMinute(2).toFormatter();
public static final DateTimeFormatter MILLI = new DateTimeFormatterBuilder().appendMillisOfSecond(3).toFormatter();
public static final DateTimeFormatter TZ = new DateTimeFormatterBuilder().appendTimeZoneOffset("Z", //$NON-NLS-1$
true,
2,
4).toFormatter();
/**
* date format to millisecond precision with timezone: <code>yyyyMMdd'T'HHmmssSSSZ</code>
*/
public static final DateTimeFormatter MILLIS_WITH_TZ = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(T).append(HOUR).append(MINUTE).append(SECOND).append(MILLI).append(TZ).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to millisecond precision without timezone: <code>yyyyMMdd'T'HHmmssSSS</code>
*/
public static final DateTimeFormatter MILLIS = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(T).append(HOUR).append(MINUTE).append(SECOND).append(MILLI).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to second precision with timezone: <code>yyyyMMdd'T'HHmmssZ</code>
*/
public static final DateTimeFormatter SECONDS_WITH_TZ = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(T).append(HOUR).append(MINUTE).append(SECOND).append(TZ).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to second precision without timezone: <code>yyyyMMdd'T'HHmmss</code>
*/
public static final DateTimeFormatter SECONDS = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(T).append(HOUR).append(MINUTE).append(SECOND).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to minute precision with timezone: <code>yyyyMMdd'T'HHmmZ</code>
*/
public static final DateTimeFormatter MINUTES_WITH_TZ = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(T).append(HOUR).append(MINUTE).append(TZ).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to minute precision without timezone: <code>yyyyMMdd'T'HHmm</code>
*/
public static final DateTimeFormatter MINUTES = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(T).append(HOUR).append(MINUTE).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to day-of-month precision with timezone: <code>yyyyMMddZ</code>
*/
public static final DateTimeFormatter DAYS_WITH_TZ = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).append(TZ).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format to day-of-month precision without timezone: <code>yyyyMMdd</code>
*/
public static final DateTimeFormatter DAYS = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).toFormatter().withZone(DateTimeZone.UTC);
/**
* date format needed for FIX specified UTCTimestamp type
*/
public static final DateTimeFormatter FIX = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).appendLiteral('-').append(HOUR).appendLiteral(':').append(MINUTE).appendLiteral(':').append(SECOND).toFormatter();
/**
* date format needed for FIX specified UTCTimestamp type (with millis)
*/
public static final DateTimeFormatter FIX_MILLIS = new DateTimeFormatterBuilder().append(YEAR).append(MONTH).append(DAY).appendLiteral('-').append(HOUR).appendLiteral(':').append(MINUTE).appendLiteral(':').append(SECOND).appendLiteral('.').append(MILLI).toFormatter();
/**
* valid date formats
*/
private static final DateTimeFormatter[] DATE_FORMATS = new DateTimeFormatter[] {
MILLIS_WITH_TZ,
MILLIS,
SECONDS_WITH_TZ,
SECONDS,
MINUTES_WITH_TZ,
MINUTES,
DAYS_WITH_TZ,
DAYS };
/**
* default date pattern
*/
private static final DateTimeFormatter DEFAULT_FORMAT = MILLIS_WITH_TZ;
/**
* Converts the given <code>Date</code> value to a <code>String</code> representation usable with
* {@link MarketDataRequest} objects.
*
* <p>The format of the returned value is ISO 8601 basic format to millisecond precision with
* time zone offset. This format can be expressed as: <code>yyyyMMdd'T'HHmmssSSSZ</code> in terms of the format expected
* by {@link SimpleDateFormat}.
*
* @param inDate a <code>Date</code> value
* @return a <code>String</code> value
*/
public static String dateToString(Date inDate)
{
return dateToString(inDate,
DEFAULT_FORMAT);
}
/**
* Converts the given <code>Date</code> value to a <code>String</code> representation in the given format usable with
* {@link MarketDataRequest} objects.
*
* @param inDate a <code>Date</code> value
* @param inFormat a <code>DateTimeFormatter</code> value
* @return a <code>String</code> value
*/
public static String dateToString(Date inDate,
DateTimeFormatter inFormat)
{
return inFormat.print(new DateTime(inDate));
}
/**
* Parses the given <code>String</code> to a <code>Date</code> value.
*
* <p>The given <code>String</code> is expected to be formatted in ISO 8601 basic format as described
* by {@link DateUtils#dateToString(Date)}. The following formats are accepted:
* <ul>
* <li>yyyyMMdd'T'HHmmssSSSZ (e.g. 20090303T224025444-0800)</li>
* <li>yyyyMMdd'T'HHmmssSSS (e.g. 20090303T224025444)</li>
* <li>yyyyMMdd'T'HHmmssZ (e.g. 20090303T224025-0800)</li>
* <li>yyyyMMdd'T'HHmmss (e.g. 20090303T224025)</li>
* <li>yyyyMMdd'T'HHmmZ (e.g. 20090303T2240-0800)</li>
* <li>yyyyMMdd'T'HHmm (e.g. 20090303T2240)</li>
* <li>yyyyMMddZ (e.g. 20090303-0800)</li>
* <li>yyyyMMdd (e.g. 20090303)</li>
* </ul>
*
* <p>If the timezone offset is omitted from the given <code>String</code>, the date/time is assumed
* to be in UTC. If omitted, milliseconds, seconds, minutes, and hours are set to zero. Fields
* may not be abbreviated, i.e., minutes must contain two digits even if the value is less than ten,
* <code>01</code> instead of <code>1</code>. Timezone offsets may be expressed as <code>Z</code>
* for UTC or as an offset from UTC indicated by <code>+</code> or <code>-</code> and four digits.
* With the exception of the timezone offset indicator, no punctuation is allowed in the expression.
*
* @param inDateString a <code>String</code> value a <code>String</code> containing a date value to be
* parsed.
* @return a <code>Date</code> value
* @throws MarketDataRequestException if the given <code>String</code> could not be parsed
*/
public static Date stringToDate(String inDateString)
throws MarketDataRequestException
{
if(inDateString == null ||
inDateString.isEmpty()) {
throw new MarketDataRequestException(new I18NBoundMessage1P(INVALID_DATE,
inDateString));
}
for(int formatCounter=0;formatCounter<DATE_FORMATS.length;formatCounter++) {
try {
return new Date(DATE_FORMATS[formatCounter].parseDateTime(inDateString).getMillis());
} catch (IllegalArgumentException e) {
// this format didn't work, try a less specific one
}
}
throw new MarketDataRequestException(new I18NBoundMessage1P(INVALID_DATE,
inDateString));
}
}