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      private static long __MaxMisses=10;
47      
48      private String _formatString;
49      private String _tzFormatString;
50      private SimpleDateFormat _tzFormat;
51      
52      private String _minFormatString;
53      private SimpleDateFormat _minFormat;
54  
55      private String _secFormatString;
56      private String _secFormatString0;
57      private String _secFormatString1;
58  
59      private long _lastMinutes = -1;
60      private long _lastSeconds = -1;
61      private int _lastMs = -1;
62      private String _lastResult = null;
63  
64      private Locale _locale	= null;
65      private DateFormatSymbols	_dfs	= null;
66  
67      /* ------------------------------------------------------------ */
68      /** Constructor.
69       * Make a DateCache that will use a default format. The default format
70       * generates the same results as Date.toString().
71       */
72      public DateCache()
73      {
74          this(DEFAULT_FORMAT);
75          getFormat().setTimeZone(TimeZone.getDefault());
76      }
77      
78      /* ------------------------------------------------------------ */
79      /** Constructor.
80       * Make a DateCache that will use the given format
81       */
82      public DateCache(String format)
83      {
84          _formatString=format;
85          setTimeZone(TimeZone.getDefault());
86          
87      }
88      
89      /* ------------------------------------------------------------ */
90      public DateCache(String format,Locale l)
91      {
92          _formatString=format;
93          _locale = l;
94          setTimeZone(TimeZone.getDefault());       
95      }
96      
97      /* ------------------------------------------------------------ */
98      public DateCache(String format,DateFormatSymbols s)
99      {
100         _formatString=format;
101         _dfs = s;
102         setTimeZone(TimeZone.getDefault());
103     }
104 
105     /* ------------------------------------------------------------ */
106     /** Set the timezone.
107      * @param tz TimeZone
108      */
109     public void setTimeZone(TimeZone tz)
110     {
111         setTzFormatString(tz);        
112         if( _locale != null ) 
113         {
114             _tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
115             _minFormat=new SimpleDateFormat(_minFormatString,_locale);
116         }
117         else if( _dfs != null ) 
118         {
119             _tzFormat=new SimpleDateFormat(_tzFormatString,_dfs);
120             _minFormat=new SimpleDateFormat(_minFormatString,_dfs);
121         }
122         else 
123         {
124             _tzFormat=new SimpleDateFormat(_tzFormatString);
125             _minFormat=new SimpleDateFormat(_minFormatString);
126         }
127         _tzFormat.setTimeZone(tz);
128         _minFormat.setTimeZone(tz);
129         _lastSeconds=-1;
130         _lastMinutes=-1;        
131     }
132 
133     /* ------------------------------------------------------------ */
134     public TimeZone getTimeZone()
135     {
136         return _tzFormat.getTimeZone();
137     }
138     
139     /* ------------------------------------------------------------ */
140     /** Set the timezone.
141      * @param timeZoneId TimeZoneId the ID of the zone as used by
142      * TimeZone.getTimeZone(id)
143      */
144     public void setTimeZoneID(String timeZoneId)
145     {
146         setTimeZone(TimeZone.getTimeZone(timeZoneId));
147     }
148     
149     /* ------------------------------------------------------------ */
150     private void setTzFormatString(final  TimeZone tz )
151     {
152         int zIndex = _formatString.indexOf( "ZZZ" );
153         if( zIndex >= 0 )
154         {
155             String ss1 = _formatString.substring( 0, zIndex );
156             String ss2 = _formatString.substring( zIndex+3 );
157             int tzOffset = tz.getRawOffset();
158             
159             StringBuffer sb = new StringBuffer(_formatString.length()+10);
160             sb.append(ss1);
161             sb.append("'");
162             if( tzOffset >= 0 )
163                 sb.append( '+' );
164             else
165             {
166                 tzOffset = -tzOffset;
167                 sb.append( '-' );
168             }
169             
170             int raw = tzOffset / (1000*60);		// Convert to seconds
171             int hr = raw / 60;
172             int min = raw % 60;
173             
174             if( hr < 10 )
175                 sb.append( '0' );
176             sb.append( hr );
177             if( min < 10 )
178                 sb.append( '0' );
179             sb.append( min );
180             sb.append( '\'' );
181             
182             sb.append(ss2);
183             _tzFormatString=sb.toString();            
184         }
185         else
186             _tzFormatString=_formatString;
187         setMinFormatString();
188     }
189 
190     
191     /* ------------------------------------------------------------ */
192     private void setMinFormatString()
193     {
194         int i = _tzFormatString.indexOf("ss.SSS");
195         int l = 6;
196         if (i>=0)
197             throw new IllegalStateException("ms not supported");
198         i = _tzFormatString.indexOf("ss");
199         l=2;
200         
201         // Build a formatter that formats a second format string
202         String ss1=_tzFormatString.substring(0,i);
203         String ss2=_tzFormatString.substring(i+l);
204         _minFormatString =ss1+"'ss'"+ss2;
205     }
206 
207     /* ------------------------------------------------------------ */
208     /** Format a date according to our stored formatter.
209      * @param inDate 
210      * @return Formatted date
211      */
212     public synchronized String format(Date inDate)
213     {
214         return format(inDate.getTime());
215     }
216     
217     /* ------------------------------------------------------------ */
218     /** Format a date according to our stored formatter.
219      * @param inDate 
220      * @return Formatted date
221      */
222     public synchronized String format(long inDate)
223     {
224         long seconds = inDate / 1000;
225 
226         // Is it not suitable to cache?
227         if (seconds<_lastSeconds ||
228             _lastSeconds>0 && seconds>_lastSeconds+__hitWindow)
229         {
230             // It's a cache miss
231             Date d = new Date(inDate);
232             return _tzFormat.format(d);
233             
234         }
235                                           
236         // Check if we are in the same second
237         // and don't care about millis
238         if (_lastSeconds==seconds )
239             return _lastResult;
240 
241         Date d = new Date(inDate);
242         
243         // Check if we need a new format string
244         long minutes = seconds/60;
245         if (_lastMinutes != minutes)
246         {
247             _lastMinutes = minutes;
248             _secFormatString=_minFormat.format(d);
249 
250             int i=_secFormatString.indexOf("ss");
251             int l=2;
252             _secFormatString0=_secFormatString.substring(0,i);
253             _secFormatString1=_secFormatString.substring(i+l);
254         }
255 
256         // Always format if we get here
257         _lastSeconds = seconds;
258         StringBuffer sb=new StringBuffer(_secFormatString.length());
259         synchronized(sb)
260         {
261             sb.append(_secFormatString0);
262             int s=(int)(seconds%60);
263             if (s<10)
264                 sb.append('0');
265             sb.append(s);
266             sb.append(_secFormatString1);
267             _lastResult=sb.toString();
268         }
269                 
270         return _lastResult;
271     }
272 
273     /* ------------------------------------------------------------ */
274     /** Format to string buffer. 
275      * @param inDate Date the format
276      * @param buffer StringBuffer
277      */
278     public void format(long inDate, StringBuffer buffer)
279     {
280         buffer.append(format(inDate));
281     }
282     
283     /* ------------------------------------------------------------ */
284     /** Get the format.
285      */
286     public SimpleDateFormat getFormat()
287     {
288         return _minFormat;
289     }
290 
291     /* ------------------------------------------------------------ */
292     public String getFormatString()
293     {
294         return _formatString;
295     }    
296 
297     /* ------------------------------------------------------------ */
298     public String now()
299     {
300         long now=System.currentTimeMillis();
301         int n=0xfff&(int)now;
302         _lastMs=n%1000;
303         return format(now);
304     }
305 
306     /* ------------------------------------------------------------ */
307     public int lastMs()
308     {
309         return _lastMs;
310     }
311 }