package hk.hku.cecid.ebms.spa; import java.sql.Timestamp; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; import java.util.Calendar; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.Locale; import hk.hku.cecid.piazza.commons.util.DataFormatter; import hk.hku.cecid.piazza.commons.util.UtilitiesException; /** * An utility for converting from java datetime to UTC conformed datetime and vice versa.<br/><br/> * * <ol> * <li>Fixed a bug that it parses UTC datetime with 'Z' timezone incorrectly becasue it used * wrong timezone (GMT, but not UTC). (Thank Martin Kalen for figure out this)</li> * <li>Added datetime / calendar two UTC convertion.</li> * </ol> * * @author Twinsen Tsang, (modifier Philip Wong) * @since 1/6/2007 * @version 1.0.2 */ public class EbmsUtility { // The internal regex for ISO8601 UTC conformed datetime. private static final String UTC_REGEX_PATTERN = "-?(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(Z|([\\+-]\\d{2}:\\d{2}))?"; // The number of group matching UTC regular expression pattern (8) including the whole string. private static final int UTC_CAPTURE_GROUP = 9; // The UTC regex pattern matcher. private static final Pattern UTC_PATTERNER = Pattern.compile(UTC_REGEX_PATTERN); /** * Convert an UTC representation of string <code>dateTime</code> to java Timestamp object. * * Followings are UTC conformed patterns: * <ul> * <li>2007-07-16T13:24:56<li> * <li>2007-07-16T13:24:56Z<li> * <li>2007-07-16T13:24:56.789<li> * <li>2007-07-16T13:24:56.789Z<li> * <li>2007-07-16T13:24:56+02:00<li> * <li>2007-07-16T13:24:56-02:00<li> * <li>2007-07-16T13:24:56.789+02:00<li> * <li>2007-07-16T13:24:56.789-02:00<li> * </ul> * * @param dateTime A string representing a UTC datetime. * @return A java timestamp object representing the <code>dateTime</code>. * @throws UtilitiesException When unable to convert the dateTime format. */ public static Timestamp UTC2Timestamp(String dateTime) throws UtilitiesException { return new Timestamp(UTC2MS(dateTime)); } /** * Convert an UTC representation of string <code>dateTime</code> to java Date object. * * Followings are UTC conformed patterns: * <ul> * <li>2007-07-16T13:24:56<li> * <li>2007-07-16T13:24:56Z<li> * <li>2007-07-16T13:24:56.789<li> * <li>2007-07-16T13:24:56.789Z<li> * <li>2007-07-16T13:24:56+02:00<li> * <li>2007-07-16T13:24:56-02:00<li> * <li>2007-07-16T13:24:56.789+02:00<li> * <li>2007-07-16T13:24:56.789-02:00<li> * </ul> * * @param dateTime A string representing a UTC datetime. * @return A java date object representing the <code>dateTime</code>. * @throws UtilitiesException When unable to convert the dateTime format. */ public static Date UTC2Date(String dateTime) throws UtilitiesException { return new Date(UTC2MS(dateTime)); } /** * Convert an UTC representation of string <code>dateTime</code> to millisecond. * * Followings are UTC conformed patterns: * <ul> * <li>2007-07-16T13:24:56<li> * <li>2007-07-16T13:24:56Z<li> * <li>2007-07-16T13:24:56.789<li> * <li>2007-07-16T13:24:56.789Z<li> * <li>2007-07-16T13:24:56+02:00<li> * <li>2007-07-16T13:24:56-02:00<li> * <li>2007-07-16T13:24:56.789+02:00<li> * <li>2007-07-16T13:24:56.789-02:00<li> * </ul> * * @param dateTime A string representing a UTC datetime. * @return The millisecond representing by <code>dateTime</code>. * @throws UtilitiesException When unable to convert the dateTime format. */ public static long UTC2MS(String dateTime) throws UtilitiesException { return UTC2Calendar(dateTime).getTimeInMillis(); } /** * Convert an UTC representation of string <code>dateTime</code> to java calendar object. * * Followings are UTC conformed patterns: * <ul> * <li>2007-07-16T13:24:56<li> * <li>2007-07-16T13:24:56Z<li> * <li>2007-07-16T13:24:56.789<li> * <li>2007-07-16T13:24:56.789Z<li> * <li>2007-07-16T13:24:56+02:00<li> * <li>2007-07-16T13:24:56-02:00<li> * <li>2007-07-16T13:24:56.789+02:00<li> * <li>2007-07-16T13:24:56.789-02:00<li> * </ul> * * @param dateTime A string representing a UTC datetime. * @return A java clendar object representing the <code>dateTime</code> * @throws UtilitiesException When unable to convert the dateTime format. */ public static Calendar UTC2Calendar(String dateTime) throws UtilitiesException { Matcher m = UTC_PATTERNER.matcher(dateTime); if (m.matches() && m.groupCount() == UTC_CAPTURE_GROUP) { int[] parts = new int[UTC_CAPTURE_GROUP - 1]; // parsing year, month, days, hours, minutes and second. int i; // i = 0 is group of whole pattern for (i = 0; i < 6; i++) parts[i] = Integer.parseInt(m.group(i + 1)); // parsing millesecond (OPTIONAL) String ms = m.group(i + 1); if (ms == null) { parts[i++] = 0; } else { if (ms.length() > 3) ms = ms.substring(0, 3); // chop the sub second part to millesecond. parts[i++] = Integer.parseInt(ms); } // parsing Timezone (Z OR [+-]hh:mm) String tzStr = m.group(i + 1); TimeZone tz = null; if (tzStr == null || tzStr.equals("Z")) // Case with or without Z tz = TimeZone.getTimeZone("UTC"); else tz = TimeZone.getTimeZone("GMT" + tzStr); Calendar c = Calendar.getInstance(); c.clear(); c.setTimeZone(tz); c.set(parts[0], parts[1]-1, parts[2], parts[3], parts[4], parts[5]); c.add(Calendar.MILLISECOND, parts[6]); // parts[6] = millescond return c; // parts[6] = millescond } else { throw new UtilitiesException("Unable to convert datetime to UTC format:" + dateTime); } } /** * Convert a date <code>dateTime</code> to UTC conformed representation of string. (e.g. 2007-07-16T13:24:56.789+13:00) * * @param dateTime * A java date representing the time you want to convert. * @param timeZone * The timezone of the <code>dateTime</code>. If it is null, it use the default * timezone in the machine. * @return * an UTC conformed representation with time specified by <code>dateTime</code> * * @throws UtilitiesException When unable to convert the <code>dateTime</code> to UTC. */ public static String date2UTC(Date dateTime, TimeZone timeZone) throws UtilitiesException { try{ // Create the formatter. SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); dateFormatter.setLenient(false); dateFormatter.setTimeZone(timeZone); DecimalFormat twoDigits = new DecimalFormat("00"); // Format the date to UTC representation with out timezone information. String utc = dateFormatter.format(dateTime); // Calculate the timezone offset from the date to convert. int tzOffset = timeZone.getOffset(dateTime.getTime()); String sign = "+"; // default using plus sign if (tzOffset < 0){ sign = "-"; tzOffset = -tzOffset; } int hours = tzOffset / 3600000; int minutes = (tzOffset % 3600000) / 60000; // Create return string using buffer return new StringBuffer(utc.length() + 7) .append(utc) .append(sign) .append(twoDigits.format(hours)) .append(":") .append(twoDigits.format(minutes)) .toString(); } catch (Exception ex){ throw new UtilitiesException(ex); } } /** * Convert a calendar <code>dateTime</code> to UTC conformed representation of string. * * @param dateTime A java calendar representing the time you want to convert. * @return * an UTC conformed representation with time specified by <code>dateTime</code> * @throws UtilitiesException * When unable to convert the <code>dateTime</code> to UTC. */ public static String calendar2UTC(Calendar dateTime) throws UtilitiesException { return EbmsUtility.date2UTC(dateTime.getTime(), dateTime.getTimeZone()); } /** * @return Get the current datetime with respect to the default timezone in UTC * conformed representation. */ public static String getCurrentUTCDateTime() { // Suppress exception. try { return EbmsUtility.date2UTC(new Date(), TimeZone.getDefault()); } catch (UtilitiesException ex){ } return null; } /** * Convert an GMT representation of string <code>dateTime</code> to java Timestamp object. * * @param dateTime A string representing a GMT datetime. * @return A java timestamp object representing the <code>dateTime</code> * @throws UtilitiesException When unable to convert the dateTime format. */ public static Timestamp GMT2Timestamp(String dateTime) throws UtilitiesException { return new Timestamp(EbmsUtility.GMT2Date(dateTime).getTime()); } /** * Convert an GMT representation of string <code>dateTime</code> to millisecond. * * @param dateTime A string representing a GMT datetime. * @return The millisecond representing the <code>dateTime</code> * @throws UtilitiesException When unable to convert the dateTime format. */ public static long GMT2MS(String dateTime) throws UtilitiesException { return EbmsUtility.GMT2Date(dateTime).getTime(); } /** * Convert an GMT representation of string <code>dateTime</code> to java calendar object. * * @param dateTime A string representing a GMT datetime. * @return The millisecond representing the <code>dateTime</code> * @throws UtilitiesException When unable to convert the dateTime format. */ public static Calendar GMT2Calender(String dateTime) throws UtilitiesException { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(EbmsUtility.GMT2Date(dateTime).getTime()); return cal; } /** * Convert an GMT representation of string <code>dateTime</code> to java Date object. * * @param dateTime A string representing a GMT datetime. * @return A java date object representing the <code>dateTime</code> * @throws UtilitiesException When unable to convert the dateTime format. */ public static Date GMT2Date(String dateTime) throws UtilitiesException { // GMT Example: Wed Jul 25 09:59:00 GMT+08:00 2007 // Reminder: We need to use locale.US for parsing the above dateTime. Date GMTdate = DataFormatter.getInstance().parseDate(dateTime, "EEE MMM dd HH:mm:ss zz yyyy", Locale.US); if (GMTdate == null) throw new UtilitiesException("Unable to convert datetime to GMT format:" + dateTime); return GMTdate; } /** * Adds an offset to the current time specificed in seconds to current date/time * * @param int offset in seconds to apply. * @return A string object representing the <code>dateTime</code> with offset applied * @throws UtilitiesException When unable to convert the dateTime format. */ public static String applyTimeToLiveOffset(int offset) throws UtilitiesException { return applyTimeToLiveOffset(new Date(), offset); } /** * Adds an offset to the current time specificed in seconds to current date/time * * @param int offset in seconds to apply. * @return A string object representing the <code>dateTime</code> with offset applied * @throws UtilitiesException When unable to convert the dateTime format. */ public static String applyTimeToLiveOffset(Date date, int offset) throws UtilitiesException { Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.add(Calendar.SECOND,offset); return date2UTC(cal.getTime(), TimeZone.getDefault()); } }