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 }