Mega Code Archive

 
Categories / Java / Development Class
 

Handle obtaining string timestamps

/**  *  Copyright 2007 University Of Southern California  *  *  Licensed under the Apache License, Version 2.0 (the "License");  *  you may not use this file except in compliance with the License.  *  You may obtain a copy of the License at  *  *  http://www.apache.org/licenses/LICENSE-2.0  *  *  Unless required by applicable law or agreed to in writing,  *  software distributed under the License is distributed on an "AS IS" BASIS,  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *  See the License for the specific language governing permissions and  *  limitations under the License.  */ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; /**  * Create a common interface to handle obtaining string timestamps.  * The difficult part is to allow for an ISO 8601 date formatting.  *  * @author Jens-S. Vöckler  * @author Yong Zhao  * @version $Revision: 379 $  *  * @see java.util.Date  * @see java.text.SimpleDateFormat  */ public class Currently {   /**    * This is used to format the time stamp.    */   private SimpleDateFormat m_formatter = null;   /**    * Default time format, which is compact and has a timezone    */   public static final String DEFAULT_FORMAT = "yyyy.MM.dd HH:mm:ss zzz";   /**    * Default ctor: Create a new instance with a default formatting    * string for timestamps.    */   public Currently() {     m_formatter = new SimpleDateFormat(DEFAULT_FORMAT);   }   /**    * Ctor: Create a new instance with a formatting string for time stamps.    * @param formatString complies to {@link java.text.SimpleDateFormat}.    */   public Currently(String formatString) {     m_formatter = new SimpleDateFormat(formatString);   }   /**    * Ctor: Create a new instance with a formatting string for time stamps.    * @param format is a description of the simple date format to use.    */   public Currently(SimpleDateFormat format) {     m_formatter = (SimpleDateFormat) format.clone();   }   /**    * Accessor: Obtains the default timestamp format for all queues.    *    * @return the currently active timestamp prefix format.    * @see #setDateFormat( String )    * @see #setDateFormat( SimpleDateFormat )    */   public SimpleDateFormat getDateFormat() {     return m_formatter;   }   /**    * Accessor: Sets the default timestamp format for all queues.    *    * @param format is the new timestamp prefix format.    * @see #setDateFormat( SimpleDateFormat )    * @see #getDateFormat()    */   public void setDateFormat(SimpleDateFormat format) {     if (format != null) {       m_formatter = format;     }   }   /**    * Accessor: Sets the default timestamp format for all queues.    *    * @param format is the new timestamp prefix format as a string.    * @see #setDateFormat( String )    * @see #getDateFormat()    */   public void setDateFormat(String format) {     if (format != null) {       m_formatter = new SimpleDateFormat(format);     }   }   /**    * Obtains the current time as formatted string according to    * the format option.    * @return the current time as formatted string.    * @see #now( Date )    */   public String now() {     return this.now(new Date());   }   /**    * Obtains the current time as formatted string according to    * the format option.    * @param then is a timestamp expressed as Date.    * @return the current time as formatted string.    * @see #now()    */   public String now(Date then) {     return this.m_formatter.format(then);   }   /**    * Store the regular expressions necessary to parse ISO timestamps.    */   private static final String c_expression[] = {       "([12][0-9]{3})-([01][0-9])-([0123][0-9])(T([012][0-9]):([0-6][0-9]):([0-6][0-9])?(\\.[0-9]+)?)?(Z|[-+][01][0-9]:?[0-6][0-9])?",       "([12][0-9]{3})([01][0-9])([0123][0-9])(T?([012][0-9])([0-6][0-9])([0-6][0-9])?(\\.[0-9]+)?)?(Z|[-+][01][0-9]:?[0-6][0-9])?"   };   /**    * Stores compiled patterns at first use, quasi-Singleton.    */   private static Pattern c_pattern[] = null;   /**    * Parses one of the ISO 8601 that it produces. Note, it will not    * parse the full range of ISO timestamps.    *    * @param stamp is the textual timestamp representation.    * @return a time or <code>null</code>, if unparsable.    */   public static Date parse(String stamp) {     // initialize the compiled expressions once     if (c_pattern == null) {       c_pattern = new Pattern[c_expression.length];       for (int i = 0; i < c_expression.length; ++i) {         c_pattern[i] = Pattern.compile(c_expression[i]);       }     }     // match against pattern     for (int i = 0; i < c_expression.length; ++i) {       Matcher m = c_pattern[i].matcher(stamp);       if (m.matches()) {         Calendar c = Calendar.getInstance();         TimeZone z = TimeZone.getDefault();         if (m.group(9) != null && m.group(9).length() > 0) {           boolean utc = (Character.toUpperCase(m.group(9).charAt(0)) == 'Z');           if (utc) {             z = TimeZone.getTimeZone("GMT+0");           } else {             z = TimeZone.getTimeZone("GMT" + m.group(9));           }         }         c.setTimeZone(z);         c.set(Calendar.YEAR, Integer.parseInt(m.group(1)));         c.set(Calendar.MONTH,               Integer.parseInt(m.group(2)) + (Calendar.JANUARY - 1));         c.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(3)));         if (m.group(4).length() > 0) {           c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(5)));           c.set(Calendar.MINUTE, Integer.parseInt(m.group(6)));           if (m.group(7) != null && m.group(7).length() > 0) {             c.set(Calendar.SECOND, Integer.parseInt(m.group(7)));           }           if (m.group(8) != null && m.group(8).length() > 1) {             String millis = m.group(8).substring(1);             while (millis.length() < 3) {               millis += "0";             }             millis = millis.substring(0, 3);             c.set(Calendar.MILLISECOND, Integer.parseInt(millis));           }         }         return c.getTime();       }     }     // not found     return null;   }   /**    * Ignores any internal date format, and tries to show a complete    * date/timp stamp of the current time in extended ISO 8601 format.    * UTC time (Zulu time) or a local timezone will be used. A sample for    * UTC output is 2002-04-23T02:49:58Z   A sample for local zone    * (CDT) is 2002-04-22T21:49:58-05:00    *    * @param zuluTime returns a UTC formatted stamp, if true. Otherwise    * the time will be formatted according to the local zone.    * @return an ISO 8601 formatted date and time representation for the    * current time in extended format without millisecond resolution    * @see #iso8601( boolean, boolean, boolean, Date )    */   public static String iso8601(boolean zuluTime) {     return Currently.iso8601(zuluTime, true, false, new Date());   }   /**    * Ignores any internal date format, and tries to show a complete    * date/timp stamp in extended ISO 8601 format. UTC time (Zulu time)    * or a local timezone will be used.<p>    *    * <table border=1>    * <tr><th>zone</th><th>format</th><th>fraction</td><th>example</th></tr>    * <tr><td>local</td><td>basic</td><td>integral</td><td>20020523T140427-0500</td></tr>    * <tr><td>UTC</td><td>basic</td><td>integral</td><td>20020523190427Z</td></tr>    * <tr><td>local</td><td>extd.</td><td>integral</td><td>2002-05-23T14:04:27-05:00</td></tr>    * <tr><td>UTC</td><td>extd.</td><td>integral</td><td>2002-05-23T19:04:27Z</td></tr>    * <tr><td>local</td><td>basic</td><td>millis</td><td>20020523T140427.166-0500</td></tr>    * <tr><td>UTC</td><td>basic</td><td>millis</td><td>20020523190427.166Z</td></tr>    * <tr><td>local</td><td>extd.</td><td>millis</td><td>2002-05-23T14:04:27.166-05:00</td></tr>    * <tr><td>UTC</td><td>extd.</td><td>millis</td><td>2002-05-23T19:04:27.166Z</td></tr>    * </table><p>    *    * @param zuluTime returns a UTC formatted stamp, if true. Otherwise    * the time will be formatted according to the local zone. Local time    * should be prefixed with the 'T'.    * @param extendedFormat will use the extended ISO 8601 format which    * separates the different timestamp items. If false, the basic    * format will be used. In UTC and basic format, the 'T' separator    * will be omitted.    * @param withMillis will put the millisecond extension into the timestamp.    * If false, the time will be without millisecond fraction. The separator    * is taken from {@link java.text.DecimalFormatSymbols#getMinusSign()},    * which usually is a period or a comma.    * @param now is a time stamp as Date.    * @return an ISO 8601 formatted date and time representation for    * the given point in time.    */   public static String iso8601(boolean zuluTime, boolean extendedFormat,                                boolean withMillis, Date now) {     Calendar c =         Calendar.getInstance(zuluTime ?                              TimeZone.getTimeZone("UTC") :                              TimeZone.getDefault());     c.setTime(now);     // set up formattting options     DecimalFormat nf2 = new DecimalFormat("##00");     DecimalFormat nf3 = new DecimalFormat("###000");     DecimalFormat nf4 = new DecimalFormat("####0000");     DecimalFormatSymbols dfs = nf2.getDecimalFormatSymbols();     // allocate result string buffer     int size = extendedFormat ? (zuluTime ? 25 : 30) : (zuluTime ? 21 : 25);     if (!withMillis) {       size -= 4;     }     StringBuffer result = new StringBuffer(size);     result.append(nf4.format(c.get(Calendar.YEAR)));     if (extendedFormat) {       result.append('-'); // position 5     }     result.append(nf2.format(c.get(Calendar.MONTH) + 1));     if (extendedFormat) {       result.append('-'); // position 8     }     result.append(nf2.format(c.get(Calendar.DAY_OF_MONTH)));     // generating UTC in basic format may leave out the 'T' separator     if (extendedFormat || !zuluTime) {       result.append('T'); // position 11     }     result.append(nf2.format(c.get(Calendar.HOUR_OF_DAY)));     if (extendedFormat) {       result.append(':'); // position 14     }     result.append(nf2.format(c.get(Calendar.MINUTE)));     if (extendedFormat) {       result.append(':'); // position 17     }     result.append(nf2.format(c.get(Calendar.SECOND)));     if (withMillis) {       // Though there is no explicit spec which allows a complete       // timestamp with milliseconds, it is implied through two       // levels, sigh. 5.3.4.2 allows decimal fractions with       // time-only stamps that have a timezone. The intro of 5.4.2       // allows 5.3.1.3.       result.append(dfs.getDecimalSeparator()); // position 20       result.append(nf3.format(c.get(Calendar.MILLISECOND)));     }     if (zuluTime) {       // this is easy       result.append('Z');     } else {       // time zone calculations       int zone_offset = c.get(Calendar.ZONE_OFFSET) / 1000;       int save_offset = c.get(Calendar.DST_OFFSET) / 1000;       int diff = (zone_offset + save_offset) / 60;       result.append(diff < 0 ? dfs.getMinusSign() : '+'); // position 24       if (diff < 0) {         diff = Math.abs(diff);       }       result.append(nf2.format(diff / 60));       if (extendedFormat) {         result.append(':');       }       result.append(nf2.format(diff % 60));     }     return result.toString();   } }