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 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 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 }