From 15e808de63e6010023db6781d49645a230bce64e Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Thu, 25 Jul 2019 13:12:14 +0200 Subject: [PATCH 1/5] support nanosecond precision when parsing rfc3339 strings --- .../com/google/api/client/util/DateTime.java | 138 +++++++++++++++--- .../google/api/client/util/DateTimeTest.java | 91 ++++++++++++ 2 files changed, 209 insertions(+), 20 deletions(-) diff --git a/google-http-client/src/main/java/com/google/api/client/util/DateTime.java b/google-http-client/src/main/java/com/google/api/client/util/DateTime.java index bd071b327..ebbae0633 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/DateTime.java +++ b/google-http-client/src/main/java/com/google/api/client/util/DateTime.java @@ -14,12 +14,15 @@ package com.google.api.client.util; +import com.google.common.base.Strings; import java.io.Serializable; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Objects; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -39,12 +42,12 @@ public final class DateTime implements Serializable { private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); /** Regular expression for parsing RFC3339 date/times. */ - private static final Pattern RFC3339_PATTERN = - Pattern.compile( - "^(\\d{4})-(\\d{2})-(\\d{2})" // yyyy-MM-dd - + "([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?)?" // 'T'HH:mm:ss.milliseconds - + "([Zz]|([+-])(\\d{2}):(\\d{2}))?"); // 'Z' or time zone shift HH:mm following '+' or - // '-' + private static final String RFC3339_REGEX = + "(\\d{4})-(\\d{2})-(\\d{2})" // yyyy-MM-dd + + "([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d{1,9})?)?" // 'T'HH:mm:ss.nanoseconds + + "([Zz]|([+-])(\\d{2}):(\\d{2}))?"; // 'Z' or time zone shift HH:mm following '+' or '-' + + private static final Pattern RFC3339_PATTERN = Pattern.compile(RFC3339_REGEX); /** * Date/time value expressed as the number of ms since the Unix epoch. @@ -260,6 +263,11 @@ public int hashCode() { * NumberFormatException}. Also, in accordance with the RFC3339 standard, any number of * milliseconds digits is now allowed. * + *

Any time information beyond millisecond precision will be truncated. Prior to version 1.30.2 + * this method did not have a well-defined behavior of what would happen with any time information + * beyond millisecond precision. This could cause some values with more than millisecond precision + * to be rounded up instead of truncated. + * *

For the date-only case, the time zone is ignored and the hourOfDay, minute, second, and * millisecond parameters are set to zero. * @@ -269,6 +277,98 @@ public int hashCode() { * time zone shift but no time. */ public static DateTime parseRfc3339(String str) throws NumberFormatException { + return parseRfc3339WithNanoSeconds(str).toDateTime(); + } + + /** + * Parses an RFC3339 timestamp to a pair of seconds and nanoseconds since Unix Epoch. + * + * @param str Date/time string in RFC3339 format + * @throws NumberFormatException if {@code str} doesn't match the RFC3339 standard format; an + * exception is thrown if {@code str} doesn't match {@code RFC3339_REGEX} or if it contains a + * time zone shift but no time. + */ + public static SecondsAndNanos parseRfc3339ToSecondsAndNanos(String str) + throws NumberFormatException { + return parseRfc3339WithNanoSeconds(str).toSecondsAndNanos(); + } + + /** A timestamp represented as the number of seconds and nanoseconds since Epoch. */ + public static final class SecondsAndNanos implements Serializable { + private final long seconds; + private final int nanos; + + public static SecondsAndNanos ofSecondsAndNanos(long seconds, int nanos) { + return new SecondsAndNanos(seconds, nanos); + } + + private SecondsAndNanos(long seconds, int nanos) { + this.seconds = seconds; + this.nanos = nanos; + } + + public long getSeconds() { + return seconds; + } + + public int getNanos() { + return nanos; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SecondsAndNanos that = (SecondsAndNanos) o; + return seconds == that.seconds && nanos == that.nanos; + } + + @Override + public int hashCode() { + return Objects.hash(seconds, nanos); + } + + @Override + public String toString() { + return String.format("Seconds: %d, Nanos: %d", seconds, nanos); + } + } + + /** Result of parsing a Rfc3339 string. */ + private static class Rfc3339ParseResult implements Serializable { + private final long seconds; + private final int nanos; + private final boolean timeGiven; + private final Integer tzShift; + + private Rfc3339ParseResult(long seconds, int nanos, boolean timeGiven, Integer tzShift) { + this.seconds = seconds; + this.nanos = nanos; + this.timeGiven = timeGiven; + this.tzShift = tzShift; + } + + /** + * Convert this {@link Rfc3339ParseResult} to a {@link DateTime} with millisecond precision. Any + * fraction of a millisecond will be truncated. + */ + private DateTime toDateTime() { + long seconds = TimeUnit.SECONDS.toMillis(this.seconds); + long nanos = TimeUnit.NANOSECONDS.toMillis(this.nanos); + return new DateTime(!timeGiven, seconds + nanos, tzShift); + } + + private SecondsAndNanos toSecondsAndNanos() { + return new SecondsAndNanos(seconds, nanos); + } + } + + private static Rfc3339ParseResult parseRfc3339WithNanoSeconds(String str) + throws NumberFormatException { Matcher matcher = RFC3339_PATTERN.matcher(str); if (!matcher.matches()) { throw new NumberFormatException("Invalid date/time format: " + str); @@ -283,7 +383,7 @@ public static DateTime parseRfc3339(String str) throws NumberFormatException { int hourOfDay = 0; int minute = 0; int second = 0; - int milliseconds = 0; + int nanoseconds = 0; Integer tzShiftInteger = null; if (isTzShiftGiven && !isTimeGiven) { @@ -297,34 +397,32 @@ public static DateTime parseRfc3339(String str) throws NumberFormatException { hourOfDay = Integer.parseInt(matcher.group(5)); // HH minute = Integer.parseInt(matcher.group(6)); // mm second = Integer.parseInt(matcher.group(7)); // ss - if (matcher.group(8) != null) { // contains .milliseconds? - milliseconds = Integer.parseInt(matcher.group(8).substring(1)); // milliseconds - // The number of digits after the dot may not be 3. Need to renormalize. - int fractionDigits = matcher.group(8).substring(1).length() - 3; - milliseconds = (int) ((float) milliseconds / Math.pow(10, fractionDigits)); + if (matcher.group(8) != null) { // contains .nanoseconds? + String fraction = Strings.padEnd(matcher.group(8).substring(1), 9, '0'); + nanoseconds = Integer.parseInt(fraction); } } Calendar dateTime = new GregorianCalendar(GMT); dateTime.set(year, month, day, hourOfDay, minute, second); - dateTime.set(Calendar.MILLISECOND, milliseconds); long value = dateTime.getTimeInMillis(); if (isTimeGiven && isTzShiftGiven) { - int tzShift; - if (Character.toUpperCase(tzShiftRegexGroup.charAt(0)) == 'Z') { - tzShift = 0; - } else { - tzShift = + if (Character.toUpperCase(tzShiftRegexGroup.charAt(0)) != 'Z') { + int tzShift = Integer.parseInt(matcher.group(11)) * 60 // time zone shift HH + Integer.parseInt(matcher.group(12)); // time zone shift mm if (matcher.group(10).charAt(0) == '-') { // time zone shift + or - tzShift = -tzShift; } value -= tzShift * 60000L; // e.g. if 1 hour ahead of UTC, subtract an hour to get UTC time + tzShiftInteger = tzShift; + } else { + tzShiftInteger = 0; } - tzShiftInteger = tzShift; } - return new DateTime(!isTimeGiven, value, tzShiftInteger); + // convert to seconds and nanoseconds + long secondsSinceEpoch = value / 1000L; + return new Rfc3339ParseResult(secondsSinceEpoch, nanoseconds, isTimeGiven, tzShiftInteger); } /** Appends a zero-padded number to a string builder. */ diff --git a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java index 6318e327c..5f1bcef11 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java @@ -14,7 +14,15 @@ package com.google.api.client.util; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import com.google.api.client.util.DateTime.SecondsAndNanos; import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.TimeZone; import junit.framework.TestCase; @@ -142,6 +150,89 @@ public void testParseRfc3339() { assertEquals( DateTime.parseRfc3339("2007-06-01t18:50:00-04:00").getValue(), DateTime.parseRfc3339("2007-06-01t22:50:00Z").getValue()); // from Section 4.2 Local Offsets + + // Test truncating beyond millisecond precision. + assertEquals( + DateTime.parseRfc3339( + "2018-12-31T23:59:59.999999999Z"), // This value would be rounded up prior to version + // 1.30.2 + DateTime.parseRfc3339("2018-12-31T23:59:59.999Z")); + assertEquals( + DateTime.parseRfc3339( + "2018-12-31T23:59:59.9999Z"), // This value would be truncated prior to version 1.30.2 + DateTime.parseRfc3339("2018-12-31T23:59:59.999Z")); + } + + /** + * The following test values have been generated and verified using the {@link DateTimeFormatter} + * in Java 8. + * + *

+   * Timestamp                           |   Seconds     |   Nanos
+   * 2018-03-01T10:11:12.999Z            |   1519899072  |   999000000
+   * 2018-10-28T02:00:00+02:00           |   1540684800  |   0
+   * 2018-10-28T03:00:00+01:00           |   1540692000  |   0
+   * 2018-01-01T00:00:00.000000001Z      |   1514764800  |   1
+   * 2018-10-28T02:00:00Z                |   1540692000  |   0
+   * 2018-12-31T23:59:59.999999999Z      |   1546300799  |   999999999
+   * 2018-03-01T10:11:12.9999Z           |   1519899072  |   999900000
+   * 2018-03-01T10:11:12.000000001Z      |   1519899072  |   1
+   * 2018-03-01T10:11:12.100000000Z      |   1519899072  |   100000000
+   * 2018-03-01T10:11:12.100000001Z      |   1519899072  |   100000001
+   * 2018-03-01T10:11:12-10:00           |   1519935072  |   0
+   * 2018-03-01T10:11:12.999999999Z      |   1519899072  |   999999999
+   * 2018-03-01T10:11:12-12:00           |   1519942272  |   0
+   * 2018-10-28T03:00:00Z                |   1540695600  |   0
+   * 2018-10-28T02:30:00Z                |   1540693800  |   0
+   * 2018-03-01T10:11:12.123Z            |   1519899072  |   123000000
+   * 2018-10-28T02:30:00+02:00           |   1540686600  |   0
+   * 2018-03-01T10:11:12.123456789Z      |   1519899072  |   123456789
+   * 2018-03-01T10:11:12.1000Z           |   1519899072  |   100000000
+   * 
+ */ + public void testParseRfc3339ToSecondsAndNanos() { + Map map = new HashMap<>(); + map.put("2018-03-01T10:11:12.999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999000000)); + map.put("2018-10-28T02:00:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540684800L, 0)); + map.put("2018-10-28T03:00:00+01:00", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); + map.put("2018-01-01T00:00:00.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1514764800L, 1)); + map.put("2018-10-28T02:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); + map.put( + "2018-12-31T23:59:59.999999999Z", + SecondsAndNanos.ofSecondsAndNanos(1546300799L, 999999999)); + map.put("2018-03-01T10:11:12.9999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999900000)); + map.put("2018-03-01T10:11:12.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 1)); + map.put( + "2018-03-01T10:11:12.100000000Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); + map.put( + "2018-03-01T10:11:12.100000001Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000001)); + map.put("2018-03-01T10:11:12-10:00", SecondsAndNanos.ofSecondsAndNanos(1519935072L, 0)); + map.put( + "2018-03-01T10:11:12.999999999Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999999999)); + map.put("2018-03-01T10:11:12-12:00", SecondsAndNanos.ofSecondsAndNanos(1519942272L, 0)); + map.put("2018-10-28T03:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540695600L, 0)); + map.put("2018-10-28T02:30:00Z", SecondsAndNanos.ofSecondsAndNanos(1540693800L, 0)); + map.put("2018-03-01T10:11:12.123Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123000000)); + map.put("2018-10-28T02:30:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540686600L, 0)); + map.put( + "2018-03-01T10:11:12.123456789Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123456789)); + map.put("2018-03-01T10:11:12.1000Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); + + for (Entry entry : map.entrySet()) { + SecondsAndNanos gTimestamp = DateTime.parseRfc3339ToSecondsAndNanos(entry.getKey()); + assertThat( + "Seconds for " + entry + " do not match", + gTimestamp.getSeconds(), + is(equalTo(entry.getValue().getSeconds()))); + assertThat( + "Nanos for " + entry + " do not match", + gTimestamp.getNanos(), + is(equalTo(entry.getValue().getNanos()))); + } } public void testParseAndFormatRfc3339() { From 5f3736773ee4e053db0a5f25fddf5b44df9cac14 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Thu, 25 Jul 2019 13:31:51 +0200 Subject: [PATCH 2/5] removed hamcrest matchers --- .../com/google/api/client/util/DateTimeTest.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java index 5f1bcef11..738e76414 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java @@ -14,10 +14,6 @@ package com.google.api.client.util; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - import com.google.api.client.util.DateTime.SecondsAndNanos; import java.util.Date; import java.util.HashMap; @@ -155,7 +151,7 @@ public void testParseRfc3339() { assertEquals( DateTime.parseRfc3339( "2018-12-31T23:59:59.999999999Z"), // This value would be rounded up prior to version - // 1.30.2 + // 1.30.2 DateTime.parseRfc3339("2018-12-31T23:59:59.999Z")); assertEquals( DateTime.parseRfc3339( @@ -224,14 +220,14 @@ public void testParseRfc3339ToSecondsAndNanos() { for (Entry entry : map.entrySet()) { SecondsAndNanos gTimestamp = DateTime.parseRfc3339ToSecondsAndNanos(entry.getKey()); - assertThat( + assertEquals( "Seconds for " + entry + " do not match", gTimestamp.getSeconds(), - is(equalTo(entry.getValue().getSeconds()))); - assertThat( + entry.getValue().getSeconds()); + assertEquals( "Nanos for " + entry + " do not match", gTimestamp.getNanos(), - is(equalTo(entry.getValue().getNanos()))); + entry.getValue().getNanos()); } } From b53e75fee88a7d9cdb347c09a90af305a616dcfa Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Fri, 26 Jul 2019 08:59:58 +0200 Subject: [PATCH 3/5] fix javadoc + change exception type --- .../main/java/com/google/api/client/util/DateTime.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/google-http-client/src/main/java/com/google/api/client/util/DateTime.java b/google-http-client/src/main/java/com/google/api/client/util/DateTime.java index ebbae0633..cd0dcd777 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/DateTime.java +++ b/google-http-client/src/main/java/com/google/api/client/util/DateTime.java @@ -263,10 +263,7 @@ public int hashCode() { * NumberFormatException}. Also, in accordance with the RFC3339 standard, any number of * milliseconds digits is now allowed. * - *

Any time information beyond millisecond precision will be truncated. Prior to version 1.30.2 - * this method did not have a well-defined behavior of what would happen with any time information - * beyond millisecond precision. This could cause some values with more than millisecond precision - * to be rounded up instead of truncated. + *

Any time information beyond millisecond precision is truncated. * *

For the date-only case, the time zone is ignored and the hourOfDay, minute, second, and * millisecond parameters are set to zero. @@ -284,12 +281,12 @@ public static DateTime parseRfc3339(String str) throws NumberFormatException { * Parses an RFC3339 timestamp to a pair of seconds and nanoseconds since Unix Epoch. * * @param str Date/time string in RFC3339 format - * @throws NumberFormatException if {@code str} doesn't match the RFC3339 standard format; an + * @throws IllegalArgumentException if {@code str} doesn't match the RFC3339 standard format; an * exception is thrown if {@code str} doesn't match {@code RFC3339_REGEX} or if it contains a * time zone shift but no time. */ public static SecondsAndNanos parseRfc3339ToSecondsAndNanos(String str) - throws NumberFormatException { + throws IllegalArgumentException { return parseRfc3339WithNanoSeconds(str).toSecondsAndNanos(); } From e8cf84a89d42d55778e12fed2925537d2e863bff Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Tue, 30 Jul 2019 22:14:58 +0200 Subject: [PATCH 4/5] assert parse directly instead of using a map --- .../google/api/client/util/DateTimeTest.java | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java index 738e76414..1ec0bbe3d 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java @@ -16,9 +16,6 @@ import com.google.api.client.util.DateTime.SecondsAndNanos; import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; import java.util.TimeZone; import junit.framework.TestCase; @@ -187,48 +184,53 @@ public void testParseRfc3339() { * */ public void testParseRfc3339ToSecondsAndNanos() { - Map map = new HashMap<>(); - map.put("2018-03-01T10:11:12.999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999000000)); - map.put("2018-10-28T02:00:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540684800L, 0)); - map.put("2018-10-28T03:00:00+01:00", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); - map.put("2018-01-01T00:00:00.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1514764800L, 1)); - map.put("2018-10-28T02:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); - map.put( + assertParsedRfc3339( + "2018-03-01T10:11:12.999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999000000)); + assertParsedRfc3339( + "2018-10-28T02:00:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540684800L, 0)); + assertParsedRfc3339( + "2018-10-28T03:00:00+01:00", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); + assertParsedRfc3339( + "2018-01-01T00:00:00.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1514764800L, 1)); + assertParsedRfc3339("2018-10-28T02:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); + assertParsedRfc3339( "2018-12-31T23:59:59.999999999Z", SecondsAndNanos.ofSecondsAndNanos(1546300799L, 999999999)); - map.put("2018-03-01T10:11:12.9999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999900000)); - map.put("2018-03-01T10:11:12.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 1)); - map.put( + assertParsedRfc3339( + "2018-03-01T10:11:12.9999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999900000)); + assertParsedRfc3339( + "2018-03-01T10:11:12.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 1)); + assertParsedRfc3339( "2018-03-01T10:11:12.100000000Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); - map.put( + assertParsedRfc3339( "2018-03-01T10:11:12.100000001Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000001)); - map.put("2018-03-01T10:11:12-10:00", SecondsAndNanos.ofSecondsAndNanos(1519935072L, 0)); - map.put( + assertParsedRfc3339( + "2018-03-01T10:11:12-10:00", SecondsAndNanos.ofSecondsAndNanos(1519935072L, 0)); + assertParsedRfc3339( "2018-03-01T10:11:12.999999999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999999999)); - map.put("2018-03-01T10:11:12-12:00", SecondsAndNanos.ofSecondsAndNanos(1519942272L, 0)); - map.put("2018-10-28T03:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540695600L, 0)); - map.put("2018-10-28T02:30:00Z", SecondsAndNanos.ofSecondsAndNanos(1540693800L, 0)); - map.put("2018-03-01T10:11:12.123Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123000000)); - map.put("2018-10-28T02:30:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540686600L, 0)); - map.put( + assertParsedRfc3339( + "2018-03-01T10:11:12-12:00", SecondsAndNanos.ofSecondsAndNanos(1519942272L, 0)); + assertParsedRfc3339("2018-10-28T03:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540695600L, 0)); + assertParsedRfc3339("2018-10-28T02:30:00Z", SecondsAndNanos.ofSecondsAndNanos(1540693800L, 0)); + assertParsedRfc3339( + "2018-03-01T10:11:12.123Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123000000)); + assertParsedRfc3339( + "2018-10-28T02:30:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540686600L, 0)); + assertParsedRfc3339( "2018-03-01T10:11:12.123456789Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123456789)); - map.put("2018-03-01T10:11:12.1000Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); + assertParsedRfc3339( + "2018-03-01T10:11:12.1000Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); + } - for (Entry entry : map.entrySet()) { - SecondsAndNanos gTimestamp = DateTime.parseRfc3339ToSecondsAndNanos(entry.getKey()); - assertEquals( - "Seconds for " + entry + " do not match", - gTimestamp.getSeconds(), - entry.getValue().getSeconds()); - assertEquals( - "Nanos for " + entry + " do not match", - gTimestamp.getNanos(), - entry.getValue().getNanos()); - } + private void assertParsedRfc3339(String input, SecondsAndNanos expected) { + SecondsAndNanos actual = DateTime.parseRfc3339ToSecondsAndNanos(input); + assertEquals( + "Seconds for " + input + " do not match", actual.getSeconds(), expected.getSeconds()); + assertEquals("Nanos for " + input + " do not match", actual.getNanos(), expected.getNanos()); } public void testParseAndFormatRfc3339() { From 1c058b42e7673de5bf3423f27977227525690674 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Tue, 30 Jul 2019 22:17:16 +0200 Subject: [PATCH 5/5] put expected and actual in correct order --- .../test/java/com/google/api/client/util/DateTimeTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java index 1ec0bbe3d..c8b9cd513 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java @@ -229,8 +229,8 @@ public void testParseRfc3339ToSecondsAndNanos() { private void assertParsedRfc3339(String input, SecondsAndNanos expected) { SecondsAndNanos actual = DateTime.parseRfc3339ToSecondsAndNanos(input); assertEquals( - "Seconds for " + input + " do not match", actual.getSeconds(), expected.getSeconds()); - assertEquals("Nanos for " + input + " do not match", actual.getNanos(), expected.getNanos()); + "Seconds for " + input + " do not match", expected.getSeconds(), actual.getSeconds()); + assertEquals("Nanos for " + input + " do not match", expected.getNanos(), actual.getNanos()); } public void testParseAndFormatRfc3339() {