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