//
// 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.
//
/* ------------------------------------------------------------ */
/**
* 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 <[email protected]>
* @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 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;
}