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