diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java index a2ce18767..aef472819 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java @@ -25,6 +25,7 @@ import com.google.common.io.BaseEncoding; import java.io.Serializable; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.List; import java.util.Map; import java.util.Objects; @@ -183,7 +184,10 @@ public long getTimestampValue() { // timestamps are encoded in the format 1408452095.22 where the integer part is seconds since // epoch (e.g. 1408452095.22 == 2014-08-19 07:41:35.220 -05:00) BigDecimal secondsWithMicro = new BigDecimal(getStringValue()); - BigDecimal scaled = secondsWithMicro.scaleByPowerOfTen(6); + // Rounding the BigDecimal to the nearest whole number before setting the longValue in order to + // address TimeStamp rounding issue described in + // https://github.com/googleapis/java-bigquery/issues/1644 + BigDecimal scaled = secondsWithMicro.scaleByPowerOfTen(6).setScale(0, RoundingMode.HALF_UP); return scaled.longValue(); } diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index 484e4c0f5..8ef74458f 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -127,6 +127,7 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -1854,6 +1855,40 @@ public void testQuery() throws InterruptedException { assertNotNull(statistics.getQueryPlan()); } + @Test + public void testQueryTimeStamp() throws InterruptedException { + String query = "SELECT TIMESTAMP '2022-01-24T23:54:25.095574Z'"; + Instant beforeQueryInstant = Instant.parse("2022-01-24T23:54:25.095574Z"); + long microsBeforeQuery = + TimeUnit.SECONDS.toMicros(beforeQueryInstant.getEpochSecond()) + + TimeUnit.NANOSECONDS.toMicros(beforeQueryInstant.getNano()); + + // Verify that timestamp remains the same when priority is set to INTERACTIVE + TableResult result = + bigquery.query( + QueryJobConfiguration.newBuilder(query) + .setDefaultDataset(DatasetId.of(DATASET)) + .setPriority(QueryJobConfiguration.Priority.INTERACTIVE) + .build()); + for (FieldValueList row : result.getValues()) { + FieldValue timeStampCell = row.get(0); + long microsAfterQuery = timeStampCell.getTimestampValue(); + assertEquals(microsBeforeQuery, microsAfterQuery); + } + + // Verify that timestamp remains the same without priority set to INTERACTIVE + TableResult resultInteractive = + bigquery.query( + QueryJobConfiguration.newBuilder(query) + .setDefaultDataset(DatasetId.of(DATASET)) + .build()); + for (FieldValueList row : resultInteractive.getValues()) { + FieldValue timeStampCell = row.get(0); + long microsAfterQuery = timeStampCell.getTimestampValue(); + assertEquals(microsBeforeQuery, microsAfterQuery); + } + } + @Test public void testQueryCaseInsensitiveSchemaFieldByGetName() throws InterruptedException { String query = "SELECT TimestampField, StringField, BooleanField FROM " + TABLE_ID.getTable();