Currently.java Source code

Java tutorial

Introduction

Here is the source code for Currently.java

Source

/**
 *  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. Vckler
 * @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();
    }

}