View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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.SimpleDateFormat;
22  import java.util.Date;
23  import java.util.Locale;
24  import java.util.TimeZone;
25  
26  /**  Date Format Cache.
27   * Computes String representations of Dates and caches
28   * the results so that subsequent requests within the same second
29   * will be fast.
30   *
31   * Only format strings that contain either "ss".  Sub second formatting is 
32   * not handled.
33   *
34   * The timezone of the date may be included as an ID with the "zzz"
35   * format string or as an offset with the "ZZZ" format string.
36   *
37   * If consecutive calls are frequently very different, then this
38   * may be a little slower than a normal DateFormat.
39   */
40  public class DateCache
41  {
42      public static final String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
43      
44      private final String _formatString;
45      private final String _tzFormatString;
46      private final SimpleDateFormat _tzFormat;
47      private final Locale _locale ;
48      
49      private volatile Tick _tick;
50  
51      /* ------------------------------------------------------------ */
52      /* ------------------------------------------------------------ */
53      public static class Tick
54      {
55          final long _seconds;
56          final String _string;
57          public Tick(long seconds, String string)
58          {
59              _seconds = seconds;
60              _string = string;
61          }
62      }
63  
64      /* ------------------------------------------------------------ */
65      /** Constructor.
66       * Make a DateCache that will use a default format. The default format
67       * generates the same results as Date.toString().
68       */
69      public DateCache()
70      {
71          this(DEFAULT_FORMAT);
72      }
73      
74      /* ------------------------------------------------------------ */
75      /** Constructor.
76       * Make a DateCache that will use the given format
77       * @param format the format to use
78       */
79      public DateCache(String format)
80      {
81          this(format,null,TimeZone.getDefault());
82      }
83      
84      /* ------------------------------------------------------------ */
85      public DateCache(String format,Locale l)
86      {
87          this(format,l,TimeZone.getDefault());
88      }
89  
90      /* ------------------------------------------------------------ */
91      public DateCache(String format,Locale l,String tz)
92      {
93          this(format,l,TimeZone.getTimeZone(tz));
94      }
95      
96      /* ------------------------------------------------------------ */
97      public DateCache(String format,Locale l,TimeZone tz)
98      {
99          _formatString=format;
100         _locale = l;
101         
102 
103         int zIndex = _formatString.indexOf( "ZZZ" );
104         if( zIndex >= 0 )
105         {
106             String ss1 = _formatString.substring( 0, zIndex );
107             String ss2 = _formatString.substring( zIndex+3 );
108             int tzOffset = tz.getRawOffset();
109             
110             StringBuilder sb = new StringBuilder(_formatString.length()+10);
111             sb.append(ss1);
112             sb.append("'");
113             if( tzOffset >= 0 )
114                 sb.append( '+' );
115             else
116             {
117                 tzOffset = -tzOffset;
118                 sb.append( '-' );
119             }
120             
121             int raw = tzOffset / (1000*60);             // Convert to seconds
122             int hr = raw / 60;
123             int min = raw % 60;
124             
125             if( hr < 10 )
126                 sb.append( '0' );
127             sb.append( hr );
128             if( min < 10 )
129                 sb.append( '0' );
130             sb.append( min );
131             sb.append( '\'' );
132             
133             sb.append(ss2);
134             _tzFormatString=sb.toString();            
135         }
136         else
137             _tzFormatString=_formatString;
138    
139         if( _locale != null ) 
140         {
141             _tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
142         }
143         else 
144         {
145             _tzFormat=new SimpleDateFormat(_tzFormatString);
146         }
147         _tzFormat.setTimeZone(tz);
148         
149         _tick=null;
150     }
151     
152 
153     /* ------------------------------------------------------------ */
154     public TimeZone getTimeZone()
155     {
156         return _tzFormat.getTimeZone();
157     }
158 
159 
160     /* ------------------------------------------------------------ */
161     /** Format a date according to our stored formatter.
162      * @param inDate the Date
163      * @return Formatted date
164      */
165     public String format(Date inDate)
166     {
167         long seconds = inDate.getTime() / 1000;
168 
169         Tick tick=_tick;
170         
171         // Is this the cached time
172         if (tick==null || seconds!=tick._seconds)
173         {
174             // It's a cache miss
175             synchronized (this)
176             {
177                 return _tzFormat.format(inDate);
178             }
179         }
180         
181         return tick._string;
182     }
183     
184     /* ------------------------------------------------------------ */
185     /** Format a date according to our stored formatter.
186      * If it happens to be in the same second as the last formatNow
187      * call, then the format is reused.
188      * @param inDate the date in milliseconds since unix epoch 
189      * @return Formatted date
190      */
191     public String format(long inDate)
192     {
193         long seconds = inDate / 1000;
194 
195         Tick tick=_tick;
196         
197         // Is this the cached time
198         if (tick==null || seconds!=tick._seconds)
199         {
200             // It's a cache miss
201             Date d = new Date(inDate);
202             synchronized (this)
203             {
204                 return _tzFormat.format(d);
205             }
206         }
207         
208         return tick._string;
209     }
210     
211     /* ------------------------------------------------------------ */
212     /** Format a date according to our stored formatter.
213      * The passed time is expected to be close to the current time, so it is 
214      * compared to the last value passed and if it is within the same second,
215      * the format is reused.  Otherwise a new cached format is created.
216      * @param now the milliseconds since unix epoch 
217      * @return Formatted date
218      */
219     public String formatNow(long now)
220     {
221         long seconds = now / 1000;
222 
223         Tick tick=_tick;
224         
225         // Is this the cached time
226         if (tick!=null && tick._seconds==seconds)
227             return tick._string;
228         return formatTick(now)._string;
229     }
230     
231     /* ------------------------------------------------------------ */
232     public String now()
233     {
234         return formatNow(System.currentTimeMillis());
235     }
236     
237     /* ------------------------------------------------------------ */
238     public Tick tick()
239     {
240         return formatTick(System.currentTimeMillis());
241     }
242     
243     /* ------------------------------------------------------------ */
244     protected Tick formatTick(long now)
245     {
246         long seconds = now / 1000;
247 
248         // Synchronize to protect _tzFormat
249         synchronized (this)
250         {
251             // recheck the tick, to save multiple formats
252             if (_tick==null || _tick._seconds!=seconds)
253             {
254                 String s= _tzFormat.format(new Date(now));
255                 return _tick=new Tick(seconds,s);
256             }
257             return _tick;
258         }
259     }
260 
261     /* ------------------------------------------------------------ */
262     public String getFormatString()
263     {
264         return _formatString;
265     }    
266 }