View Javadoc

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