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.nio.ByteBuffer;
22  import java.text.DateFormatSymbols;
23  import java.text.SimpleDateFormat;
24  import java.util.Date;
25  import java.util.Locale;
26  import java.util.TimeZone;
27  import java.util.Timer;
28  import java.util.TimerTask;
29  
30  /* ------------------------------------------------------------ */
31  /**  Date Format Cache.
32   * Computes String representations of Dates and caches
33   * the results so that subsequent requests within the same minute
34   * will be fast.
35   *
36   * Only format strings that contain either "ss" or "ss.SSS" are
37   * handled.
38   *
39   * The timezone of the date may be included as an ID with the "zzz"
40   * format string or as an offset with the "ZZZ" format string.
41   *
42   * If consecutive calls are frequently very different, then this
43   * may be a little slower than a normal DateFormat.
44   *
45   */
46  
47  public class DateCache
48  {
49      public static final String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
50      
51      private final String _formatString;
52      private String _tzFormatString;
53      private SimpleDateFormat _tzFormat;
54      
55      private volatile Tick _tick;
56  
57      private Locale _locale	= null;
58      private DateFormatSymbols	_dfs	= null;
59      
60      private static Timer __timer;
61      
62  
63      public static Timer getTimer()
64      {
65          synchronized (DateCache.class)
66          {
67              if (__timer==null)
68                  __timer=new Timer("DateCache",true);
69              return __timer;
70          }
71      }
72      
73      /* ------------------------------------------------------------ */
74      /* ------------------------------------------------------------ */
75      private static class Tick
76      {
77          final long _seconds;
78          final String _string;
79          public Tick(long seconds, String string)
80          {
81              _seconds = seconds;
82              _string = string;
83          }
84      }
85  
86      /* ------------------------------------------------------------ */
87      /** Constructor.
88       * Make a DateCache that will use a default format. The default format
89       * generates the same results as Date.toString().
90       */
91      public DateCache()
92      {
93          this(DEFAULT_FORMAT);
94      }
95      
96      /* ------------------------------------------------------------ */
97      /** Constructor.
98       * Make a DateCache that will use the given format
99       */
100     public DateCache(String format)
101     {
102         _formatString=format;
103         setTimeZone(TimeZone.getDefault());
104         
105         synchronized (DateCache.class)
106         {
107             long now=System.currentTimeMillis();
108             long tick=1000*((now/1000)+1)-now;
109             formatNow();
110             getTimer().scheduleAtFixedRate(new TimerTask()
111             {
112                 @Override
113                 public void run()
114                 {
115                     formatNow();
116                 }
117             },
118             tick,
119             1000);
120         }
121     }
122     
123     /* ------------------------------------------------------------ */
124     public DateCache(String format,Locale l)
125     {
126         this(format);
127         _locale = l;
128         setTimeZone(TimeZone.getDefault());       
129     }
130     
131     /* ------------------------------------------------------------ */
132     public DateCache(String format,DateFormatSymbols s)
133     {
134         this(format);
135         _dfs = s;
136         setTimeZone(TimeZone.getDefault());
137     }
138 
139     /* ------------------------------------------------------------ */
140     /** Set the timezone.
141      * @param tz TimeZone
142      */
143     public void setTimeZone(TimeZone tz)
144     {
145         setTzFormatString(tz);        
146         if( _locale != null ) 
147         {
148             _tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
149         }
150         else if( _dfs != null ) 
151         {
152             _tzFormat=new SimpleDateFormat(_tzFormatString,_dfs);
153         }
154         else 
155         {
156             _tzFormat=new SimpleDateFormat(_tzFormatString);
157         }
158         _tzFormat.setTimeZone(tz);
159         _tick=null;
160     }
161 
162     /* ------------------------------------------------------------ */
163     public TimeZone getTimeZone()
164     {
165         return _tzFormat.getTimeZone();
166     }
167     
168     /* ------------------------------------------------------------ */
169     /** Set the timezone.
170      * @param timeZoneId TimeZoneId the ID of the zone as used by
171      * TimeZone.getTimeZone(id)
172      */
173     public void setTimeZoneID(String timeZoneId)
174     {
175         setTimeZone(TimeZone.getTimeZone(timeZoneId));
176     }
177     
178     /* ------------------------------------------------------------ */
179     private void setTzFormatString(final  TimeZone tz )
180     {
181         int zIndex = _formatString.indexOf( "ZZZ" );
182         if( zIndex >= 0 )
183         {
184             String ss1 = _formatString.substring( 0, zIndex );
185             String ss2 = _formatString.substring( zIndex+3 );
186             int tzOffset = tz.getRawOffset();
187             
188             StringBuilder sb = new StringBuilder(_formatString.length()+10);
189             sb.append(ss1);
190             sb.append("'");
191             if( tzOffset >= 0 )
192                 sb.append( '+' );
193             else
194             {
195                 tzOffset = -tzOffset;
196                 sb.append( '-' );
197             }
198             
199             int raw = tzOffset / (1000*60);		// Convert to seconds
200             int hr = raw / 60;
201             int min = raw % 60;
202             
203             if( hr < 10 )
204                 sb.append( '0' );
205             sb.append( hr );
206             if( min < 10 )
207                 sb.append( '0' );
208             sb.append( min );
209             sb.append( '\'' );
210             
211             sb.append(ss2);
212             _tzFormatString=sb.toString();            
213         }
214         else
215             _tzFormatString=_formatString;
216         _tick=null;
217     }
218 
219 
220     /* ------------------------------------------------------------ */
221     /** Format a date according to our stored formatter.
222      * @param inDate 
223      * @return Formatted date
224      */
225     public String format(Date inDate)
226     {
227         long seconds = inDate.getTime() / 1000;
228 
229         Tick tick=_tick;
230         
231         // Is this the cached time
232         if (tick==null || seconds!=tick._seconds)
233         {
234             // It's a cache miss
235             synchronized (this)
236             {
237                 return _tzFormat.format(inDate);
238             }
239         }
240         
241         return tick._string;
242     }
243     
244     /* ------------------------------------------------------------ */
245     /** Format a date according to our stored formatter.
246      * @param inDate 
247      * @return Formatted date
248      */
249     public String format(long inDate)
250     {
251         long seconds = inDate / 1000;
252 
253         Tick tick=_tick;
254         
255         // Is this the cached time
256         if (tick==null || seconds!=tick._seconds)
257         {
258             // It's a cache miss
259             Date d = new Date(inDate);
260             synchronized (this)
261             {
262                 return _tzFormat.format(d);
263             }
264         }
265         
266         return tick._string;
267     }
268     
269     /* ------------------------------------------------------------ */
270     public String now()
271     {
272         return _tick._string;
273     }
274     
275     /* ------------------------------------------------------------ */
276     protected void formatNow()
277     {
278         long now = System.currentTimeMillis();
279         long seconds = now / 1000;
280 
281         synchronized (this)
282         {
283             String s= _tzFormat.format(new Date(now));
284             _tick=new Tick(seconds,s);
285         }
286     }
287 
288     /* ------------------------------------------------------------ */
289     /** Format to string buffer. 
290      * @param inDate Date the format
291      * @param buffer StringBuilder
292      */
293     public void format(long inDate, StringBuilder buffer)
294     {
295         buffer.append(format(inDate));
296     }
297 
298     /* ------------------------------------------------------------ */
299     public String getFormatString()
300     {
301         return _formatString;
302     }    
303 
304     /* ------------------------------------------------------------ */
305     private volatile ByteBuffer _buffer;
306     private volatile Object _last;
307     public synchronized ByteBuffer formatBuffer(long date)
308     {
309         String d = format(date);
310         if (d==_last)
311             return _buffer;
312         _last=d;
313         _buffer=BufferUtil.toBuffer(d);
314         
315         return _buffer;
316     }
317 }