DateCache.java Source code

Java tutorial

Introduction

Here is the source code for DateCache.java

Source

// 
// Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/* ------------------------------------------------------------ */
/**
 * Date Format Cache. Computes String representations of Dates and caches the
 * results so that subsequent requests within the same minute will be fast.
 * 
 * Only format strings that contain either "ss" or "ss.SSS" are handled.
 * 
 * The timezone of the date may be included as an ID with the "zzz" format
 * string or as an offset with the "ZZZ" format string.
 * 
 * If consecutive calls are frequently very different, then this may be a little
 * slower than a normal DateFormat.
 * 
 * @author Kent Johnson <KJohnson@transparent.com>
 * @author Greg Wilkins (gregw)
 */

public class DateCache {
    private static long __hitWindow = 60 * 60;

    private static long __MaxMisses = 10;

    private String _formatString;

    private String _tzFormatString;

    private SimpleDateFormat _tzFormat;

    private String _minFormatString;

    private SimpleDateFormat _minFormat;

    private String _secFormatString;

    private String _secFormatString0;

    private String _secFormatString1;

    private boolean _millis = false;

    private long _misses = 0;

    private long _lastMinutes = -1;

    private long _lastSeconds = -1;

    private String _lastResult = null;

    private Locale _locale = null;

    private DateFormatSymbols _dfs = null;

    /* ------------------------------------------------------------ */
    /**
     * Constructor. Make a DateCache that will use a default format. The default
     * format generates the same results as Date.toString().
     */
    public DateCache() {
        this("EEE MMM dd HH:mm:ss zzz yyyy");
        getFormat().setTimeZone(TimeZone.getDefault());
    }

    /* ------------------------------------------------------------ */
    /**
     * Constructor. Make a DateCache that will use the given format
     */
    public DateCache(String format) {
        _formatString = format;
        setTimeZone(TimeZone.getDefault());

    }

    /* ------------------------------------------------------------ */
    public DateCache(String format, Locale l) {
        _formatString = format;
        _locale = l;
        setTimeZone(TimeZone.getDefault());
    }

    /* ------------------------------------------------------------ */
    public DateCache(String format, DateFormatSymbols s) {
        _formatString = format;
        _dfs = s;
        setTimeZone(TimeZone.getDefault());
    }

    /* ------------------------------------------------------------ */
    /**
     * Set the timezone.
     * 
     * @param tz
     *          TimeZone
     */
    public void setTimeZone(TimeZone tz) {
        setTzFormatString(tz);
        if (_locale != null) {
            _tzFormat = new SimpleDateFormat(_tzFormatString, _locale);
            _minFormat = new SimpleDateFormat(_minFormatString, _locale);
        } else if (_dfs != null) {
            _tzFormat = new SimpleDateFormat(_tzFormatString, _dfs);
            _minFormat = new SimpleDateFormat(_minFormatString, _dfs);
        } else {
            _tzFormat = new SimpleDateFormat(_tzFormatString);
            _minFormat = new SimpleDateFormat(_minFormatString);
        }
        _tzFormat.setTimeZone(tz);
        _minFormat.setTimeZone(tz);
        _lastSeconds = -1;
        _lastMinutes = -1;
    }

    /* ------------------------------------------------------------ */
    public TimeZone getTimeZone() {
        return _tzFormat.getTimeZone();
    }

    /* ------------------------------------------------------------ */
    /**
     * Set the timezone.
     * 
     * @param timeZoneId
     *          TimeZoneId the ID of the zone as used by TimeZone.getTimeZone(id)
     */
    public void setTimeZoneID(String timeZoneId) {
        setTimeZone(TimeZone.getTimeZone(timeZoneId));
    }

    /* ------------------------------------------------------------ */
    private void setTzFormatString(final TimeZone tz) {
        int zIndex = _formatString.indexOf("ZZZ");
        if (zIndex >= 0) {
            String ss1 = _formatString.substring(0, zIndex);
            String ss2 = _formatString.substring(zIndex + 3);
            int tzOffset = tz.getRawOffset();

            StringBuffer sb = new StringBuffer(_formatString.length() + 10);
            sb.append(ss1);
            sb.append("'");
            if (tzOffset >= 0)
                sb.append('+');
            else {
                tzOffset = -tzOffset;
                sb.append('-');
            }

            int raw = tzOffset / (1000 * 60); // Convert to seconds
            int hr = raw / 60;
            int min = raw % 60;

            if (hr < 10)
                sb.append('0');
            sb.append(hr);
            if (min < 10)
                sb.append('0');
            sb.append(min);
            sb.append('\'');

            sb.append(ss2);
            _tzFormatString = sb.toString();
        } else
            _tzFormatString = _formatString;
        setMinFormatString();
    }

    /* ------------------------------------------------------------ */
    private void setMinFormatString() {
        int i = _tzFormatString.indexOf("ss.SSS");
        int l = 6;
        if (i >= 0)
            _millis = true;
        else {
            i = _tzFormatString.indexOf("ss");
            l = 2;
        }

        // Build a formatter that formats a second format string
        // Have to replace @ with ' later due to bug in SimpleDateFormat
        String ss1 = _tzFormatString.substring(0, i);
        String ss2 = _tzFormatString.substring(i + l);
        _minFormatString = ss1 + (_millis ? "'ss.SSS'" : "'ss'") + ss2;
    }

    /* ------------------------------------------------------------ */
    /**
     * Format a date according to our stored formatter.
     * 
     * @param inDate
     * @return Formatted date
     */
    public synchronized String format(Date inDate) {
        return format(inDate.getTime());
    }

    /* ------------------------------------------------------------ */
    /**
     * Format a date according to our stored formatter.
     * 
     * @param inDate
     * @return Formatted date
     */
    public synchronized String format(long inDate) {
        long seconds = inDate / 1000;

        // Is it not suitable to cache?
        if (seconds < _lastSeconds || _lastSeconds > 0 && seconds > _lastSeconds + __hitWindow) {
            // It's a cache miss
            _misses++;
            if (_misses < __MaxMisses) {
                Date d = new Date(inDate);
                return _tzFormat.format(d);
            }
        } else if (_misses > 0)
            _misses--;

        // Check if we are in the same second
        // and don't care about millis
        if (_lastSeconds == seconds && !_millis)
            return _lastResult;

        Date d = new Date(inDate);

        // Check if we need a new format string
        long minutes = seconds / 60;
        if (_lastMinutes != minutes) {
            _lastMinutes = minutes;
            _secFormatString = _minFormat.format(d);

            int i;
            int l;
            if (_millis) {
                i = _secFormatString.indexOf("ss.SSS");
                l = 6;
            } else {
                i = _secFormatString.indexOf("ss");
                l = 2;
            }
            _secFormatString0 = _secFormatString.substring(0, i);
            _secFormatString1 = _secFormatString.substring(i + l);
        }

        // Always format if we get here
        _lastSeconds = seconds;
        StringBuffer sb = new StringBuffer(_secFormatString.length());
        synchronized (sb) {
            sb.append(_secFormatString0);
            int s = (int) (seconds % 60);
            if (s < 10)
                sb.append('0');
            sb.append(s);
            if (_millis) {
                long millis = inDate % 1000;
                if (millis < 10)
                    sb.append(".00");
                else if (millis < 100)
                    sb.append(".0");
                else
                    sb.append('.');
                sb.append(millis);
            }
            sb.append(_secFormatString1);
            _lastResult = sb.toString();
        }

        return _lastResult;
    }

    /* ------------------------------------------------------------ */
    /**
     * Format to string buffer.
     * 
     * @param inDate
     *          Date the format
     * @param buffer
     *          StringBuffer
     */
    public void format(long inDate, StringBuffer buffer) {
        buffer.append(format(inDate));
    }

    /* ------------------------------------------------------------ */
    /**
     * Get the format.
     */
    public SimpleDateFormat getFormat() {
        return _minFormat;
    }

    /* ------------------------------------------------------------ */
    public String getFormatString() {
        return _formatString;
    }

    /* ------------------------------------------------------------ */
    public String now() {
        return format(System.currentTimeMillis());
    }
}