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 }