Date Format Cache.
//
// 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());
}
}
Related examples in the same category