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    }