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 aef4728194..58c012a60e 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 @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static java.time.temporal.ChronoUnit.MICROS; import com.google.api.client.util.Data; import com.google.api.core.BetaApi; @@ -26,6 +27,7 @@ import java.io.Serializable; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.Instant; import java.util.List; import java.util.Map; import java.util.Objects; @@ -36,6 +38,7 @@ * query or when listing table data. */ public class FieldValue implements Serializable { + private static final int MICROSECONDS = 1000000; private static final long serialVersionUID = 469098630191710061L; @@ -191,6 +194,21 @@ public long getTimestampValue() { return scaled.longValue(); } + /** + * Returns this field's value as a {@code String}, representing a timestamp as an Instant. This + * method should only be used if the corresponding field has {@link LegacySQLTypeName#TIMESTAMP} + * type. + * + * @throws ClassCastException if the field is not a primitive type + * @throws NumberFormatException if the field's value could not be converted to {@link Long} + * @throws NullPointerException if {@link #isNull()} returns {@code true} + */ + @SuppressWarnings("unchecked") + public Instant getTimestampInstant() { + checkNotNull(value); + return Instant.EPOCH.plus(getTimestampValue(), MICROS); + } + /** * Returns this field's value as a {@link java.math.BigDecimal}. This method should only be used * if the corresponding field has {@link LegacySQLTypeName#NUMERIC} type. 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 8e8f4cc0c5..4f72e7e66d 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 @@ -2382,6 +2382,23 @@ public void testMultipleStatementsQueryException() throws InterruptedException { } } + @Test + public void testTimestamp() throws InterruptedException { + String query = "SELECT TIMESTAMP '2022-01-24T23:54:25.095574Z'"; + String timestampStringValueExpected = "2022-01-24T23:54:25.095574Z"; + + TableResult resultInteractive = + bigquery.query( + QueryJobConfiguration.newBuilder(query) + .setDefaultDataset(DatasetId.of(DATASET)) + .build()); + for (FieldValueList row : resultInteractive.getValues()) { + FieldValue timeStampCell = row.get(0); + Instant timestampStringValueActual = timeStampCell.getTimestampInstant(); + assertEquals(timestampStringValueExpected, timestampStringValueActual.toString()); + } + } + /* TODO(prasmish): replicate the entire test case for executeSelect */ @Test public void testQuery() throws InterruptedException {