001 package org.apache.commons.net.ntp; 002 /* 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 020 021 import java.lang.ref.SoftReference; 022 import java.text.DateFormat; 023 import java.text.SimpleDateFormat; 024 import java.util.Date; 025 import java.util.Locale; 026 import java.util.TimeZone; 027 028 /*** 029 * TimeStamp class represents the Network Time Protocol (NTP) timestamp 030 * as defined in RFC-1305 and SNTP (RFC-2030). It is represented as a 031 * 64-bit unsigned fixed-point number in seconds relative to 0-hour on 1-January-1900. 032 * The 32-bit low-order bits are the fractional seconds whose precision is 033 * about 200 picoseconds. Assumes overflow date when date passes MAX_LONG 034 * and reverts back to 0 is 2036 and not 1900. Test for most significant 035 * bit: if MSB=0 then 2036 basis is used otherwise 1900 if MSB=1. 036 * <p> 037 * Methods exist to convert NTP timestamps to and from the equivalent Java date 038 * representation, which is the number of milliseconds since the standard base 039 * time known as "the epoch", namely January 1, 1970, 00:00:00 GMT. 040 * </p> 041 * 042 * @author Jason Mathews, MITRE Corp 043 * @version $Revision: 929344 $ $Date: 2010-03-31 01:20:49 +0100 (Wed, 31 Mar 2010) $ 044 * @see java.util.Date 045 */ 046 public class TimeStamp implements java.io.Serializable, Comparable<TimeStamp> 047 { 048 049 /** 050 * baseline NTP time if bit-0=0 -> 7-Feb-2036 @ 06:28:16 UTC 051 */ 052 protected static final long msb0baseTime = 2085978496000L; 053 054 /** 055 * baseline NTP time if bit-0=1 -> 1-Jan-1900 @ 01:00:00 UTC 056 */ 057 protected static final long msb1baseTime = -2208988800000L; 058 059 /** 060 * Default NTP date string format. E.g. Fri, Sep 12 2003 21:06:23.860. 061 * See <code>java.text.SimpleDateFormat</code> for code descriptions. 062 */ 063 public final static String NTP_DATE_FORMAT = "EEE, MMM dd yyyy HH:mm:ss.SSS"; 064 065 /* 066 * Caches for the DateFormatters used by various toString methods. 067 */ 068 private static SoftReference<DateFormat> simpleFormatter = null; 069 private static SoftReference<DateFormat> utcFormatter = null; 070 071 /** 072 * NTP timestamp value: 64-bit unsigned fixed-point number as defined in RFC-1305 073 * with high-order 32 bits the seconds field and the low-order 32-bits the 074 * fractional field. 075 */ 076 private final long ntpTime; 077 078 private static final long serialVersionUID = 8139806907588338737L; 079 080 // initialization of static time bases 081 /* 082 static { 083 TimeZone utcZone = TimeZone.getTimeZone("UTC"); 084 Calendar calendar = Calendar.getInstance(utcZone); 085 calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0); 086 calendar.set(Calendar.MILLISECOND, 0); 087 msb1baseTime = calendar.getTime().getTime(); 088 calendar.set(2036, Calendar.FEBRUARY, 7, 6, 28, 16); 089 calendar.set(Calendar.MILLISECOND, 0); 090 msb0baseTime = calendar.getTime().getTime(); 091 } 092 */ 093 094 /*** 095 * Constructs a newly allocated NTP timestamp object 096 * that represents the native 64-bit long argument. 097 */ 098 public TimeStamp(long ntpTime) 099 { 100 this.ntpTime = ntpTime; 101 } 102 103 /*** 104 * Constructs a newly allocated NTP timestamp object 105 * that represents the value represented by the string 106 * in hexdecimal form (e.g. "c1a089bd.fc904f6d"). 107 * 108 * @throws NumberFormatException - if the string does not contain a parsable timestamp. 109 */ 110 public TimeStamp(String s) throws NumberFormatException 111 { 112 ntpTime = decodeNtpHexString(s); 113 } 114 115 /*** 116 * Constructs a newly allocated NTP timestamp object 117 * that represents the Java Date argument. 118 * 119 * @param d - the Date to be represented by the Timestamp object. 120 */ 121 public TimeStamp(Date d) 122 { 123 ntpTime = (d == null) ? 0 : toNtpTime(d.getTime()); 124 } 125 126 /*** 127 * Returns the value of this Timestamp as a long value. 128 * 129 * @return the 64-bit long value represented by this object. 130 */ 131 public long ntpValue() 132 { 133 return ntpTime; 134 } 135 136 /*** 137 * Returns high-order 32-bits representing the seconds of this NTP timestamp. 138 * 139 * @return seconds represented by this NTP timestamp. 140 */ 141 public long getSeconds() 142 { 143 return (ntpTime >>> 32) & 0xffffffffL; 144 } 145 146 /*** 147 * Returns low-order 32-bits representing the fractional seconds. 148 * 149 * @return fractional seconds represented by this NTP timestamp. 150 */ 151 public long getFraction() 152 { 153 return ntpTime & 0xffffffffL; 154 } 155 156 /*** 157 * Convert NTP timestamp to Java standard time. 158 * 159 * @return NTP Timestamp in Java time 160 */ 161 public long getTime() 162 { 163 return getTime(ntpTime); 164 } 165 166 /*** 167 * Convert NTP timestamp to Java Date object. 168 * 169 * @return NTP Timestamp in Java Date 170 */ 171 public Date getDate() 172 { 173 long time = getTime(ntpTime); 174 return new Date(time); 175 } 176 177 /*** 178 * Convert 64-bit NTP timestamp to Java standard time. 179 * 180 * Note that java time (milliseconds) by definition has less precision 181 * then NTP time (picoseconds) so converting NTP timestamp to java time and back 182 * to NTP timestamp loses precision. For example, Tue, Dec 17 2002 09:07:24.810 EST 183 * is represented by a single Java-based time value of f22cd1fc8a, but its 184 * NTP equivalent are all values ranging from c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c. 185 * 186 * @param ntpTimeValue 187 * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT 188 * represented by this NTP timestamp value. 189 */ 190 public static long getTime(long ntpTimeValue) 191 { 192 long seconds = (ntpTimeValue >>> 32) & 0xffffffffL; // high-order 32-bits 193 long fraction = ntpTimeValue & 0xffffffffL; // low-order 32-bits 194 195 // Use round-off on fractional part to preserve going to lower precision 196 fraction = Math.round(1000D * fraction / 0x100000000L); 197 198 /* 199 * If the most significant bit (MSB) on the seconds field is set we use 200 * a different time base. The following text is a quote from RFC-2030 (SNTP v4): 201 * 202 * If bit 0 is set, the UTC time is in the range 1968-2036 and UTC time 203 * is reckoned from 0h 0m 0s UTC on 1 January 1900. If bit 0 is not set, 204 * the time is in the range 2036-2104 and UTC time is reckoned from 205 * 6h 28m 16s UTC on 7 February 2036. 206 */ 207 long msb = seconds & 0x80000000L; 208 if (msb == 0) { 209 // use base: 7-Feb-2036 @ 06:28:16 UTC 210 return msb0baseTime + (seconds * 1000) + fraction; 211 } else { 212 // use base: 1-Jan-1900 @ 01:00:00 UTC 213 return msb1baseTime + (seconds * 1000) + fraction; 214 } 215 } 216 217 /*** 218 * Helper method to convert Java time to NTP timestamp object. 219 * Note that Java time (milliseconds) by definition has less precision 220 * then NTP time (picoseconds) so converting Ntptime to Javatime and back 221 * to Ntptime loses precision. For example, Tue, Dec 17 2002 09:07:24.810 222 * is represented by a single Java-based time value of f22cd1fc8a, but its 223 * NTP equivalent are all values from c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c. 224 * @param date the milliseconds since January 1, 1970, 00:00:00 GMT. 225 * @return NTP timestamp object at the specified date. 226 */ 227 public static TimeStamp getNtpTime(long date) 228 { 229 return new TimeStamp(toNtpTime(date)); 230 } 231 232 /*** 233 * Constructs a NTP timestamp object and initializes it so that 234 * it represents the time at which it was allocated, measured to the 235 * nearest millisecond. 236 * @return NTP timestamp object set to the current time. 237 * @see java.lang.System#currentTimeMillis() 238 */ 239 public static TimeStamp getCurrentTime() 240 { 241 return getNtpTime(System.currentTimeMillis()); 242 } 243 244 /*** 245 * Convert NTP timestamp hexstring (e.g. "c1a089bd.fc904f6d") to the NTP 246 * 64-bit unsigned fixed-point number. 247 * 248 * @return NTP 64-bit timestamp value. 249 * @throws NumberFormatException - if the string does not contain a parsable timestamp. 250 */ 251 protected static long decodeNtpHexString(String s) 252 throws NumberFormatException 253 { 254 if (s == null) { 255 throw new NumberFormatException("null"); 256 } 257 int ind = s.indexOf('.'); 258 if (ind == -1) { 259 if (s.length() == 0) return 0; 260 return Long.parseLong(s, 16) << 32; // no decimal 261 } 262 263 return Long.parseLong(s.substring(0, ind), 16) << 32 | 264 Long.parseLong(s.substring(ind + 1), 16); 265 } 266 267 /*** 268 * Parses the string argument as a NTP hexidecimal timestamp representation string 269 * (e.g. "c1a089bd.fc904f6d"). 270 * 271 * @param s - hexstring. 272 * @return the Timestamp represented by the argument in hexidecimal. 273 * @throws NumberFormatException - if the string does not contain a parsable timestamp. 274 */ 275 public static TimeStamp parseNtpString(String s) 276 throws NumberFormatException 277 { 278 return new TimeStamp(decodeNtpHexString(s)); 279 } 280 281 /*** 282 * Converts Java time to 64-bit NTP time representation. 283 * 284 * @param t Java time 285 * @return NTP timestamp representation of Java time value. 286 */ 287 protected static long toNtpTime(long t) 288 { 289 boolean useBase1 = t < msb0baseTime; // time < Feb-2036 290 long baseTime; 291 if (useBase1) { 292 baseTime = t - msb1baseTime; // dates <= Feb-2036 293 } else { 294 // if base0 needed for dates >= Feb-2036 295 baseTime = t - msb0baseTime; 296 } 297 298 long seconds = baseTime / 1000; 299 long fraction = ((baseTime % 1000) * 0x100000000L) / 1000; 300 301 if (useBase1) { 302 seconds |= 0x80000000L; // set high-order bit if msb1baseTime 1900 used 303 } 304 305 long time = seconds << 32 | fraction; 306 return time; 307 } 308 309 /*** 310 * Computes a hashcode for this Timestamp. The result is the exclusive 311 * OR of the two halves of the primitive <code>long</code> value 312 * represented by this <code>TimeStamp</code> object. That is, the hashcode 313 * is the value of the expression: 314 * <blockquote><pre> 315 * (int)(this.ntpValue()^(this.ntpValue() >>> 32)) 316 * </pre></blockquote> 317 * 318 * @return a hash code value for this object. 319 */ 320 @Override 321 public int hashCode() 322 { 323 return (int) (ntpTime ^ (ntpTime >>> 32)); 324 } 325 326 /*** 327 * Compares this object against the specified object. 328 * The result is <code>true</code> if and only if the argument is 329 * not <code>null</code> and is a <code>Long</code> object that 330 * contains the same <code>long</code> value as this object. 331 * 332 * @param obj the object to compare with. 333 * @return <code>true</code> if the objects are the same; 334 * <code>false</code> otherwise. 335 */ 336 @Override 337 public boolean equals(Object obj) 338 { 339 if (obj instanceof TimeStamp) { 340 return ntpTime == ((TimeStamp) obj).ntpValue(); 341 } 342 return false; 343 } 344 345 /*** 346 * Converts this <code>TimeStamp</code> object to a <code>String</code>. 347 * The NTP timestamp 64-bit long value is represented as hex string with 348 * seconds separated by fractional seconds by a decimal point; 349 * e.g. c1a089bd.fc904f6d <=> Tue, Dec 10 2002 10:41:49.986 350 * 351 * @return NTP timestamp 64-bit long value as hex string with seconds 352 * separated by fractional seconds. 353 */ 354 @Override 355 public String toString() 356 { 357 return toString(ntpTime); 358 } 359 360 /*** 361 * Left-pad 8-character hex string with 0's 362 * 363 * @param buf - StringBuilder which is appended with leading 0's. 364 * @param l - a long. 365 */ 366 private static void appendHexString(StringBuilder buf, long l) 367 { 368 String s = Long.toHexString(l); 369 for (int i = s.length(); i < 8; i++) 370 buf.append('0'); 371 buf.append(s); 372 } 373 374 /*** 375 * Converts 64-bit NTP timestamp value to a <code>String</code>. 376 * The NTP timestamp value is represented as hex string with 377 * seconds separated by fractional seconds by a decimal point; 378 * e.g. c1a089bd.fc904f6d <=> Tue, Dec 10 2002 10:41:49.986 379 * 380 * @return NTP timestamp 64-bit long value as hex string with seconds 381 * separated by fractional seconds. 382 */ 383 public static String toString(long ntpTime) 384 { 385 StringBuilder buf = new StringBuilder(); 386 // high-order second bits (32..63) as hexstring 387 appendHexString(buf, (ntpTime >>> 32) & 0xffffffffL); 388 389 // low-order fractional seconds bits (0..31) as hexstring 390 buf.append('.'); 391 appendHexString(buf, ntpTime & 0xffffffffL); 392 393 return buf.toString(); 394 } 395 396 /*** 397 * Converts this <code>TimeStamp</code> object to a <code>String</code> 398 * of the form: 399 * <blockquote><pre> 400 * EEE, MMM dd yyyy HH:mm:ss.SSS</pre></blockquote> 401 * See java.text.SimpleDataFormat for code descriptions. 402 * 403 * @return a string representation of this date. 404 */ 405 public String toDateString() 406 { 407 DateFormat formatter = null; 408 if (simpleFormatter != null) { 409 formatter = simpleFormatter.get(); 410 } 411 if (formatter == null) { 412 // No cache yet, or cached formatter GC'd 413 formatter = new SimpleDateFormat(NTP_DATE_FORMAT, Locale.US); 414 formatter.setTimeZone(TimeZone.getDefault()); 415 simpleFormatter = new SoftReference<DateFormat>(formatter); 416 } 417 Date ntpDate = getDate(); 418 synchronized (formatter) { 419 return formatter.format(ntpDate); 420 } 421 } 422 423 /*** 424 * Converts this <code>TimeStamp</code> object to a <code>String</code> 425 * of the form: 426 * <blockquote><pre> 427 * EEE, MMM dd yyyy HH:mm:ss.SSS UTC</pre></blockquote> 428 * See java.text.SimpleDataFormat for code descriptions. 429 * 430 * @return a string representation of this date in UTC. 431 */ 432 public String toUTCString() 433 { 434 DateFormat formatter = null; 435 if (utcFormatter != null) 436 formatter = utcFormatter.get(); 437 if (formatter == null) { 438 // No cache yet, or cached formatter GC'd 439 formatter = new SimpleDateFormat(NTP_DATE_FORMAT + " 'UTC'", 440 Locale.US); 441 formatter.setTimeZone(TimeZone.getTimeZone("UTC")); 442 utcFormatter = new SoftReference<DateFormat>(formatter); 443 } 444 Date ntpDate = getDate(); 445 synchronized (formatter) { 446 return formatter.format(ntpDate); 447 } 448 } 449 450 /*** 451 * Compares two Timestamps numerically. 452 * 453 * @param anotherTimeStamp - the <code>TimeStamp</code> to be compared. 454 * @return the value <code>0</code> if the argument TimeStamp is equal to 455 * this TimeStamp; a value less than <code>0</code> if this TimeStamp 456 * is numerically less than the TimeStamp argument; and a 457 * value greater than <code>0</code> if this TimeStamp is 458 * numerically greater than the TimeStamp argument 459 * (signed comparison). 460 */ 461 public int compareTo(TimeStamp anotherTimeStamp) 462 { 463 long thisVal = this.ntpTime; 464 long anotherVal = anotherTimeStamp.ntpTime; 465 return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1)); 466 } 467 468 }