001 package org.apache.commons.net.ntp;
002 /*
003 * Copyright 2001-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018
019 import java.util.TimeZone;
020 import java.util.Date;
021 import java.util.Locale;
022 import java.lang.ref.SoftReference;
023 import java.text.SimpleDateFormat;
024 import java.text.DateFormat;
025
026 /***
027 * TimeStamp class represents the Network Time Protocol (NTP) timestamp
028 * as defined in RFC-1305 and SNTP (RFC-2030). It is represented as a
029 * 64-bit unsigned fixed-point number in seconds relative to 0-hour on 1-January-1900.
030 * The 32-bit low-order bits are the fractional seconds whose precision is
031 * about 200 picoseconds. Assumes overflow date when date passes MAX_LONG
032 * and reverts back to 0 is 2036 and not 1900. Test for most significant
033 * bit: if MSB=0 then 2036 basis is used otherwise 1900 if MSB=1.
034 * <p>
035 * Methods exist to convert NTP timestamps to and from the equivalent Java date
036 * representation, which is the number of milliseconds since the standard base
037 * time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
038 * </p>
039 *
040 * @author Jason Mathews, MITRE Corp
041 * @version $Revision: 165675 $ $Date: 2005-05-02 21:09:55 +0100 (Mon, 02 May 2005) $
042 * @see java.util.Date
043 */
044 public class TimeStamp implements java.io.Serializable, Comparable
045 {
046
047 /**
048 * baseline NTP time if bit-0=0 -> 7-Feb-2036 @ 06:28:16 UTC
049 */
050 protected static final long msb0baseTime = 2085978496000L;
051
052 /**
053 * baseline NTP time if bit-0=1 -> 1-Jan-1900 @ 01:00:00 UTC
054 */
055 protected static final long msb1baseTime = -2208988800000L;
056
057 /**
058 * Default NTP date string format. E.g. Fri, Sep 12 2003 21:06:23.860.
059 * See <code>java.text.SimpleDateFormat</code> for code descriptions.
060 */
061 public final static String NTP_DATE_FORMAT = "EEE, MMM dd yyyy HH:mm:ss.SSS";
062
063 /*
064 * Caches for the DateFormatters used by various toString methods.
065 */
066 private static SoftReference simpleFormatter = null;
067 private static SoftReference utcFormatter = null;
068
069 /**
070 * NTP timestamp value: 64-bit unsigned fixed-point number as defined in RFC-1305
071 * with high-order 32 bits the seconds field and the low-order 32-bits the
072 * fractional field.
073 */
074 private long ntpTime;
075
076 private static final long serialVersionUID = 8139806907588338737L;
077
078 // initialization of static time bases
079 /*
080 static {
081 TimeZone utcZone = TimeZone.getTimeZone("UTC");
082 Calendar calendar = Calendar.getInstance(utcZone);
083 calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0);
084 calendar.set(Calendar.MILLISECOND, 0);
085 msb1baseTime = calendar.getTime().getTime();
086 calendar.set(2036, Calendar.FEBRUARY, 7, 6, 28, 16);
087 calendar.set(Calendar.MILLISECOND, 0);
088 msb0baseTime = calendar.getTime().getTime();
089 }
090 */
091
092 /***
093 * Constructs a newly allocated NTP timestamp object
094 * that represents the native 64-bit long argument.
095 */
096 public TimeStamp(long ntpTime)
097 {
098 this.ntpTime = ntpTime;
099 }
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; // high-order 32-bits
191 long fraction = ntpTimeValue & 0xffffffffL; // low-order 32-bits
192
193 // Use round-off on fractional part to preserve going to lower precision
194 fraction = Math.round(1000D * fraction / 0x100000000L);
195
196 /*
197 * If the most significant bit (MSB) on the seconds field is set we use
198 * a different time base. The following text is a quote from RFC-2030 (SNTP v4):
199 *
200 * If bit 0 is set, the UTC time is in the range 1968-2036 and UTC time
201 * is reckoned from 0h 0m 0s UTC on 1 January 1900. If bit 0 is not set,
202 * the time is in the range 2036-2104 and UTC time is reckoned from
203 * 6h 28m 16s UTC on 7 February 2036.
204 */
205 long msb = seconds & 0x80000000L;
206 if (msb == 0) {
207 // use base: 7-Feb-2036 @ 06:28:16 UTC
208 return msb0baseTime + (seconds * 1000) + fraction;
209 } else {
210 // use base: 1-Jan-1900 @ 01:00:00 UTC
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; // no decimal
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; // time < Feb-2036
288 long baseTime;
289 if (useBase1) {
290 baseTime = t - msb1baseTime; // dates <= Feb-2036
291 } else {
292 // if base0 needed for dates >= Feb-2036
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; // set high-order bit if msb1baseTime 1900 used
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 // high-order second bits (32..63) as hexstring
382 appendHexString(buf, (ntpTime >>> 32) & 0xffffffffL);
383
384 // low-order fractional seconds bits (0..31) as hexstring
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 // No cache yet, or cached formatter GC'd
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 // No cache yet, or cached formatter GC'd
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 }