View Javadoc

    1   // ========================================================================
    2   // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14
    15  package org.mortbay.util;
    16
    17  import java.text.DateFormatSymbols;
    18  import java.text.SimpleDateFormat;
    19  import java.util.Date;
    20  import java.util.Locale;
    21  import java.util.TimeZone;
    22
    23  /* ------------------------------------------------------------ */
    24  /**  Date Format Cache.
    25   * Computes String representations of Dates and caches
    26   * the results so that subsequent requests within the same minute
    27   * will be fast.
    28   *
    29   * Only format strings that contain either "ss" or "ss.SSS" are
    30   * handled.
    31   *
    32   * The timezone of the date may be included as an ID with the "zzz"
    33   * format string or as an offset with the "ZZZ" format string.
    34   *
    35   * If consecutive calls are frequently very different, then this
    36   * may be a little slower than a normal DateFormat.
    37   *
    38   * @author Kent Johnson <KJohnson@transparent.com>
    39   * @author Greg Wilkins (gregw)
    40   */
    41
    42  public class DateCache
    43  {
    44      public static String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
    45      private static long __hitWindow=60*60;
    46
    47      private String _formatString;
    48      private String _tzFormatString;
    49      private SimpleDateFormat _tzFormat;
    50
    51      private String _minFormatString;
    52      private SimpleDateFormat _minFormat;
    53
    54      private String _secFormatString;
    55      private String _secFormatString0;
    56      private String _secFormatString1;
    57
    58      private long _lastMinutes = -1;
    59      private long _lastSeconds = -1;
    60      private int _lastMs = -1;
    61      private String _lastResult = null;
    62
    63      private Locale _locale	= null;
    64      private DateFormatSymbols	_dfs	= null;
    65
    66      /* ------------------------------------------------------------ */
    67      /** Constructor.
    68       * Make a DateCache that will use a default format. The default format
    69       * generates the same results as Date.toString().
    70       */
    71      public DateCache()
    72      {
    73          this(DEFAULT_FORMAT);
    74          getFormat().setTimeZone(TimeZone.getDefault());
    75      }
    76
    77      /* ------------------------------------------------------------ */
    78      /** Constructor.
    79       * Make a DateCache that will use the given format
    80       */
    81      public DateCache(String format)
    82      {
    83          _formatString=format;
    84          setTimeZone(TimeZone.getDefault());
    85
    86      }
    87
    88      /* ------------------------------------------------------------ */
    89      public DateCache(String format,Locale l)
    90      {
    91          _formatString=format;
    92          _locale = l;
    93          setTimeZone(TimeZone.getDefault());
    94      }
    95
    96      /* ------------------------------------------------------------ */
    97      public DateCache(String format,DateFormatSymbols s)
    98      {
    99          _formatString=format;
    100         _dfs = s;
    101         setTimeZone(TimeZone.getDefault());
    102     }
    103
    104     /* ------------------------------------------------------------ */
    105     /** Set the timezone.
    106      * @param tz TimeZone
    107      */
    108     public void setTimeZone(TimeZone tz)
    109     {
    110         setTzFormatString(tz);
    111         if( _locale != null )
    112         {
    113             _tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
    114             _minFormat=new SimpleDateFormat(_minFormatString,_locale);
    115         }
    116         else if( _dfs != null )
    117         {
    118             _tzFormat=new SimpleDateFormat(_tzFormatString,_dfs);
    119             _minFormat=new SimpleDateFormat(_minFormatString,_dfs);
    120         }
    121         else
    122         {
    123             _tzFormat=new SimpleDateFormat(_tzFormatString);
    124             _minFormat=new SimpleDateFormat(_minFormatString);
    125         }
    126         _tzFormat.setTimeZone(tz);
    127         _minFormat.setTimeZone(tz);
    128         _lastSeconds=-1;
    129         _lastMinutes=-1;
    130     }
    131
    132     /* ------------------------------------------------------------ */
    133     public TimeZone getTimeZone()
    134     {
    135         return _tzFormat.getTimeZone();
    136     }
    137
    138     /* ------------------------------------------------------------ */
    139     /** Set the timezone.
    140      * @param timeZoneId TimeZoneId the ID of the zone as used by
    141      * TimeZone.getTimeZone(id)
    142      */
    143     public void setTimeZoneID(String timeZoneId)
    144     {
    145         setTimeZone(TimeZone.getTimeZone(timeZoneId));
    146     }
    147
    148     /* ------------------------------------------------------------ */
    149     private void setTzFormatString(final  TimeZone tz )
    150     {
    151         int zIndex = _formatString.indexOf( "ZZZ" );
    152         if( zIndex >= 0 )
    153         {
    154             String ss1 = _formatString.substring( 0, zIndex );
    155             String ss2 = _formatString.substring( zIndex+3 );
    156             int tzOffset = tz.getRawOffset();
    157
    158             StringBuffer sb = new StringBuffer(_formatString.length()+10);
    159             sb.append(ss1);
    160             sb.append("'");
    161             if( tzOffset >= 0 )
    162                 sb.append( '+' );
    163             else
    164             {
    165                 tzOffset = -tzOffset;
    166                 sb.append( '-' );
    167             }
    168
    169             int raw = tzOffset / (1000*60);		// Convert to seconds
    170             int hr = raw / 60;
    171             int min = raw % 60;
    172
    173             if( hr < 10 )
    174                 sb.append( '0' );
    175             sb.append( hr );
    176             if( min < 10 )
    177                 sb.append( '0' );
    178             sb.append( min );
    179             sb.append( '\'' );
    180
    181             sb.append(ss2);
    182             _tzFormatString=sb.toString();
    183         }
    184         else
    185             _tzFormatString=_formatString;
    186         setMinFormatString();
    187     }
    188
    189
    190     /* ------------------------------------------------------------ */
    191     private void setMinFormatString()
    192     {
    193         int i = _tzFormatString.indexOf("ss.SSS");
    194         int l = 6;
    195         if (i>=0)
    196             throw new IllegalStateException("ms not supported");
    197         i = _tzFormatString.indexOf("ss");
    198         l=2;
    199
    200         // Build a formatter that formats a second format string
    201         String ss1=_tzFormatString.substring(0,i);
    202         String ss2=_tzFormatString.substring(i+l);
    203         _minFormatString =ss1+"'ss'"+ss2;
    204     }
    205
    206     /* ------------------------------------------------------------ */
    207     /** Format a date according to our stored formatter.
    208      * @param inDate
    209      * @return Formatted date
    210      */
    211     public synchronized String format(Date inDate)
    212     {
    213         return format(inDate.getTime());
    214     }
    215
    216     /* ------------------------------------------------------------ */
    217     /** Format a date according to our stored formatter.
    218      * @param inDate
    219      * @return Formatted date
    220      */
    221     public synchronized String format(long inDate)
    222     {
    223         long seconds = inDate / 1000;
    224
    225         // Is it not suitable to cache?
    226         if (seconds<_lastSeconds ||
    227             _lastSeconds>0 && seconds>_lastSeconds+__hitWindow)
    228         {
    229             // It's a cache miss
    230             Date d = new Date(inDate);
    231             return _tzFormat.format(d);
    232
    233         }
    234
    235         // Check if we are in the same second
    236         // and don't care about millis
    237         if (_lastSeconds==seconds )
    238             return _lastResult;
    239
    240         Date d = new Date(inDate);
    241
    242         // Check if we need a new format string
    243         long minutes = seconds/60;
    244         if (_lastMinutes != minutes)
    245         {
    246             _lastMinutes = minutes;
    247             _secFormatString=_minFormat.format(d);
    248
    249             int i=_secFormatString.indexOf("ss");
    250             int l=2;
    251             _secFormatString0=_secFormatString.substring(0,i);
    252             _secFormatString1=_secFormatString.substring(i+l);
    253         }
    254
    255         // Always format if we get here
    256         _lastSeconds = seconds;
    257         StringBuffer sb=new StringBuffer(_secFormatString.length());
    258         synchronized(sb)
    259         {
    260             sb.append(_secFormatString0);
    261             int s=(int)(seconds%60);
    262             if (s<10)
    263                 sb.append('0');
    264             sb.append(s);
    265             sb.append(_secFormatString1);
    266             _lastResult=sb.toString();
    267         }
    268
    269         return _lastResult;
    270     }
    271
    272     /* ------------------------------------------------------------ */
    273     /** Format to string buffer.
    274      * @param inDate Date the format
    275      * @param buffer StringBuffer
    276      */
    277     public void format(long inDate, StringBuffer buffer)
    278     {
    279         buffer.append(format(inDate));
    280     }
    281
    282     /* ------------------------------------------------------------ */
    283     /** Get the format.
    284      */
    285     public SimpleDateFormat getFormat()
    286     {
    287         return _minFormat;
    288     }
    289
    290     /* ------------------------------------------------------------ */
    291     public String getFormatString()
    292     {
    293         return _formatString;
    294     }
    295
    296     /* ------------------------------------------------------------ */
    297     public String now()
    298     {
    299         long now=System.currentTimeMillis();
    300         _lastMs=(int)(now%1000);
    301         return format(now);
    302     }
    303
    304     /* ------------------------------------------------------------ */
    305     public int lastMs()
    306     {
    307         return _lastMs;
    308     }
    309 }