1 package org.apache.commons.net.ntp;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.TimeZone;
20 import java.util.Date;
21 import java.util.Locale;
22 import java.lang.ref.SoftReference;
23 import java.text.SimpleDateFormat;
24 import java.text.DateFormat;
25
26 /***
27 * TimeStamp class represents the Network Time Protocol (NTP) timestamp
28 * as defined in RFC-1305 and SNTP (RFC-2030). It is represented as a
29 * 64-bit unsigned fixed-point number in seconds relative to 0-hour on 1-January-1900.
30 * The 32-bit low-order bits are the fractional seconds whose precision is
31 * about 200 picoseconds. Assumes overflow date when date passes MAX_LONG
32 * and reverts back to 0 is 2036 and not 1900. Test for most significant
33 * bit: if MSB=0 then 2036 basis is used otherwise 1900 if MSB=1.
34 * <p>
35 * Methods exist to convert NTP timestamps to and from the equivalent Java date
36 * representation, which is the number of milliseconds since the standard base
37 * time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
38 * </p>
39 *
40 * @author Jason Mathews, MITRE Corp
41 * @version $Revision: 165675 $ $Date: 2005-05-02 15:09:55 -0500 (Mon, 02 May 2005) $
42 * @see java.util.Date
43 */
44 public class TimeStamp implements java.io.Serializable, Comparable
45 {
46
47 /**
48 * baseline NTP time if bit-0=0 -> 7-Feb-2036 @ 06:28:16 UTC
49 */
50 protected static final long msb0baseTime = 2085978496000L;
51
52 /**
53 * baseline NTP time if bit-0=1 -> 1-Jan-1900 @ 01:00:00 UTC
54 */
55 protected static final long msb1baseTime = -2208988800000L;
56
57 /**
58 * Default NTP date string format. E.g. Fri, Sep 12 2003 21:06:23.860.
59 * See <code>java.text.SimpleDateFormat</code> for code descriptions.
60 */
61 public final static String NTP_DATE_FORMAT = "EEE, MMM dd yyyy HH:mm:ss.SSS";
62
63
64
65
66 private static SoftReference simpleFormatter = null;
67 private static SoftReference utcFormatter = null;
68
69 /**
70 * NTP timestamp value: 64-bit unsigned fixed-point number as defined in RFC-1305
71 * with high-order 32 bits the seconds field and the low-order 32-bits the
72 * fractional field.
73 */
74 private long ntpTime;
75
76 private static final long serialVersionUID = 8139806907588338737L;
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 /***
93 * Constructs a newly allocated NTP timestamp object
94 * that represents the native 64-bit long argument.
95 */
96 public TimeStamp(long ntpTime)
97 {
98 this.ntpTime = ntpTime;
99 }
100
101 /***
102 * Constructs a newly allocated NTP timestamp object
103 * that represents the value represented by the string
104 * in hexdecimal form (e.g. "c1a089bd.fc904f6d").
105 *
106 * @throws NumberFormatException - if the string does not contain a parsable timestamp.
107 */
108 public TimeStamp(String s) throws NumberFormatException
109 {
110 ntpTime = decodeNtpHexString(s);
111 }
112
113 /***
114 * Constructs a newly allocated NTP timestamp object
115 * that represents the Java Date argument.
116 *
117 * @param d - the Date to be represented by the Timestamp object.
118 */
119 public TimeStamp(Date d)
120 {
121 ntpTime = (d == null) ? 0 : toNtpTime(d.getTime());
122 }
123
124 /***
125 * Returns the value of this Timestamp as a long value.
126 *
127 * @return the 64-bit long value represented by this object.
128 */
129 public long ntpValue()
130 {
131 return ntpTime;
132 }
133
134 /***
135 * Returns high-order 32-bits representing the seconds of this NTP timestamp.
136 *
137 * @return seconds represented by this NTP timestamp.
138 */
139 public long getSeconds()
140 {
141 return (ntpTime >>> 32) & 0xffffffffL;
142 }
143
144 /***
145 * Returns low-order 32-bits representing the fractional seconds.
146 *
147 * @return fractional seconds represented by this NTP timestamp.
148 */
149 public long getFraction()
150 {
151 return ntpTime & 0xffffffffL;
152 }
153
154 /***
155 * Convert NTP timestamp to Java standard time.
156 *
157 * @return NTP Timestamp in Java time
158 */
159 public long getTime()
160 {
161 return getTime(ntpTime);
162 }
163
164 /***
165 * Convert NTP timestamp to Java Date object.
166 *
167 * @return NTP Timestamp in Java Date
168 */
169 public Date getDate()
170 {
171 long time = getTime(ntpTime);
172 return new Date(time);
173 }
174
175 /***
176 * Convert 64-bit NTP timestamp to Java standard time.
177 *
178 * Note that java time (milliseconds) by definition has less precision
179 * then NTP time (picoseconds) so converting NTP timestamp to java time and back
180 * to NTP timestamp loses precision. For example, Tue, Dec 17 2002 09:07:24.810 EST
181 * is represented by a single Java-based time value of f22cd1fc8a, but its
182 * NTP equivalent are all values ranging from c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c.
183 *
184 * @param ntpTimeValue
185 * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
186 * represented by this NTP timestamp value.
187 */
188 public static long getTime(long ntpTimeValue)
189 {
190 long seconds = (ntpTimeValue >>> 32) & 0xffffffffL;
191 long fraction = ntpTimeValue & 0xffffffffL;
192
193
194 fraction = Math.round(1000D * fraction / 0x100000000L);
195
196
197
198
199
200
201
202
203
204
205 long msb = seconds & 0x80000000L;
206 if (msb == 0) {
207
208 return msb0baseTime + (seconds * 1000) + fraction;
209 } else {
210
211 return msb1baseTime + (seconds * 1000) + fraction;
212 }
213 }
214
215 /***
216 * Helper method to convert Java time to NTP timestamp object.
217 * Note that Java time (milliseconds) by definition has less precision
218 * then NTP time (picoseconds) so converting Ntptime to Javatime and back
219 * to Ntptime loses precision. For example, Tue, Dec 17 2002 09:07:24.810
220 * is represented by a single Java-based time value of f22cd1fc8a, but its
221 * NTP equivalent are all values from c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c.
222 * @param date the milliseconds since January 1, 1970, 00:00:00 GMT.
223 * @return NTP timestamp object at the specified date.
224 */
225 public static TimeStamp getNtpTime(long date)
226 {
227 return new TimeStamp(toNtpTime(date));
228 }
229
230 /***
231 * Constructs a NTP timestamp object and initializes it so that
232 * it represents the time at which it was allocated, measured to the
233 * nearest millisecond.
234 * @return NTP timestamp object set to the current time.
235 * @see java.lang.System#currentTimeMillis()
236 */
237 public static TimeStamp getCurrentTime()
238 {
239 return getNtpTime(System.currentTimeMillis());
240 }
241
242 /***
243 * Convert NTP timestamp hexstring (e.g. "c1a089bd.fc904f6d") to the NTP
244 * 64-bit unsigned fixed-point number.
245 *
246 * @return NTP 64-bit timestamp value.
247 * @throws NumberFormatException - if the string does not contain a parsable timestamp.
248 */
249 protected static long decodeNtpHexString(String s)
250 throws NumberFormatException
251 {
252 if (s == null) {
253 throw new NumberFormatException("null");
254 }
255 int ind = s.indexOf('.');
256 if (ind == -1) {
257 if (s.length() == 0) return 0;
258 return Long.parseLong(s, 16) << 32;
259 }
260
261 return Long.parseLong(s.substring(0, ind), 16) << 32 |
262 Long.parseLong(s.substring(ind + 1), 16);
263 }
264
265 /***
266 * Parses the string argument as a NTP hexidecimal timestamp representation string
267 * (e.g. "c1a089bd.fc904f6d").
268 *
269 * @param s - hexstring.
270 * @return the Timestamp represented by the argument in hexidecimal.
271 * @throws NumberFormatException - if the string does not contain a parsable timestamp.
272 */
273 public static TimeStamp parseNtpString(String s)
274 throws NumberFormatException
275 {
276 return new TimeStamp(decodeNtpHexString(s));
277 }
278
279 /***
280 * Converts Java time to 64-bit NTP time representation.
281 *
282 * @param t Java time
283 * @return NTP timestamp representation of Java time value.
284 */
285 protected static long toNtpTime(long t)
286 {
287 boolean useBase1 = t < msb0baseTime;
288 long baseTime;
289 if (useBase1) {
290 baseTime = t - msb1baseTime;
291 } else {
292
293 baseTime = t - msb0baseTime;
294 }
295
296 long seconds = baseTime / 1000;
297 long fraction = ((baseTime % 1000) * 0x100000000L) / 1000;
298
299 if (useBase1) {
300 seconds |= 0x80000000L;
301 }
302
303 long time = seconds << 32 | fraction;
304 return time;
305 }
306
307 /***
308 * Computes a hashcode for this Timestamp. The result is the exclusive
309 * OR of the two halves of the primitive <code>long</code> value
310 * represented by this <code>TimeStamp</code> object. That is, the hashcode
311 * is the value of the expression:
312 * <blockquote><pre>
313 * (int)(this.ntpValue()^(this.ntpValue() >>> 32))
314 * </pre></blockquote>
315 *
316 * @return a hash code value for this object.
317 */
318 public int hashCode()
319 {
320 return (int) (ntpTime ^ (ntpTime >>> 32));
321 }
322
323 /***
324 * Compares this object against the specified object.
325 * The result is <code>true</code> if and only if the argument is
326 * not <code>null</code> and is a <code>Long</code> object that
327 * contains the same <code>long</code> value as this object.
328 *
329 * @param obj the object to compare with.
330 * @return <code>true</code> if the objects are the same;
331 * <code>false</code> otherwise.
332 */
333 public boolean equals(Object obj)
334 {
335 if (obj instanceof TimeStamp) {
336 return ntpTime == ((TimeStamp) obj).ntpValue();
337 }
338 return false;
339 }
340
341 /***
342 * Converts this <code>TimeStamp</code> object to a <code>String</code>.
343 * The NTP timestamp 64-bit long value is represented as hex string with
344 * seconds separated by fractional seconds by a decimal point;
345 * e.g. c1a089bd.fc904f6d <=> Tue, Dec 10 2002 10:41:49.986
346 *
347 * @return NTP timestamp 64-bit long value as hex string with seconds
348 * separated by fractional seconds.
349 */
350 public String toString()
351 {
352 return toString(ntpTime);
353 }
354
355 /***
356 * Left-pad 8-character hex string with 0's
357 *
358 * @param buf - StringBuffer which is appended with leading 0's.
359 * @param l - a long.
360 */
361 private static void appendHexString(StringBuffer buf, long l)
362 {
363 String s = Long.toHexString(l);
364 for (int i = s.length(); i < 8; i++)
365 buf.append('0');
366 buf.append(s);
367 }
368
369 /***
370 * Converts 64-bit NTP timestamp value to a <code>String</code>.
371 * The NTP timestamp value is represented as hex string with
372 * seconds separated by fractional seconds by a decimal point;
373 * e.g. c1a089bd.fc904f6d <=> Tue, Dec 10 2002 10:41:49.986
374 *
375 * @return NTP timestamp 64-bit long value as hex string with seconds
376 * separated by fractional seconds.
377 */
378 public static String toString(long ntpTime)
379 {
380 StringBuffer buf = new StringBuffer();
381
382 appendHexString(buf, (ntpTime >>> 32) & 0xffffffffL);
383
384
385 buf.append('.');
386 appendHexString(buf, ntpTime & 0xffffffffL);
387
388 return buf.toString();
389 }
390
391 /***
392 * Converts this <code>TimeStamp</code> object to a <code>String</code>
393 * of the form:
394 * <blockquote><pre>
395 * EEE, MMM dd yyyy HH:mm:ss.SSS</pre></blockquote>
396 * See java.text.SimpleDataFormat for code descriptions.
397 *
398 * @return a string representation of this date.
399 */
400 public String toDateString()
401 {
402 DateFormat formatter = null;
403 if (simpleFormatter != null) {
404 formatter = (DateFormat) simpleFormatter.get();
405 }
406 if (formatter == null) {
407
408 formatter = new SimpleDateFormat(NTP_DATE_FORMAT, Locale.US);
409 formatter.setTimeZone(TimeZone.getDefault());
410 simpleFormatter = new SoftReference(formatter);
411 }
412 Date ntpDate = getDate();
413 synchronized (formatter) {
414 return formatter.format(ntpDate);
415 }
416 }
417
418 /***
419 * Converts this <code>TimeStamp</code> object to a <code>String</code>
420 * of the form:
421 * <blockquote><pre>
422 * EEE, MMM dd yyyy HH:mm:ss.SSS UTC</pre></blockquote>
423 * See java.text.SimpleDataFormat for code descriptions.
424 *
425 * @return a string representation of this date in UTC.
426 */
427 public String toUTCString()
428 {
429 DateFormat formatter = null;
430 if (utcFormatter != null)
431 formatter = (DateFormat) utcFormatter.get();
432 if (formatter == null) {
433
434 formatter = new SimpleDateFormat(NTP_DATE_FORMAT + " 'UTC'",
435 Locale.US);
436 formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
437 utcFormatter = new SoftReference(formatter);
438 }
439 Date ntpDate = getDate();
440 synchronized (formatter) {
441 return formatter.format(ntpDate);
442 }
443 }
444
445 /***
446 * Compares two Timestamps numerically.
447 *
448 * @param anotherTimeStamp - the <code>TimeStamp</code> to be compared.
449 * @return the value <code>0</code> if the argument TimeStamp is equal to
450 * this TimeStamp; a value less than <code>0</code> if this TimeStamp
451 * is numerically less than the TimeStamp argument; and a
452 * value greater than <code>0</code> if this TimeStamp is
453 * numerically greater than the TimeStamp argument
454 * (signed comparison).
455 */
456 public int compareTo(TimeStamp anotherTimeStamp)
457 {
458 long thisVal = this.ntpTime;
459 long anotherVal = anotherTimeStamp.ntpTime;
460 return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
461 }
462
463 /***
464 * Compares this TimeStamp to another Object. If the Object is a TimeStamp,
465 * this function behaves like <code>compareTo(TimeStamp)</code>. Otherwise,
466 * it throws a <code>ClassCastException</code> (as TimeStamps are comparable
467 * only to other TimeStamps).
468 *
469 * @param o the <code>Object</code> to be compared.
470 * @return the value <code>0</code> if the argument is a TimeStamp
471 * numerically equal to this TimeStamp; a value less than
472 * <code>0</code> if the argument is a TimeStamp numerically
473 * greater than this TimeStamp; and a value greater than
474 * <code>0</code> if the argument is a TimeStamp numerically
475 * less than this TimeStamp.
476 * @exception ClassCastException if the argument is not a
477 * <code>TimeStamp</code>.
478 * @see java.lang.Comparable
479 */
480 public int compareTo(Object o)
481 {
482 return compareTo((TimeStamp) o);
483 }
484
485 }