| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package com.google.protobuf.util; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| import static com.google.protobuf.util.DurationsTest.duration; |
| |
| import com.google.common.collect.Lists; |
| import com.google.protobuf.Duration; |
| import com.google.protobuf.Timestamp; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.TimeZone; |
| import org.junit.Assert; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Unit tests for {@link Timestamps}. */ |
| @RunWith(JUnit4.class) |
| public class TimestampsTest { |
| private static final int MILLIS_PER_SECOND = 1000; |
| private static final long MILLIS = 1409130915111L; |
| private static final long SECONDS = MILLIS / MILLIS_PER_SECOND; |
| private static final long MICROS = MILLIS * 1000; |
| private static final long NANOS = MICROS * 1000; |
| |
| @SuppressWarnings("ConstantOverflow") |
| private static final long MAX_VALUE = Long.MAX_VALUE * MILLIS_PER_SECOND + MILLIS_PER_SECOND; |
| |
| @SuppressWarnings("ConstantOverflow") |
| private static final long MIN_VALUE = Long.MIN_VALUE * MILLIS_PER_SECOND; |
| |
| private static final Timestamp TIMESTAMP = timestamp(1409130915, 111000000); |
| private static final Timestamp ZERO_TIMESTAMP = timestamp(0, 0); |
| private static final Timestamp ONE_OF_TIMESTAMP = timestamp(-1, 999000000); |
| |
| private static final Timestamp INVALID_MAX = |
| Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).setNanos(Integer.MAX_VALUE).build(); |
| private static final Timestamp INVALID_MIN = |
| Timestamp.newBuilder().setSeconds(Long.MIN_VALUE).setNanos(Integer.MIN_VALUE).build(); |
| |
| @Test |
| public void testMinMaxAreValid() { |
| assertThat(Timestamps.isValid(Timestamps.MAX_VALUE)).isTrue(); |
| assertThat(Timestamps.isValid(Timestamps.MIN_VALUE)).isTrue(); |
| } |
| |
| |
| @Test |
| public void testIsValid_false() { |
| assertThat(Timestamps.isValid(0L, -1)).isFalse(); |
| assertThat(Timestamps.isValid(1L, -1)).isFalse(); |
| assertThat(Timestamps.isValid(1L, (int) Timestamps.NANOS_PER_SECOND)).isFalse(); |
| assertThat(Timestamps.isValid(-62135596801L, 0)).isFalse(); |
| assertThat(Timestamps.isValid(253402300800L, 0)).isFalse(); |
| } |
| |
| @Test |
| public void testIsValid_true() { |
| assertThat(Timestamps.isValid(0L, 0)).isTrue(); |
| assertThat(Timestamps.isValid(1L, 0)).isTrue(); |
| assertThat(Timestamps.isValid(1L, 1)).isTrue(); |
| assertThat(Timestamps.isValid(42L, 0)).isTrue(); |
| assertThat(Timestamps.isValid(42L, 42)).isTrue(); |
| assertThat(Timestamps.isValid(-62135596800L, 0)).isTrue(); |
| assertThat(Timestamps.isValid(-62135596800L, 1)).isTrue(); |
| assertThat(Timestamps.isValid(62135596799L, 1)).isTrue(); |
| assertThat(Timestamps.isValid(253402300799L, 0)).isTrue(); |
| assertThat(Timestamps.isValid(253402300798L, 1)).isTrue(); |
| assertThat(Timestamps.isValid(253402300798L, (int) (Timestamps.NANOS_PER_SECOND - 1))).isTrue(); |
| } |
| |
| @Test |
| public void testTimestampStringFormat() throws Exception { |
| Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z"); |
| Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z"); |
| assertThat(start.getSeconds()).isEqualTo(Timestamps.TIMESTAMP_SECONDS_MIN); |
| assertThat(start.getNanos()).isEqualTo(0); |
| assertThat(end.getSeconds()).isEqualTo(Timestamps.TIMESTAMP_SECONDS_MAX); |
| assertThat(end.getNanos()).isEqualTo(999999999); |
| assertThat(Timestamps.toString(start)).isEqualTo("0001-01-01T00:00:00Z"); |
| assertThat(Timestamps.toString(end)).isEqualTo("9999-12-31T23:59:59.999999999Z"); |
| |
| Timestamp value = Timestamps.parse("1970-01-01T00:00:00Z"); |
| assertThat(value.getSeconds()).isEqualTo(0); |
| assertThat(value.getNanos()).isEqualTo(0); |
| value = Timestamps.parseUnchecked("1970-01-01T00:00:00Z"); |
| assertThat(value.getSeconds()).isEqualTo(0); |
| assertThat(value.getNanos()).isEqualTo(0); |
| |
| // Test negative timestamps. |
| value = Timestamps.parse("1969-12-31T23:59:59.999Z"); |
| assertThat(value.getSeconds()).isEqualTo(-1); |
| // Nano part is in the range of [0, 999999999] for Timestamp. |
| assertThat(value.getNanos()).isEqualTo(999000000); |
| value = Timestamps.parseUnchecked("1969-12-31T23:59:59.999Z"); |
| assertThat(value.getSeconds()).isEqualTo(-1); |
| // Nano part is in the range of [0, 999999999] for Timestamp. |
| assertThat(value.getNanos()).isEqualTo(999000000); |
| |
| // Test that 3, 6, or 9 digits are used for the fractional part. |
| value = Timestamp.newBuilder().setNanos(10).build(); |
| assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.000000010Z"); |
| value = Timestamp.newBuilder().setNanos(10000).build(); |
| assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.000010Z"); |
| value = Timestamp.newBuilder().setNanos(10000000).build(); |
| assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.010Z"); |
| |
| // Test that parsing accepts timezone offsets. |
| value = Timestamps.parse("1970-01-01T00:00:00.010+08:00"); |
| assertThat(Timestamps.toString(value)).isEqualTo("1969-12-31T16:00:00.010Z"); |
| value = Timestamps.parse("1970-01-01T00:00:00.010-08:00"); |
| assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T08:00:00.010Z"); |
| value = Timestamps.parseUnchecked("1970-01-01T00:00:00.010+08:00"); |
| assertThat(Timestamps.toString(value)).isEqualTo("1969-12-31T16:00:00.010Z"); |
| value = Timestamps.parseUnchecked("1970-01-01T00:00:00.010-08:00"); |
| assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T08:00:00.010Z"); |
| } |
| |
| private volatile boolean stopParsingThreads = false; |
| private volatile String errorMessage = ""; |
| |
| private class ParseTimestampThread extends Thread { |
| private final String[] strings; |
| private final Timestamp[] values; |
| |
| public ParseTimestampThread(String[] strings, Timestamp[] values) { |
| this.strings = strings; |
| this.values = values; |
| } |
| |
| @Override |
| public void run() { |
| int index = 0; |
| while (!stopParsingThreads) { |
| Timestamp result; |
| try { |
| result = Timestamps.parse(strings[index]); |
| } catch (ParseException e) { |
| errorMessage = "Failed to parse timestamp: " + strings[index]; |
| break; |
| } |
| if (result.getSeconds() != values[index].getSeconds() |
| || result.getNanos() != values[index].getNanos()) { |
| errorMessage = |
| "Actual result: " + result.toString() + ", expected: " + values[index].toString(); |
| break; |
| } |
| index = (index + 1) % strings.length; |
| } |
| } |
| } |
| |
| @Test |
| public void testTimestampConcurrentParsing() throws Exception { |
| String[] timestampStrings = |
| new String[] { |
| "0001-01-01T00:00:00Z", |
| "9999-12-31T23:59:59.999999999Z", |
| "1970-01-01T00:00:00Z", |
| "1969-12-31T23:59:59.999Z", |
| }; |
| Timestamp[] timestampValues = new Timestamp[timestampStrings.length]; |
| for (int i = 0; i < timestampStrings.length; i++) { |
| timestampValues[i] = Timestamps.parse(timestampStrings[i]); |
| } |
| |
| final int threadCount = 16; |
| final int runningTime = 5000; // in milliseconds. |
| final List<Thread> threads = new ArrayList<>(); |
| |
| stopParsingThreads = false; |
| errorMessage = ""; |
| for (int i = 0; i < threadCount; i++) { |
| Thread thread = new ParseTimestampThread(timestampStrings, timestampValues); |
| thread.start(); |
| threads.add(thread); |
| } |
| Thread.sleep(runningTime); |
| stopParsingThreads = true; |
| for (Thread thread : threads) { |
| thread.join(); |
| } |
| assertThat(errorMessage).isEmpty(); |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatValueTooSmall() throws Exception { |
| try { |
| // Value too small. |
| Timestamp value = |
| Timestamp.newBuilder().setSeconds(Timestamps.TIMESTAMP_SECONDS_MIN - 1).build(); |
| Timestamps.toString(value); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatValueTooLarge() throws Exception { |
| try { |
| // Value too large. |
| Timestamp value = |
| Timestamp.newBuilder().setSeconds(Timestamps.TIMESTAMP_SECONDS_MAX + 1).build(); |
| Timestamps.toString(value); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatNanosTooSmall() throws Exception { |
| try { |
| // Invalid nanos value. |
| Timestamp value = Timestamp.newBuilder().setNanos(-1).build(); |
| Timestamps.toString(value); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatNanosTooLarge() throws Exception { |
| try { |
| // Invalid nanos value. |
| Timestamp value = Timestamp.newBuilder().setNanos(1000000000).build(); |
| Timestamps.toString(value); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatDateTooSmall() { |
| try { |
| Timestamps.parse("0000-01-01T00:00:00Z"); |
| Assert.fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| assertThat(expected).hasCauseThat().isNotNull(); |
| } |
| try { |
| Timestamps.parseUnchecked("0000-01-01T00:00:00Z"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatDateTooLarge() { |
| try { |
| Timestamps.parse("10000-01-01T00:00:00Z"); |
| Assert.fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| try { |
| Timestamps.parseUnchecked("10000-01-01T00:00:00Z"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatMissingT() { |
| try { |
| Timestamps.parse("1970-01-01 00:00:00Z"); |
| Assert.fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| try { |
| Timestamps.parseUnchecked("1970-01-01 00:00:00Z"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidFormatMissingZ() { |
| try { |
| Timestamps.parse("1970-01-01T00:00:00"); |
| assertWithMessage("ParseException is expected.").fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| try { |
| Timestamps.parseUnchecked("1970-01-01T00:00:00"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidOffset() { |
| try { |
| Timestamps.parse("1970-01-01T00:00:00+0000"); |
| assertWithMessage("ParseException is expected.").fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| try { |
| Timestamps.parseUnchecked("1970-01-01T00:00:00+0000"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidTrailingText() { |
| try { |
| Timestamps.parse("1970-01-01T00:00:00Z0"); |
| assertWithMessage("ParseException is expected.").fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| try { |
| Timestamps.parseUnchecked("1970-01-01T00:00:00Z0"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampInvalidNanoSecond() { |
| try { |
| Timestamps.parse("1970-01-01T00:00:00.ABCZ"); |
| assertWithMessage("ParseException is expected.").fail(); |
| } catch (ParseException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| try { |
| Timestamps.parseUnchecked("1970-01-01T00:00:00.ABCZ"); |
| assertWithMessage("IllegalArgumentException is expected.").fail(); |
| } catch (IllegalArgumentException expected) { |
| Assert.assertNotNull(expected.getMessage()); |
| } |
| } |
| |
| @Test |
| public void testTimestampConversion() throws Exception { |
| Timestamp timestamp = Timestamps.parse("1970-01-01T00:00:01.111111111Z"); |
| assertThat(Timestamps.toNanos(timestamp)).isEqualTo(1111111111); |
| assertThat(Timestamps.toMicros(timestamp)).isEqualTo(1111111); |
| assertThat(Timestamps.toMillis(timestamp)).isEqualTo(1111); |
| assertThat(Timestamps.toSeconds(timestamp)).isEqualTo(1); |
| timestamp = Timestamps.fromNanos(1111111111); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111111111Z"); |
| timestamp = Timestamps.fromMicros(1111111); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111111Z"); |
| timestamp = Timestamps.fromMillis(1111); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z"); |
| timestamp = Timestamps.fromSeconds(1); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01Z"); |
| |
| timestamp = Timestamps.parse("1969-12-31T23:59:59.111111111Z"); |
| assertThat(Timestamps.toNanos(timestamp)).isEqualTo(-888888889); |
| assertThat(Timestamps.toMicros(timestamp)).isEqualTo(-888889); |
| assertThat(Timestamps.toMillis(timestamp)).isEqualTo(-889); |
| assertThat(Timestamps.toSeconds(timestamp)).isEqualTo(-1); |
| timestamp = Timestamps.fromNanos(-888888889); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111111111Z"); |
| timestamp = Timestamps.fromMicros(-888889); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111111Z"); |
| timestamp = Timestamps.fromMillis(-889); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111Z"); |
| timestamp = Timestamps.fromSeconds(-1); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59Z"); |
| } |
| |
| @Test |
| public void testFromDate() { |
| Date date = new Date(1111); |
| Timestamp timestamp = Timestamps.fromDate(date); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z"); |
| } |
| |
| @Test |
| public void testFromDate_after9999CE() { |
| // protobuf still requires Java 7 so no java.time :-( |
| Calendar calendar = Calendar.getInstance(); |
| calendar.clear(); // avoid random number of milliseconds |
| calendar.setTimeZone(TimeZone.getTimeZone("GMT-0")); |
| calendar.set(20000, Calendar.OCTOBER, 20, 5, 4, 3); |
| Date date = calendar.getTime(); |
| try { |
| Timestamps.fromDate(date); |
| Assert.fail("should have thrown IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid."); |
| } |
| } |
| |
| @Test |
| public void testFromDate_beforeYear1() { |
| // protobuf still requires Java 7 so no java.time :-( |
| Calendar calendar = Calendar.getInstance(); |
| calendar.clear(); // avoid random number of milliseconds |
| calendar.setTimeZone(TimeZone.getTimeZone("GMT-0")); |
| calendar.set(-32, Calendar.OCTOBER, 20, 5, 4, 3); |
| Date date = calendar.getTime(); |
| try { |
| Timestamps.fromDate(date); |
| Assert.fail("should have thrown IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid."); |
| } |
| } |
| |
| @Test |
| public void testFromDate_after2262CE() { |
| // protobuf still requires Java 7 so no java.time :-( |
| Calendar calendar = Calendar.getInstance(); |
| calendar.clear(); // avoid random number of milliseconds |
| calendar.setTimeZone(TimeZone.getTimeZone("GMT-0")); |
| calendar.set(2299, Calendar.OCTOBER, 20, 5, 4, 3); |
| Date date = calendar.getTime(); |
| Timestamp timestamp = Timestamps.fromDate(date); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("2299-10-20T05:04:03Z"); |
| } |
| |
| /* Timestamp only stores integral seconds in the Date parent class and stores the nanosecond |
| * adjustment in the Timestamp class. */ |
| @Test |
| public void testFromSqlTimestampSubMillisecondPrecision() { |
| java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(1111); |
| sqlTimestamp.setNanos(sqlTimestamp.getNanos() + 234567); |
| Timestamp timestamp = Timestamps.fromDate(sqlTimestamp); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111234567Z"); |
| } |
| |
| @Test |
| public void testFromSqlTimestamp() { |
| Date date = new java.sql.Timestamp(1111); |
| Timestamp timestamp = Timestamps.fromDate(date); |
| assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z"); |
| } |
| |
| @Test |
| public void testTimeOperations() throws Exception { |
| Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z"); |
| Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z"); |
| |
| Duration duration = Timestamps.between(start, end); |
| assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s"); |
| Timestamp value = Timestamps.add(start, duration); |
| assertThat(value).isEqualTo(end); |
| value = Timestamps.subtract(end, duration); |
| assertThat(value).isEqualTo(start); |
| |
| duration = Timestamps.between(end, start); |
| assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s"); |
| value = Timestamps.add(end, duration); |
| assertThat(value).isEqualTo(start); |
| value = Timestamps.subtract(start, duration); |
| assertThat(value).isEqualTo(end); |
| } |
| |
| @Test |
| public void testComparator() { |
| assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 2))).isEqualTo(0); |
| assertThat(Timestamps.compare(timestamp(0, 0), timestamp(0, 0))).isEqualTo(0); |
| assertThat(Timestamps.compare(timestamp(3, 1), timestamp(1, 1))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 1))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(1, 1), timestamp(3, 1))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(3, 1), timestamp(3, 2))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-1, 1))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-3, 2), timestamp(-3, 3))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-1, 1), timestamp(-3, 1))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-3, 2))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-10, 1), timestamp(1, 1))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(0, 1), timestamp(0, 1))).isEqualTo(0); |
| assertThat(Timestamps.compare(timestamp(0x80000000L, 0), timestamp(0, 0))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(0xFFFFFFFF00000000L, 0), timestamp(0, 0))) |
| .isLessThan(0); |
| |
| Timestamp timestamp0 = timestamp(-50, 400); |
| Timestamp timestamp1 = timestamp(-50, 500); |
| Timestamp timestamp2 = timestamp(50, 500); |
| Timestamp timestamp3 = timestamp(100, 20); |
| Timestamp timestamp4 = timestamp(100, 50); |
| Timestamp timestamp5 = timestamp(100, 150); |
| Timestamp timestamp6 = timestamp(150, 40); |
| |
| List<Timestamp> timestamps = |
| Lists.newArrayList( |
| timestamp5, |
| timestamp3, |
| timestamp1, |
| timestamp4, |
| timestamp6, |
| timestamp2, |
| timestamp0, |
| timestamp3); |
| |
| Collections.sort(timestamps, Timestamps.comparator()); |
| assertThat(timestamps) |
| .containsExactly( |
| timestamp0, |
| timestamp1, |
| timestamp2, |
| timestamp3, |
| timestamp3, |
| timestamp4, |
| timestamp5, |
| timestamp6) |
| .inOrder(); |
| } |
| |
| @Test |
| public void testCompare() { |
| assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 2))).isEqualTo(0); |
| assertThat(Timestamps.compare(timestamp(0, 0), timestamp(0, 0))).isEqualTo(0); |
| assertThat(Timestamps.compare(timestamp(3, 1), timestamp(1, 1))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 1))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(1, 1), timestamp(3, 1))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(3, 1), timestamp(3, 2))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-1, 1))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-3, 2), timestamp(-3, 3))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-1, 1), timestamp(-3, 1))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-3, 2))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(-10, 1), timestamp(1, 1))).isLessThan(0); |
| assertThat(Timestamps.compare(timestamp(0, 1), timestamp(0, 1))).isEqualTo(0); |
| assertThat(Timestamps.compare(timestamp(0x80000000L, 0), timestamp(0, 0))).isGreaterThan(0); |
| assertThat(Timestamps.compare(timestamp(0xFFFFFFFF00000000L, 0), timestamp(0, 0))) |
| .isLessThan(0); |
| } |
| |
| @Test |
| public void testOverflowsArithmeticException() throws Exception { |
| try { |
| Timestamps.toNanos(Timestamps.parse("9999-12-31T23:59:59.999999999Z")); |
| assertWithMessage("Expected an ArithmeticException to be thrown").fail(); |
| } catch (ArithmeticException expected) { |
| } |
| } |
| |
| @Test |
| public void testPositiveOverflow() { |
| try { |
| Timestamps.add(Timestamps.MAX_VALUE, Durations.MAX_VALUE); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testNegativeOverflow() { |
| try { |
| Timestamps.subtract(Timestamps.MIN_VALUE, Durations.MAX_VALUE); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testInvalidMaxNanosecondsOverflow() { |
| try { |
| Timestamps.toNanos(INVALID_MAX); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| @Test |
| public void testInvalidMaxMicrosecondsOverflow() { |
| try { |
| Timestamps.toMicros(INVALID_MAX); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testInvalidMaxMillisecondsOverflow() { |
| try { |
| Timestamps.toMillis(INVALID_MAX); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testInvalidMaxSecondsOverflow() { |
| try { |
| Timestamps.toSeconds(INVALID_MAX); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testInvalidMinNanosecondsOverflow() { |
| try { |
| Timestamps.toNanos(INVALID_MIN); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testInvalidMicrosecondsMinOverflow() { |
| try { |
| Timestamps.toMicros(INVALID_MIN); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testInvalidMinMillisecondsOverflow() { |
| try { |
| Timestamps.toMillis(INVALID_MIN); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testOverInvalidMinSecondsflow() { |
| try { |
| Timestamps.toSeconds(INVALID_MIN); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testMaxNanosecondsConversion() { |
| assertThat(Timestamps.toString(Timestamps.fromNanos(Long.MAX_VALUE))) |
| .isEqualTo("2262-04-11T23:47:16.854775807Z"); |
| } |
| |
| @Test |
| public void testIllegalArgumentExceptionForMaxMicroseconds() { |
| try { |
| Timestamps.fromMicros(Long.MAX_VALUE); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| |
| @Test |
| public void testIllegalArgumentExceptionForMaxMilliseconds() { |
| try { |
| Durations.fromMillis(Long.MAX_VALUE); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testMinNanosecondsConversion() { |
| assertThat(Timestamps.toString(Timestamps.fromNanos(Long.MIN_VALUE))) |
| .isEqualTo("1677-09-21T00:12:43.145224192Z"); |
| } |
| |
| @Test |
| public void testIllegalArgumentExceptionForMinMicroseconds() { |
| try { |
| Timestamps.fromMicros(Long.MIN_VALUE); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| |
| @Test |
| public void testIllegalArgumentExceptionForMinMilliseconds() { |
| try { |
| Timestamps.fromMillis(Long.MIN_VALUE); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testConvertFromSeconds() { |
| assertThat(Timestamps.fromSeconds(SECONDS).getSeconds()).isEqualTo(TIMESTAMP.getSeconds()); |
| assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromSeconds(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromSeconds(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| } |
| |
| @Test |
| public void testConvertFromMillis() { |
| assertThat(Timestamps.fromMillis(MILLIS)).isEqualTo(TIMESTAMP); |
| assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromMillis(-1)).isEqualTo(ONE_OF_TIMESTAMP); |
| assertThat(Timestamps.fromMillis(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromMillis(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| } |
| |
| @Test |
| public void testConvertFromMicros() { |
| assertThat(Timestamps.fromMicros(MICROS)).isEqualTo(TIMESTAMP); |
| assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromMicros(-1000)).isEqualTo(ONE_OF_TIMESTAMP); |
| assertThat(Timestamps.fromMicros(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromMicros(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| } |
| |
| @Test |
| public void testConvertToSeconds() { |
| assertThat(Timestamps.toSeconds(TIMESTAMP)).isEqualTo(SECONDS); |
| assertThat(Timestamps.toSeconds(ZERO_TIMESTAMP)).isEqualTo(0); |
| assertThat(Timestamps.toSeconds(ONE_OF_TIMESTAMP)).isEqualTo(-1); |
| } |
| |
| @Test |
| public void testConvertToMillis() { |
| assertThat(Timestamps.toMillis(TIMESTAMP)).isEqualTo(MILLIS); |
| assertThat(Timestamps.toMillis(ZERO_TIMESTAMP)).isEqualTo(0); |
| assertThat(Timestamps.toMillis(ONE_OF_TIMESTAMP)).isEqualTo(-1); |
| } |
| |
| @Test |
| public void testConvertToMicros() { |
| assertThat(Timestamps.toMicros(TIMESTAMP)).isEqualTo(MICROS); |
| assertThat(Timestamps.toMicros(ZERO_TIMESTAMP)).isEqualTo(0); |
| assertThat(Timestamps.toMicros(ONE_OF_TIMESTAMP)).isEqualTo(-1000); |
| } |
| |
| @Test |
| public void testConvertFromMillisAboveTimestampMaxLimit() { |
| long timestampMaxSeconds = 253402300799L; |
| try { |
| Timestamps.fromMillis((timestampMaxSeconds + 1) * MILLIS_PER_SECOND); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testConvertFromMillisBelowTimestampMaxLimit() { |
| long timestampMinSeconds = -62135596800L; |
| try { |
| Timestamps.fromMillis((timestampMinSeconds - 1) * MILLIS_PER_SECOND); |
| assertWithMessage("Expected an IllegalArgumentException to be thrown").fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test |
| public void testConvertFromNanos() { |
| assertThat(Timestamps.fromNanos(NANOS)).isEqualTo(TIMESTAMP); |
| assertThat(Timestamps.fromNanos(0)).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromNanos(-1000000)).isEqualTo(ONE_OF_TIMESTAMP); |
| assertThat(Timestamps.fromNanos(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| assertThat(Timestamps.fromNanos(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP); |
| } |
| |
| @Test |
| public void testConvertToNanos() { |
| assertThat(Timestamps.toNanos(TIMESTAMP)).isEqualTo(NANOS); |
| assertThat(Timestamps.toNanos(ZERO_TIMESTAMP)).isEqualTo(0); |
| assertThat(Timestamps.toNanos(ONE_OF_TIMESTAMP)).isEqualTo(-1000000); |
| } |
| |
| @Test |
| public void testAdd() { |
| assertThat(Timestamps.add(timestamp(1, 10), duration(1, 20))).isEqualTo(timestamp(2, 30)); |
| assertThat(Timestamps.add(timestamp(1, 10), duration(-1, -11))) |
| .isEqualTo(timestamp(-1, 999999999)); |
| assertThat(Timestamps.add(timestamp(10, 10), duration(-1, -11))) |
| .isEqualTo(timestamp(8, 999999999)); |
| assertThat(Timestamps.add(timestamp(1, 1), duration(1, 999999999))).isEqualTo(timestamp(3, 0)); |
| assertThat(Timestamps.add(timestamp(1, 2), duration(1, 999999999))).isEqualTo(timestamp(3, 1)); |
| } |
| |
| @Test |
| public void testSubtractDuration() { |
| assertThat(Timestamps.subtract(timestamp(1, 10), duration(-1, -20))) |
| .isEqualTo(timestamp(2, 30)); |
| assertThat(Timestamps.subtract(timestamp(1, 10), duration(1, 11))) |
| .isEqualTo(timestamp(-1, 999999999)); |
| assertThat(Timestamps.subtract(timestamp(10, 10), duration(1, 11))) |
| .isEqualTo(timestamp(8, 999999999)); |
| assertThat(Timestamps.subtract(timestamp(1, 1), duration(-1, -999999999))) |
| .isEqualTo(timestamp(3, 0)); |
| assertThat(Timestamps.subtract(timestamp(1, 2), duration(-1, -999999999))) |
| .isEqualTo(timestamp(3, 1)); |
| } |
| |
| static Timestamp timestamp(long seconds, int nanos) { |
| return Timestamps.checkValid( |
| Timestamps.checkValid(Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos))); |
| } |
| } |