diff --git a/src/edu/nps/moves/dis7/utilities/DisTime.java b/src/edu/nps/moves/dis7/utilities/DisTime.java index 9a551169e16463f3a6a3deeb0600cdcbe5b5c244..12fa7a5fdb9a06c78a31b1202142e7066b102958 100644 --- a/src/edu/nps/moves/dis7/utilities/DisTime.java +++ b/src/edu/nps/moves/dis7/utilities/DisTime.java @@ -9,6 +9,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.*; import java.util.*; /** @@ -75,7 +76,7 @@ import java.util.*; * rollover, instead getting only a monotonically increasing timestamp value.</p> * * <p><b>TODO: timestamp normalization to an initial reference time.</b> - * Functionality is needed to define a shared common time origin (epoch) and also to + * Functionality is needed to define a shared common time origin (epochLvc) and also to * precisely adjust stream timestamps when coordinating recorded PDU playback within LVC applications. * We think the ability to "start at time 0.0", or normalizing initial time to zero * for a recorded PDU stream, is actually a pretty common use case. @@ -121,11 +122,18 @@ public class DisTime */ private static boolean hostClockSynchronized = true; - /** Reference starting time for current timestamps + /** Whether to apply epochLvc as baseline starting time for DIS timestamps, enabling time normalization of LVC streams. + * Distinct from Unix timestamp EPOCH. * <a href="https://en.wikipedia.org/wiki/Epoch_(computing)" target="_blank">https://en.wikipedia.org/wiki/Epoch_(computing)</a>. */ - private static int epoch = 0; - + private static boolean applyEpochLvc = false; + + /** Reference starting time for current DIS timestamps, enabling time normalization of LVC streams. + * Distinct from Unix timestamp EPOCH. + * <a href="https://en.wikipedia.org/wiki/Epoch_(computing)" target="_blank">https://en.wikipedia.org/wiki/Epoch_(computing)</a>. + */ + private static Instant epochLvc = Instant.now(); // initialized at start + /** We can marshal the PDU with a timestamp set to any of several styles. * Remember, you MUST set a timestamp. DIS will regard multiple packets sent * with the same timestamp as duplicates and may discard them. @@ -141,8 +149,11 @@ public class DisTime /** calendar instance */ private static GregorianCalendar calendar; +// private LocalDateTime todayDateTime = new LocalDateTime(); +// private Instant todayInstant = new Instant(); - private static String dateFormatPattern = "HH:mm:ss"; + private static String dateFormatPattern = "yyyy-mm-dd"; + private static String timeFormatPattern = "HH:mm:ss"; // public static DisTime disTime = null; @@ -167,7 +178,7 @@ public class DisTime /** * For current system time, returns the number of DIS time units since the top of the hour, or else - * number of DIS time units since previously set epoch timestamp (for time-zero-based streams). + * number of DIS time units since previously set epochLvc timestamp (for time-zero-based streams). * Note that there are 2^31-1 DIS time units per hour. * @return integer DIS time units since the start of the hour. */ @@ -194,13 +205,14 @@ public class DisTime double differenceValue; int differenceTimestamp; - if (epoch == 0) + if (!hasEpochLvc()) { timeDifferenceMsec = currentTime - topOfHourMsec; } else // normalized time reference having 00:00 at start { - timeDifferenceMsec = currentTime - epoch; + Duration d = java.time.Duration.between(epochLvc, Instant.now()); + timeDifferenceMsec = d.toMillis(); } differenceValue = (timeDifferenceMsec / (3600.0 * 1000.0)) * Integer.MAX_VALUE; differenceTimestamp = (int) differenceValue; @@ -292,7 +304,7 @@ public class DisTime /** * Convert timestamp value to string for logging and diagnostics, - * taking into account epoch and TimeStampStyle (DIS absolute/relative, Unix or Year). + * taking into account epochLvc and TimeStampStyle (DIS absolute/relative, Unix or Year). * TODO consider different formats for different timestampStyle values. * @param timestamp value in milliseconds * @see GregorianCalendar @@ -302,13 +314,13 @@ public class DisTime public static String convertToString(int timestamp) { GregorianCalendar newCalendar = new GregorianCalendar(); - DateFormat formatter = new SimpleDateFormat(dateFormatPattern); + DateFormat formatter = new SimpleDateFormat(dateFormatPattern + " " + timeFormatPattern); if ((timestampStyle == TimestampStyle.IEEE_ABSOLUTE) || (timestampStyle == TimestampStyle.IEEE_RELATIVE)) { - // if epoch is not set, this is regular DIS value - newCalendar.setTimeInMillis(timestamp - epoch); + // if epochLvc is not set, this is regular DIS value + newCalendar.setTimeInMillis(timestamp); // - epochLvc return formatter.format(newCalendar.getTime()); } else if (timestampStyle == TimestampStyle.UNIX) // TODO @@ -401,46 +413,95 @@ public class DisTime return hostClockSynchronized; } - /** Set epoch using current time for zero-based clock, meaning timestamps are normalized to "time zero" of simulation + /** Reset epochLvc using current time for zero-based clock, meaning timestamps are normalized to "time zero" of simulation * as initial starting time */ - public static void setEpochCurrentTimestamp() + public static void setEpochLvcNow() { - setEpoch(getCurrentDisAbsoluteTimestamp()); + applyEpochLvc = true; + setEpochLvc(Instant.now()); // getCurrentDisAbsoluteTimestamp()); } - /** Set initial timestamp as epoch for zero-based clock, meaning timestamps normalized to 0 as initial starting time - * @param initialTimestamp first timestamp in series, considered time zero + /** Set Instant value as epochLvc for zero-based clock, meaning timestamps normalized to 0 at that initial starting time + * @param newEpoch Instant corresponding to first PDU in series, considered time zero */ - public static void setEpoch(int initialTimestamp) + public static void setEpochLvc(java.time.Instant newEpoch) { - epoch = initialTimestamp; + epochLvc = newEpoch; } - /** Get initial timestamp for zero-based clock, meaning all timestamps are measured with resepct to given starting time + /** Get initial timestamp for zero-based clock, meaning all timestamps are measured with respect to given starting time * @return whether localhost is synchronized to time reference */ - public static int getEpoch() + public static java.time.Instant getEpochLvc() + { + return epochLvc; + } + /** Whether epochLvc is currently applied + * @return whether epochLvc is active + */ + public static boolean hasEpochLvc() + { + return applyEpochLvc; + } + + /** Reset epochLvc so it is no longer active + */ + public static void clearEpochLvc() { - return epoch; + applyEpochLvc = false; + } + + private void sleep(long ms) { + sleep(ms, 0); + } + + private void sleep(long ms, int ns) + { + try { + Thread.sleep(ms, ns); + } + catch (InterruptedException ex) {} } /** Self-test for basic smoke testing */ private void selfTest() { DisTime.initializeTimestampMethod(); + System.out.println("=== legacy java.util.Date, calendar methods ==="); System.out.println("DisTime.getTimestampStyle() = " + DisTime.getTimestampStyle()); - System.out.println(" = " + dateFormatPattern); + System.out.println("patterns = " + dateFormatPattern + " " + timeFormatPattern); int initialTimestamp = DisTime.getTimestamp(); System.out.println("DisTime.getTimestamp() initialTimestamp = " + convertToString(initialTimestamp) + " = " + Integer.toUnsignedString(initialTimestamp) + " = " + initialTimestamp + " (unsigned vs signed output)"); System.out.println("DisTime.getTimestamp() = " + convertToString(DisTime.getTimestamp()) + " = " + Integer.toUnsignedString(DisTime.getTimestamp()) + " = " + DisTime.getTimestamp()+ " (unsigned vs signed output)"); System.out.println("DisTime.getCurrentDisAbsoluteTimestamp() = " + convertToString(DisTime.getCurrentDisAbsoluteTimestamp()) + " = " + Integer.toUnsignedString(DisTime.getCurrentDisAbsoluteTimestamp())); System.out.println("DisTime.getCurrentDisRelativeTimestamp() = " + convertToString(DisTime.getCurrentDisRelativeTimestamp()) + " = " + Integer.toUnsignedString(DisTime.getCurrentDisRelativeTimestamp())); System.out.println("DisTime.getCurrentDisTimeUnitsSinceTopOfHour() = " + convertToString(DisTime.getCurrentDisTimeUnitsSinceTopOfHour()) + " = " + DisTime.getCurrentDisTimeUnitsSinceTopOfHour()); - System.out.println("DisTime.getEpoch() = " + convertToString(DisTime.getEpoch()) + " = " + DisTime.getEpoch()); - setEpochCurrentTimestamp(); - System.out.println("DisTime.setEpochCurrentTimestamp();"); - System.out.println("DisTime.getEpoch() = " + convertToString(DisTime.getEpoch()) + " = " + DisTime.getEpoch()); - System.out.println("DisTime.getCurrentDisTimeUnitsSinceTopOfHour() = " + convertToString(DisTime.getCurrentDisTimeUnitsSinceTopOfHour()) + " = " + DisTime.getCurrentDisTimeUnitsSinceTopOfHour()); - } + + System.out.println(); + System.out.println("=== modern java.time methods ==="); + System.out.println("note that LocalDateTime is current time zone while default Instant is UTC with time zone Z appended"); + System.out.println("java.time.LocalDateTime.now(), Instant.now() = " + java.time.LocalDateTime.now() + ", " + java.time.Instant.now()); + System.out.println("java.time.LocalDateTime.now(), Instant.now() = " + java.time.LocalDateTime.now() + ", " + java.time.Instant.now()); + System.out.println("java.time.LocalDateTime.now(), Instant.now() = " + java.time.LocalDateTime.now() + ", " + java.time.Instant.now()); + System.out.println("sleep for 1000 msec..."); + sleep(1000l); // hold on a second + System.out.println("java.time.LocalDateTime.now(), Instant.now() = " + java.time.LocalDateTime.now() + ", " + java.time.Instant.now()); + System.out.println("java.time.LocalDateTime.now(), Instant.now() = " + java.time.LocalDateTime.now() + ", " + java.time.Instant.now()); + System.out.println("java.time.LocalDateTime.now(), Instant.now() = " + java.time.LocalDateTime.now() + ", " + java.time.Instant.now()); + + System.out.println("DisTime.hasEpochLvc() = " + DisTime.hasEpochLvc()); + System.out.println("DisTime.setEpochLvcNow()..."); + setEpochLvcNow(); + System.out.println("DisTime.hasEpochLvc(), = " + DisTime.hasEpochLvc()); + Instant now = Instant.now(); + System.out.println("DisTime.getEpochLvc(), Instant.now(), duration = " + DisTime.getEpochLvc() + ", " + now + ", " + + java.time.Duration.between(getEpochLvc(), now).toMillis() + " msec"); + System.out.println("sleep for 1000 msec..."); + sleep(1000l); // hold on a second + now = Instant.now(); + System.out.println("DisTime.getEpochLvc(), Instant.now(), duration = " + DisTime.getEpochLvc() + ", " + now + ", " + + java.time.Duration.between(getEpochLvc(), now).toMillis() + " msec"); +// System.out.println("DisTime.getCurrentDisTimeUnitsSinceTopOfHour() = " + convertToString(DisTime.getCurrentDisTimeUnitsSinceTopOfHour()) + " = " + DisTime.getCurrentDisTimeUnitsSinceTopOfHour()); + } /** * Main method for testing. diff --git a/src/edu/nps/moves/dis7/utilities/DisTimeLog.txt b/src/edu/nps/moves/dis7/utilities/DisTimeLog.txt index c01445fb5c5abac78ca91a228e15950ee9bb5a57..c50499580b8597cfbee803595f97084fca137070 100644 --- a/src/edu/nps/moves/dis7/utilities/DisTimeLog.txt +++ b/src/edu/nps/moves/dis7/utilities/DisTimeLog.txt @@ -7,15 +7,29 @@ Compiling 1 source file to C:\x3d-github\open-dis7-java\build\classes compile-single: run-single: *** DisTime.main() self test started... +=== legacy java.util.Date, calendar methods === DisTime.getTimestampStyle() = IEEE_ABSOLUTE -DisTime.getTimestamp() initialTimestamp = 08:52:40 = 1011160571 = 1011160571 (unsigned vs signed output) -DisTime.getTimestamp() = 08:52:59 = 1011180853 = 1011180853 (unsigned vs signed output) -DisTime.getCurrentDisAbsoluteTimestamp() = 08:53:00 = 1011180853 -DisTime.getCurrentDisRelativeTimestamp() = 08:53:03 = 1011184430 -DisTime.getCurrentDisTimeUnitsSinceTopOfHour() = 12:26:32 = 505592215 -DisTime.getEpoch() = 16:00:00 = 0 -DisTime.setEpochCurrentTimestamp(); -DisTime.getEpoch() = 16:00:00 = 1011184431 -DisTime.getCurrentDisTimeUnitsSinceTopOfHour() = 19:38:19 = 2147483647 +patterns = yyyy-mm-dd HH:mm:ss +DisTime.getTimestamp() initialTimestamp = 1970-10-02 08:10:52 = 144652113 = 144652113 (unsigned vs signed output) +DisTime.getTimestamp() = 1970-11-02 08:11:01 = 144662849 = 144662849 (unsigned vs signed output) +DisTime.getCurrentDisAbsoluteTimestamp() = 1970-11-02 08:11:02 = 144662849 +DisTime.getCurrentDisRelativeTimestamp() = 1970-11-02 08:11:02 = 144662848 +DisTime.getCurrentDisTimeUnitsSinceTopOfHour() = 1970-05-01 12:05:31 = 72331424 + +=== modern java.time methods === +note that LocalDateTime is current time zone while default Instant is UTC with time zone Z appended +java.time.LocalDateTime.now(), Instant.now() = 2022-01-23T20:02:01.267763900, 2022-01-24T04:02:01.269764Z +java.time.LocalDateTime.now(), Instant.now() = 2022-01-23T20:02:01.270748500, 2022-01-24T04:02:01.270748500Z +java.time.LocalDateTime.now(), Instant.now() = 2022-01-23T20:02:01.271426100, 2022-01-24T04:02:01.271426100Z +sleep for 1000 msec... +java.time.LocalDateTime.now(), Instant.now() = 2022-01-23T20:02:02.277595700, 2022-01-24T04:02:02.277595700Z +java.time.LocalDateTime.now(), Instant.now() = 2022-01-23T20:02:02.277735400, 2022-01-24T04:02:02.277735400Z +java.time.LocalDateTime.now(), Instant.now() = 2022-01-23T20:02:02.277735400, 2022-01-24T04:02:02.277735400Z +DisTime.hasEpochLvc() = false +DisTime.setEpochLvcNow()... +DisTime.hasEpochLvc(), = true +DisTime.getEpochLvc(), Instant.now(), duration = 2022-01-24T04:02:02.279866100Z, 2022-01-24T04:02:02.279866100Z, 0 msec +sleep for 1000 msec... +DisTime.getEpochLvc(), Instant.now(), duration = 2022-01-24T04:02:02.279866100Z, 2022-01-24T04:02:03.289787Z, 1009 msec *** DisTime.main() self test complete. -BUILD SUCCESSFUL (total time: 0 seconds) +BUILD SUCCESSFUL (total time: 2 seconds)