Java tutorial
// // 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()); } }