View Javadoc

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