[java-support] branch master updated: JSPT-79 - Review date and time handling for Java 8

Ian Young ian at iay.org.uk
Wed Mar 20 11:56:02 EDT 2019


This is an automated email from the git hooks/post-receive script.

iay pushed a commit to branch master
in repository java-support.

View the commit online:
http://git.shibboleth.net/view/?p=java-support.git;a=commit;h=a54e14305561cba4f4103041b41b436ee2ff3822

The following commit(s) were added to refs/heads/master by this push:
       new  a54e143   JSPT-79 - Review date and time handling for Java 8
a54e143 is described below

commit a54e14305561cba4f4103041b41b436ee2ff3822
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Wed Mar 20 15:55:56 2019 +0000

    JSPT-79 - Review date and time handling for Java 8
    
    Change AttributeSupport to have a long-clean API for instants and durations.
    Related changes made in DOMTypeSupport, not yet complete.
---
 .../java/support/xml/AttributeSupport.java         | 37 +++++++++++--------
 .../utilities/java/support/xml/DOMTypeSupport.java | 23 ++++++++----
 .../java/support/xml/AttributeSupportTest.java     | 43 ++++++++++++----------
 .../java/support/xml/DomTypeSupportTest.java       | 14 ++-----
 4 files changed, 66 insertions(+), 51 deletions(-)

diff --git a/src/main/java/net/shibboleth/utilities/java/support/xml/AttributeSupport.java b/src/main/java/net/shibboleth/utilities/java/support/xml/AttributeSupport.java
index 9375a9c..4afe75a 100644
--- a/src/main/java/net/shibboleth/utilities/java/support/xml/AttributeSupport.java
+++ b/src/main/java/net/shibboleth/utilities/java/support/xml/AttributeSupport.java
@@ -17,6 +17,8 @@
 
 package net.shibboleth.utilities.java.support.xml;
 
+import java.time.Duration;
+import java.time.Instant;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -155,16 +157,21 @@ public final class AttributeSupport {
     }
 
     /**
-     * Adds an attribute to the given element. The value of the attribute is the instant now + the given duration
+     * Adds an attribute to the given element. The value of the attribute is the given instant
      * expressed in XML dateTime format.
+     *
+     * Note that simply using <code>instant.toString()</code> is equivalent for many use cases, but
+     * the result will be different on a system with a higher-resolution clock, as the resulting
+     * string value may have sub-millisecond precision. This method always works to millisecond
+     * precision.
      * 
      * @param element element to which the attribute will be added, not null
      * @param attributeName name of the attribute, not null
-     * @param duration duration, in milliseconds, must be greater than 0
+     * @param instant instant to set into the attribute, not null
      */
     public static void appendDateTimeAttribute(@Nonnull final Element element, @Nonnull final QName attributeName,
-            final long duration) {
-        appendAttribute(element, attributeName, DOMTypeSupport.longToDateTime(duration));
+            @Nonnull final Instant instant) {
+        appendAttribute(element, attributeName, DOMTypeSupport.instantToDateTime(instant));
     }
 
     /**
@@ -172,11 +179,11 @@ public final class AttributeSupport {
      * 
      * @param element element to which the attribute will be added, not null
      * @param attributeName name of the attribute, not null
-     * @param duration duration, in milliseconds, must be greater than 0
+     * @param duration duration, must be greater than 0
      */
     public static void appendDurationAttribute(@Nonnull final Element element, @Nonnull final QName attributeName,
-            final long duration) {
-        appendAttribute(element, attributeName, DOMTypeSupport.longToDuration(duration));
+            @Nonnull final Duration duration) {
+        appendAttribute(element, attributeName, DOMTypeSupport.longToDuration(duration.toMillis()));
     }
 
     /**
@@ -346,33 +353,33 @@ public final class AttributeSupport {
     }
 
     /**
-     * Gets the value of a dateTime-type attribute in milliseconds since the epoch.
+     * Gets the value of a dateTime-type attribute as an {@link Instant}.
      * 
      * @param attribute attribute from which to extract the value, may be null
      * 
-     * @return date/time in millisecond since the epoch, or null if the attribute was null
+     * @return date/time as an {@link Instant}, or null if the attribute was null
      */
-    @Nullable public static Long getDateTimeAttributeAsLong(@Nullable final Attr attribute) {
+    @Nullable public static Instant getDateTimeAttribute(@Nullable final Attr attribute) {
         if (attribute == null || StringSupport.trimOrNull(attribute.getValue()) == null) {
             return null;
         }
 
-        return DOMTypeSupport.dateTimeToLong(attribute.getValue());
+        return DOMTypeSupport.dateTimeToInstant(attribute.getValue());
     }
 
     /**
-     * Gets the value of a duration-type attribute in milliseconds.
+     * Gets the value of a duration-type attribute as a {@link Duration}.
      * 
      * @param attribute attribute from which to extract the value, may be null
      * 
-     * @return duration, in millisecond, or null if the attribute was null
+     * @return duration, or null if the attribute was null
      */
-    @Nullable public static Long getDurationAttributeValueAsLong(@Nullable final Attr attribute) {
+    @Nullable public static Duration getDurationAttributeValue(@Nullable final Attr attribute) {
         if (attribute == null || StringSupport.trimOrNull(attribute.getValue()) == null)  {
             return null;
         }
 
-        return DOMTypeSupport.durationToLong(attribute.getValue());
+        return DOMTypeSupport.durationToDuration(attribute.getValue());
     }
 
     /**
diff --git a/src/main/java/net/shibboleth/utilities/java/support/xml/DOMTypeSupport.java b/src/main/java/net/shibboleth/utilities/java/support/xml/DOMTypeSupport.java
index 8c68872..c3bf4ca 100644
--- a/src/main/java/net/shibboleth/utilities/java/support/xml/DOMTypeSupport.java
+++ b/src/main/java/net/shibboleth/utilities/java/support/xml/DOMTypeSupport.java
@@ -17,6 +17,7 @@
 
 package net.shibboleth.utilities.java.support.xml;
 
+import java.time.Instant;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.TimeZone;
@@ -49,18 +50,18 @@ public final class DOMTypeSupport {
     }
 
     /**
-     * Converts a lexical dateTime, as defined by XML Schema 1.0, into milliseconds since the epoch.
+     * Converts a lexical dateTime, as defined by XML Schema 1.0, into an {@link Instant}.
      * 
      * @param dateTime lexical date/time, may not be null
      * 
-     * @return the date/time expressed as milliseconds since the epoch
+     * @return the date/time expressed as an {@link Instant}
      */
-    public static long dateTimeToLong(@Nonnull final String dateTime) {
+    public static Instant dateTimeToInstant(@Nonnull final String dateTime) {
         final String trimmedString =
                 Constraint.isNotNull(StringSupport.trimOrNull(dateTime), "Lexical dateTime may not be null or empty");
 
         final XMLGregorianCalendar calendar = dataTypeFactory.newXMLGregorianCalendar(trimmedString);
-        return calendar.toGregorianCalendar().getTimeInMillis();
+        return calendar.toGregorianCalendar().toInstant();
     }
 
     /**
@@ -70,6 +71,7 @@ public final class DOMTypeSupport {
      * 
      * @return duration in milliseconds
      */
+    @Deprecated
     public static long durationToLong(final String duration) {
         return dataTypeFactory.newDuration(duration).getTimeInMillis(baseline);
     }
@@ -81,6 +83,7 @@ public final class DOMTypeSupport {
      * 
      * @return duration in milliseconds
      */
+    @Deprecated
     public static long durationToLong(final Duration duration) {
         return duration.getTimeInMillis(baseline);
     }
@@ -139,17 +142,22 @@ public final class DOMTypeSupport {
     }
 
     /**
-     * Converts a numerical date/time, given in milliseconds since the epoch, to a lexical dateTime defined by XML
+     * Converts a numerical date/time, given as an {@link Instant}, to a lexical dateTime defined by XML
      * Schema 1.0.
      * 
+     * Note that simply using <code>instant.toString()</code> is equivalent for many use cases, but
+     * the result will be different on a system with a higher-resolution clock, as the resulting
+     * string value may have sub-millisecond precision. This method always works to millisecond
+     * precision.
+     * 
      * @param dateTime the date time to be converted
      * 
      * @return the lexical representation of the date/time
      */
-    @Nonnull public static String longToDateTime(final long dateTime) {
+    @Nonnull public static String instantToDateTime(@Nonnull final Instant dateTime) {
         final GregorianCalendar calendar = new GregorianCalendar();
         calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
-        calendar.setTimeInMillis(dateTime);
+        calendar.setTimeInMillis(dateTime.toEpochMilli());
 
         return dataTypeFactory.newXMLGregorianCalendar(calendar).normalize().toXMLFormat();
     }
@@ -161,6 +169,7 @@ public final class DOMTypeSupport {
      * 
      * @return the lexical representation
      */
+    @Deprecated
     @Nonnull public static String longToDuration(final long duration) {
         return dataTypeFactory.newDuration(duration).toString();
     }
diff --git a/src/test/java/net/shibboleth/utilities/java/support/xml/AttributeSupportTest.java b/src/test/java/net/shibboleth/utilities/java/support/xml/AttributeSupportTest.java
index 676229a..54c79bb 100644
--- a/src/test/java/net/shibboleth/utilities/java/support/xml/AttributeSupportTest.java
+++ b/src/test/java/net/shibboleth/utilities/java/support/xml/AttributeSupportTest.java
@@ -18,6 +18,9 @@
 package net.shibboleth.utilities.java.support.xml;
 
 import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
@@ -423,29 +426,29 @@ public class AttributeSupportTest {
         Assert.assertEquals(AttributeSupport.getAttributeValueAsQName(AttributeSupport.getAttribute(attributes,
                 new QName(TEST_NS, "testAttrZero"))), new QName("0"), "attribute called testAttrZero");
 
-        // getDateTimeAttributeAsLong
+        // getDateTimeAttribute
         // Use the previously tested AttributeSupport.getAttribute
-        Assert.assertNull(AttributeSupport.getDateTimeAttributeAsLong(null), "null attribute should be null");
-        Assert.assertNull(AttributeSupport.getDateTimeAttributeAsLong(AttributeSupport.getAttribute(attributes,
+        Assert.assertNull(AttributeSupport.getDateTimeAttribute(null), "null attribute should be null");
+        Assert.assertNull(AttributeSupport.getDateTimeAttribute(AttributeSupport.getAttribute(attributes,
                 new QName(TEST_NS, "testAttrEmpty"))), "\"\" should be null");
-        Assert.assertNull(AttributeSupport.getDateTimeAttributeAsLong(AttributeSupport.getAttribute(attributes,
+        Assert.assertNull(AttributeSupport.getDateTimeAttribute(AttributeSupport.getAttribute(attributes,
                 new QName(TEST_NS, "testAttrEmpty"))), "\"0\" should be null");
         Assert.assertEquals(
-                AttributeSupport.getDateTimeAttributeAsLong(
-                        AttributeSupport.getAttribute(attributes, new QName(TEST_NS, "testAttrEpochPlusOneSec")))
-                        .intValue(), 1000, "attribute called testAttrEpochPlusOneSec");
+                AttributeSupport.getDateTimeAttribute(
+                        AttributeSupport.getAttribute(attributes, new QName(TEST_NS, "testAttrEpochPlusOneSec"))),
+                Instant.ofEpochSecond(1), "attribute called testAttrEpochPlusOneSec");
 
         // getDurationAttributeValueAsLong
         // Use the previously tested AttributeSupport.getAttribute
-        Assert.assertNull(AttributeSupport.getDurationAttributeValueAsLong(null), "null attribute should be null");
-        Assert.assertNull(AttributeSupport.getDurationAttributeValueAsLong(AttributeSupport.getAttribute(attributes,
+        Assert.assertNull(AttributeSupport.getDurationAttributeValue(null), "null attribute should be null");
+        Assert.assertNull(AttributeSupport.getDurationAttributeValue(AttributeSupport.getAttribute(attributes,
                 new QName(TEST_NS, "testAttrEmpty"))), "\"\" should be null");
-        Assert.assertNull(AttributeSupport.getDurationAttributeValueAsLong(AttributeSupport.getAttribute(attributes,
+        Assert.assertNull(AttributeSupport.getDurationAttributeValue(AttributeSupport.getAttribute(attributes,
                 new QName(TEST_NS, "testAttrEmpty"))), "\"0\" should be null");
         Assert.assertEquals(
-                AttributeSupport.getDurationAttributeValueAsLong(
-                        AttributeSupport.getAttribute(attributes, new QName(TEST_NS, "testAttrMinusOneDay")))
-                        .intValue(), -24 * 60 * 60 * 1000, "attribute called testAttrMinusOneDay");
+                AttributeSupport.getDurationAttributeValue(
+                        AttributeSupport.getAttribute(attributes, new QName(TEST_NS, "testAttrMinusOneDay"))),
+                        Duration.ofDays(-1), "attribute called testAttrMinusOneDay");
     }
 
     @Test(dependsOnMethods = {"testGetAttributeMethods", "testGetID"}) public void testAppends() {
@@ -592,7 +595,7 @@ public class AttributeSupportTest {
         Assert.assertEquals(AttributeSupport.getIdAttribute(createdElement).getValue(), testResult,
                 "id Attribute added correctly");
 
-        int duration = 1000;
+        final var duration = Duration.ofSeconds(1);
         qNameBase = qNameBase + "New";
         qName = new QName(TEST_NS, qNameBase, TEST_PREFIX);
         Assert.assertNull(AttributeSupport.getAttributeValue(createdElement, qName), "Test precondition");
@@ -620,10 +623,12 @@ public class AttributeSupportTest {
         }
         Assert.assertFalse(thrown, "All non nulls should not throw");
         Assert.assertEquals(
-                AttributeSupport.getDurationAttributeValueAsLong(AttributeSupport.getAttribute(createdElement, qName))
-                        .intValue(), duration, "getDurationAttributeValueAsLong failed");
+                AttributeSupport.getDurationAttributeValue(AttributeSupport.getAttribute(createdElement, qName)),
+                        duration, "getDurationAttributeValueAsLong failed");
 
-        long time = 1000 * 60 * 60 * 24;
+        // Construct a time that contains nothing below the level of milliseconds,
+        // for compatibility with representations that don't have higher precision.
+        final var time = Instant.now().truncatedTo(ChronoUnit.MILLIS);
         qNameBase = qNameBase + "New";
         qName = new QName(TEST_NS, qNameBase, TEST_PREFIX);
         Assert.assertNull(AttributeSupport.getAttributeValue(createdElement, qName), "Test precondition");
@@ -651,8 +656,8 @@ public class AttributeSupportTest {
         }
         Assert.assertFalse(thrown, "All non nulls should not throw");
         Assert.assertEquals(
-                AttributeSupport.getDateTimeAttributeAsLong(AttributeSupport.getAttribute(createdElement, qName))
-                        .intValue(), time, "getDurationAttributeValueAsLong failed");
+                AttributeSupport.getDateTimeAttribute(AttributeSupport.getAttribute(createdElement, qName)),
+                        time, "getDurationAttributeValueAsLong failed");
 
     }
 
diff --git a/src/test/java/net/shibboleth/utilities/java/support/xml/DomTypeSupportTest.java b/src/test/java/net/shibboleth/utilities/java/support/xml/DomTypeSupportTest.java
index 14b7ffa..c6db94c 100644
--- a/src/test/java/net/shibboleth/utilities/java/support/xml/DomTypeSupportTest.java
+++ b/src/test/java/net/shibboleth/utilities/java/support/xml/DomTypeSupportTest.java
@@ -18,6 +18,7 @@
 package net.shibboleth.utilities.java.support.xml;
 
 import java.io.IOException;
+import java.time.Instant;
 
 import javax.xml.namespace.QName;
 import javax.xml.parsers.DocumentBuilder;
@@ -60,16 +61,9 @@ public class DomTypeSupportTest {
     }
     
     
-    @Test public void testDateTimeToLong() {
-        Assert.assertEquals(DOMTypeSupport.dateTimeToLong("1970-01-01T00:00:01Z"), 1000, "Epoch plus one second");
-        Assert.assertEquals(DOMTypeSupport.dateTimeToLong("1969-12-31T23:59:59Z"), -1000, "Epoch minus one second");
-        Assert.assertEquals(DOMTypeSupport.dateTimeToLong("1970-01-01T00:00:00-05:00"), 5 * 3600 * 1000,
-                "Epoch minus 5 hours timezone");
-    }
-
-    @Test public void testLongToDateTime() {
-        Assert.assertEquals(DOMTypeSupport.longToDateTime(1000), "1970-01-01T00:00:01.000Z", "Epoch plus one second");
-        Assert.assertEquals(DOMTypeSupport.longToDateTime(-1000), "1969-12-31T23:59:59.000Z", "Epoch minus one second");
+    @Test public void testInstantToDateTime() {
+        Assert.assertEquals(DOMTypeSupport.instantToDateTime(Instant.EPOCH.plusMillis(1000)), "1970-01-01T00:00:01.000Z", "Epoch plus one second");
+        Assert.assertEquals(DOMTypeSupport.instantToDateTime(Instant.EPOCH.plusMillis(-1000)), "1969-12-31T23:59:59.000Z", "Epoch minus one second");
     }
 
     @Test public void testDurationToLong() {

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list