1 // ========================================================================
2 // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 // ========================================================================
14
15 package org.mortbay.util;
16
17 import java.text.DateFormatSymbols;
18 import java.text.SimpleDateFormat;
19 import java.util.Date;
20 import java.util.Locale;
21 import java.util.TimeZone;
22
23 /* ------------------------------------------------------------ */
24 /** Date Format Cache.
25 * Computes String representations of Dates and caches
26 * the results so that subsequent requests within the same minute
27 * will be fast.
28 *
29 * Only format strings that contain either "ss" or "ss.SSS" are
30 * handled.
31 *
32 * The timezone of the date may be included as an ID with the "zzz"
33 * format string or as an offset with the "ZZZ" format string.
34 *
35 * If consecutive calls are frequently very different, then this
36 * may be a little slower than a normal DateFormat.
37 *
38 * @author Kent Johnson <KJohnson@transparent.com>
39 * @author Greg Wilkins (gregw)
40 */
41
42 public class DateCache
43 {
44 public static String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
45 private static long __hitWindow=60*60;
46
47 private String _formatString;
48 private String _tzFormatString;
49 private SimpleDateFormat _tzFormat;
50
51 private String _minFormatString;
52 private SimpleDateFormat _minFormat;
53
54 private String _secFormatString;
55 private String _secFormatString0;
56 private String _secFormatString1;
57
58 private long _lastMinutes = -1;
59 private long _lastSeconds = -1;
60 private int _lastMs = -1;
61 private String _lastResult = null;
62
63 private Locale _locale = null;
64 private DateFormatSymbols _dfs = null;
65
66 /* ------------------------------------------------------------ */
67 /** Constructor.
68 * Make a DateCache that will use a default format. The default format
69 * generates the same results as Date.toString().
70 */
71 public DateCache()
72 {
73 this(DEFAULT_FORMAT);
74 getFormat().setTimeZone(TimeZone.getDefault());
75 }
76
77 /* ------------------------------------------------------------ */
78 /** Constructor.
79 * Make a DateCache that will use the given format
80 */
81 public DateCache(String format)
82 {
83 _formatString=format;
84 setTimeZone(TimeZone.getDefault());
85
86 }
87
88 /* ------------------------------------------------------------ */
89 public DateCache(String format,Locale l)
90 {
91 _formatString=format;
92 _locale = l;
93 setTimeZone(TimeZone.getDefault());
94 }
95
96 /* ------------------------------------------------------------ */
97 public DateCache(String format,DateFormatSymbols s)
98 {
99 _formatString=format;
100 _dfs = s;
101 setTimeZone(TimeZone.getDefault());
102 }
103
104 /* ------------------------------------------------------------ */
105 /** Set the timezone.
106 * @param tz TimeZone
107 */
108 public void setTimeZone(TimeZone tz)
109 {
110 setTzFormatString(tz);
111 if( _locale != null )
112 {
113 _tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
114 _minFormat=new SimpleDateFormat(_minFormatString,_locale);
115 }
116 else if( _dfs != null )
117 {
118 _tzFormat=new SimpleDateFormat(_tzFormatString,_dfs);
119 _minFormat=new SimpleDateFormat(_minFormatString,_dfs);
120 }
121 else
122 {
123 _tzFormat=new SimpleDateFormat(_tzFormatString);
124 _minFormat=new SimpleDateFormat(_minFormatString);
125 }
126 _tzFormat.setTimeZone(tz);
127 _minFormat.setTimeZone(tz);
128 _lastSeconds=-1;
129 _lastMinutes=-1;
130 }
131
132 /* ------------------------------------------------------------ */
133 public TimeZone getTimeZone()
134 {
135 return _tzFormat.getTimeZone();
136 }
137
138 /* ------------------------------------------------------------ */
139 /** Set the timezone.
140 * @param timeZoneId TimeZoneId the ID of the zone as used by
141 * TimeZone.getTimeZone(id)
142 */
143 public void setTimeZoneID(String timeZoneId)
144 {
145 setTimeZone(TimeZone.getTimeZone(timeZoneId));
146 }
147
148 /* ------------------------------------------------------------ */
149 private void setTzFormatString(final TimeZone tz )
150 {
151 int zIndex = _formatString.indexOf( "ZZZ" );
152 if( zIndex >= 0 )
153 {
154 String ss1 = _formatString.substring( 0, zIndex );
155 String ss2 = _formatString.substring( zIndex+3 );
156 int tzOffset = tz.getRawOffset();
157
158 StringBuffer sb = new StringBuffer(_formatString.length()+10);
159 sb.append(ss1);
160 sb.append("'");
161 if( tzOffset >= 0 )
162 sb.append( '+' );
163 else
164 {
165 tzOffset = -tzOffset;
166 sb.append( '-' );
167 }
168
169 int raw = tzOffset / (1000*60); // Convert to seconds
170 int hr = raw / 60;
171 int min = raw % 60;
172
173 if( hr < 10 )
174 sb.append( '0' );
175 sb.append( hr );
176 if( min < 10 )
177 sb.append( '0' );
178 sb.append( min );
179 sb.append( '\'' );
180
181 sb.append(ss2);
182 _tzFormatString=sb.toString();
183 }
184 else
185 _tzFormatString=_formatString;
186 setMinFormatString();
187 }
188
189
190 /* ------------------------------------------------------------ */
191 private void setMinFormatString()
192 {
193 int i = _tzFormatString.indexOf("ss.SSS");
194 int l = 6;
195 if (i>=0)
196 throw new IllegalStateException("ms not supported");
197 i = _tzFormatString.indexOf("ss");
198 l=2;
199
200 // Build a formatter that formats a second format string
201 String ss1=_tzFormatString.substring(0,i);
202 String ss2=_tzFormatString.substring(i+l);
203 _minFormatString =ss1+"'ss'"+ss2;
204 }
205
206 /* ------------------------------------------------------------ */
207 /** Format a date according to our stored formatter.
208 * @param inDate
209 * @return Formatted date
210 */
211 public synchronized String format(Date inDate)
212 {
213 return format(inDate.getTime());
214 }
215
216 /* ------------------------------------------------------------ */
217 /** Format a date according to our stored formatter.
218 * @param inDate
219 * @return Formatted date
220 */
221 public synchronized String format(long inDate)
222 {
223 long seconds = inDate / 1000;
224
225 // Is it not suitable to cache?
226 if (seconds<_lastSeconds ||
227 _lastSeconds>0 && seconds>_lastSeconds+__hitWindow)
228 {
229 // It's a cache miss
230 Date d = new Date(inDate);
231 return _tzFormat.format(d);
232
233 }
234
235 // Check if we are in the same second
236 // and don't care about millis
237 if (_lastSeconds==seconds )
238 return _lastResult;
239
240 Date d = new Date(inDate);
241
242 // Check if we need a new format string
243 long minutes = seconds/60;
244 if (_lastMinutes != minutes)
245 {
246 _lastMinutes = minutes;
247 _secFormatString=_minFormat.format(d);
248
249 int i=_secFormatString.indexOf("ss");
250 int l=2;
251 _secFormatString0=_secFormatString.substring(0,i);
252 _secFormatString1=_secFormatString.substring(i+l);
253 }
254
255 // Always format if we get here
256 _lastSeconds = seconds;
257 StringBuffer sb=new StringBuffer(_secFormatString.length());
258 synchronized(sb)
259 {
260 sb.append(_secFormatString0);
261 int s=(int)(seconds%60);
262 if (s<10)
263 sb.append('0');
264 sb.append(s);
265 sb.append(_secFormatString1);
266 _lastResult=sb.toString();
267 }
268
269 return _lastResult;
270 }
271
272 /* ------------------------------------------------------------ */
273 /** Format to string buffer.
274 * @param inDate Date the format
275 * @param buffer StringBuffer
276 */
277 public void format(long inDate, StringBuffer buffer)
278 {
279 buffer.append(format(inDate));
280 }
281
282 /* ------------------------------------------------------------ */
283 /** Get the format.
284 */
285 public SimpleDateFormat getFormat()
286 {
287 return _minFormat;
288 }
289
290 /* ------------------------------------------------------------ */
291 public String getFormatString()
292 {
293 return _formatString;
294 }
295
296 /* ------------------------------------------------------------ */
297 public String now()
298 {
299 long now=System.currentTimeMillis();
300 int n=0xfff&(int)now;
301 _lastMs=n%1000;
302 return format(now);
303 }
304
305 /* ------------------------------------------------------------ */
306 public int lastMs()
307 {
308 return _lastMs;
309 }
310 }