Development Class Java

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