Skip to content

fix(bigquery-jdbc): preserve microsecond precision for TIME in getString() and LocalTime conversions - #14715

Open
keshavdandeva wants to merge 1 commit into
mainfrom
jdbc/fix-time-precision
Open

keshavdandeva wants to merge 1 commit into
mainfrom
jdbc/fix-time-precision

Conversation

@keshavdandeva

Copy link
Copy Markdown
Contributor

b/570669274

Summary

Fixes sub-second truncation for BigQuery TIME values (including nested ARRAY<TIME> and STRUCT<TIME>) when accessed as strings or java.time.LocalTime, while keeping java.sql.Time strictly compliant with the JDK and JDBC specifications (HH:mm:ss format, millisecond backing).

Problem

BigQuery TIME supports microsecond precision (HH:mm:ss.SSSSSS). Previously:

  1. BigQueryTypeRegistry.createTimeDescriptor() converted raw TIME values (String from REST JSON or Long microseconds from Arrow Storage Read API) to String and LocalTime by routing through java.sql.Time. Because java.sql.Time only stores milliseconds and java.sql.Time.toString() is specified by the JDK to return "hh:mm:ss", fractional seconds were truncated or dropped.
  2. BigQueryBaseResultSet.getObject(int, Class<T>) called getObject(columnIndex) (producing a truncated java.sql.Time) before converting to LocalTime.class or String.class.
  3. BigQueryBaseArray.toString() and BigQueryBaseStruct.toString() formatted TIME elements/attributes via java.sql.Time.toString(), truncating fractional seconds to "HH:mm:ss" when calling ResultSet.getString() on ARRAY<TIME> or STRUCT<TIME> columns.

Solution

  • BigQueryTemporalUtility & BigQueryTypeRegistry:
    • Added TIME_FORMATTER (HH:mm:ss with up to 9 optional fractional digits) in BigQueryTemporalUtility.
    • Updated BigQueryTypeRegistry.createTimeDescriptor() to convert raw String and Long (epoch microseconds) directly to LocalTime and String without intermediate boxing into java.sql.Time.
  • BigQueryBaseResultSet (getObject(int, Class<T>)):
    • Exposed package-private abstract Object getObjectInternal(int columnIndex) on BigQueryBaseResultSet (implemented by the existing getObjectInternal methods in BigQueryJsonResultSet and BigQueryArrowResultSet).
    • For StandardSQLTypeName.TIME with target type LocalTime.class or String.class, convert directly from getObjectInternal(columnIndex) to preserve full microsecond precision.
  • BigQueryBaseArray & BigQueryBaseStruct (toString()):
    • Added package-private abstract Object getRawElement(int index) on BigQueryBaseArray and abstract Object getRawAttribute(int index) on BigQueryBaseStruct, implemented in their JSON and Arrow subclasses.
    • Updated BigQueryBaseArray.toString() and BigQueryBaseStruct.toString() to format StandardSQLTypeName.TIME elements/attributes from their raw values via BigQueryTypeRegistry.convert(..., StandardSQLTypeName.TIME, String.class, null).
    • Array.getResultSet().getString(2) also preserves full microsecond precision for ARRAY<TIME> as BigQueryJsonResultSet and BigQueryArrowResultSet read from the underlying raw values.

Behavior Matrix

Access Method Top-Level TIME ARRAY<TIME> STRUCT<TIME>
rs.getString(...) "14:30:00.123456" "[14:30:00.123456, ...]" "{\"t\":\"14:30:00.123456\"}"
rs.getObject(..., LocalTime.class) LocalTime (microsecond precision) N/A N/A
rs.getArray(...).getResultSet().getString(2) N/A "14:30:00.123456" N/A
rs.getTime(...) / getArray() / getAttributes() Standard java.sql.Time (JDK spec compliant) java.sql.Time[] Object[] with java.sql.Time

Testing

  • Unit tests in BigQueryTypeRegistryTest, BigQueryJsonArrayOfPrimitivesTest, BigQueryArrowArrayOfPrimitivesTest, BigQueryJsonStructTest, and BigQueryArrowStructTest.
  • End-to-end integration test testTimeMicrosecondPrecisionInArrayAndStruct in ITBigQueryJDBCTest covering both REST JSON and Storage Read (Arrow) pipelines.

@keshavdandeva
keshavdandeva marked this pull request as ready for review October 21, 2026 14:38
@keshavdandeva
keshavdandeva requested review from a team as code owners October 21, 2026 14:38

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the BigQuery JDBC driver to improve the handling of TIME data types, specifically focusing on preserving microsecond precision during conversions. Key changes include introducing a new getRawElement and getRawAttribute abstraction to handle raw values consistently across different implementations, updating BigQueryTypeRegistry to correctly parse and format time values with microsecond precision, and adding comprehensive test cases to verify these improvements in arrays, structs, and standard result sets. The review comments suggest using legacy Calendar manipulation instead of java.time APIs for java.sql.Time conversions to better handle Daylight Saving Time and avoid potential precision loss.

@keshavdandeva
keshavdandeva added this pull request to stack #14721 October 21, 2026 19:25
@keshavdandeva
keshavdandeva force-pushed the jdbc/fix-time-precision branch from f860ebd to f2e2624 Compare October 21, 2026 19:25

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant