1717package com .google .cloud .bigquery .jdbc .it ;
1818
1919import static com .google .common .truth .Truth .assertThat ;
20+ import static org .junit .jupiter .api .Assertions .assertAll ;
2021import static org .junit .jupiter .api .Assertions .assertEquals ;
2122import static org .junit .jupiter .api .Assertions .assertFalse ;
2223import static org .junit .jupiter .api .Assertions .assertNotNull ;
5758import java .time .Instant ;
5859import java .time .LocalTime ;
5960import java .time .ZoneId ;
61+ import java .time .ZoneOffset ;
6062import java .time .format .DateTimeFormatter ;
6163import java .util .Calendar ;
6264import java .util .Properties ;
@@ -1655,12 +1657,16 @@ public void testPreparedStatementDateTimeValues() throws SQLException {
16551657
16561658 bigQueryStatement .execute (String .format (createTableQuery , DATASET , TABLE_NAME1 ));
16571659
1660+ Instant moment = Instant .parse ("2025-12-03T12:34:56.123Z" );
1661+ Time noon = Time .valueOf (LocalTime .NOON );
1662+ Date date = Date .valueOf ("2025-12-03" );
1663+
16581664 PreparedStatement insertPs = bigQueryConnection .prepareStatement (insertQuery );
16591665 insertPs .setString (1 , "dishwasher" );
16601666 insertPs .setInt (2 , 1 );
1661- insertPs .setTimestamp (3 , new Timestamp ( System . currentTimeMillis () ));
1662- insertPs .setTime (4 , Time . valueOf ( LocalTime . NOON ) );
1663- insertPs .setDate (5 , Date . valueOf ( "2025-12-3" ) );
1667+ insertPs .setTimestamp (3 , Timestamp . from ( moment ));
1668+ insertPs .setTime (4 , noon );
1669+ insertPs .setDate (5 , date );
16641670
16651671 int insertStatus = insertPs .executeUpdate ();
16661672 assertEquals (1 , insertStatus );
@@ -1669,22 +1675,65 @@ public void testPreparedStatementDateTimeValues() throws SQLException {
16691675 Calendar utcCal = Calendar .getInstance (TimeZone .getTimeZone ("UTC" ));
16701676 insertPs .setString (1 , "refrigerator" );
16711677 insertPs .setInt (2 , 2 );
1672- insertPs .setTimestamp (3 , new Timestamp ( System . currentTimeMillis () ), utcCal );
1673- insertPs .setTime (4 , Time . valueOf ( LocalTime . NOON ) , utcCal );
1674- insertPs .setDate (5 , Date . valueOf ( "2025-12-03" ) , utcCal );
1678+ insertPs .setTimestamp (3 , Timestamp . from ( moment ), utcCal );
1679+ insertPs .setTime (4 , noon , utcCal );
1680+ insertPs .setDate (5 , date , utcCal );
16751681
16761682 int insertStatus2 = insertPs .executeUpdate ();
16771683 assertEquals (1 , insertStatus2 );
16781684
1685+ // Read the stored values server-side so no driver temporal conversion is involved.
16791686 ResultSet rs =
16801687 bigQueryStatement .executeQuery (
1681- String .format ("SELECT COUNT(*) AS row_count\n " + "FROM %s.%s" , DATASET , TABLE_NAME1 ));
1682- rs .next ();
1688+ String .format (
1689+ "SELECT IntegerField, UNIX_MILLIS(TimestampField), CAST(TimeField AS STRING),"
1690+ + " CAST(DateField AS STRING) FROM %s.%s ORDER BY IntegerField" ,
1691+ DATASET , TABLE_NAME1 ));
1692+
1693+ assertTrue (rs .next ());
1694+ assertEquals (1 , rs .getInt (1 ));
1695+ long plainTimestamp = rs .getLong (2 );
1696+ String plainTime = rs .getString (3 );
1697+ String plainDate = rs .getString (4 );
1698+ assertTrue (rs .next ());
16831699 assertEquals (2 , rs .getInt (1 ));
1700+ long calTimestamp = rs .getLong (2 );
1701+ String calTime = rs .getString (3 );
1702+ String calDate = rs .getString (4 );
1703+ assertFalse (rs .next ());
16841704
1705+ // Drop before asserting so a failure does not leak the table.
16851706 String dropQuery = String .format ("DROP TABLE %s.%s" , DATASET , TABLE_NAME1 );
16861707 int dropStatus = bigQueryStatement .executeUpdate (dropQuery );
16871708 assertEquals (0 , dropStatus );
1709+
1710+ assertAll (
1711+ // Row 1: plain setters.
1712+ () ->
1713+ assertEquals (
1714+ moment .toEpochMilli (), plainTimestamp , "setTimestamp must store the moment" ),
1715+ () -> assertEquals ("12:00:00" , plainTime , "setTime must send its digits" ),
1716+ () -> assertEquals ("2025-12-03" , plainDate , "setDate must send its digits" ),
1717+ // Row 2: Calendar setters. The Calendar cannot change a TIMESTAMP moment; for TIME and DATE
1718+ // it is the zone the value is rendered in.
1719+ () ->
1720+ assertEquals (
1721+ moment .toEpochMilli (), calTimestamp , "setTimestamp(ts, cal) must store the moment" ),
1722+ () ->
1723+ assertEquals (
1724+ Instant .ofEpochMilli (noon .getTime ())
1725+ .atZone (ZoneOffset .UTC )
1726+ .format (DateTimeFormatter .ofPattern ("HH:mm:ss" )),
1727+ calTime ,
1728+ "setTime(t, cal) must send the time of t in the Calendar's zone" ),
1729+ () ->
1730+ assertEquals (
1731+ Instant .ofEpochMilli (date .getTime ())
1732+ .atZone (ZoneOffset .UTC )
1733+ .toLocalDate ()
1734+ .toString (),
1735+ calDate ,
1736+ "setDate(d, cal) must send the date of d in the Calendar's zone" ));
16881737 }
16891738
16901739 @ Test
@@ -2560,12 +2609,7 @@ public void validateGetString() throws Exception {
25602609 DateTimeFormatter .ofPattern ("yyyy-MM-dd HH:mm:ss.SSSSSS" );
25612610 String expectedTimestampString =
25622611 timestampFormatter .format (
2563- Instant .parse ("2023-07-28T12:30:00Z" ).atZone (ZoneId .systemDefault ()).toLocalDateTime ());
2564- String expectedArrayTimestamp =
2565- String .format (
2566- "[%s, %s]" ,
2567- Timestamp .from (Instant .parse ("2023-01-01T01:00:00Z" )),
2568- Timestamp .from (Instant .parse ("2023-01-01T02:00:00Z" )));
2612+ Instant .parse ("2023-07-28T12:30:00Z" ).atZone (ZoneOffset .UTC ).toLocalDateTime ());
25692613
25702614 final ImmutableMap <String , Object > stringResults =
25712615 new ImmutableMap .Builder <String , Object >()
@@ -2593,7 +2637,7 @@ public void validateGetString() throws Exception {
25932637 .put ("arrayNumeric" , "[10.5, 20.5]" )
25942638 .put ("arrayBignumeric" , "[100.1, 200.2]" )
25952639 .put ("arrayBoolean" , "[true, false]" )
2596- .put ("arrayTimestamp" , expectedArrayTimestamp )
2640+ .put ("arrayTimestamp" , "[2023-01-01 01:00:00.0, 2023-01-01 02:00:00.0]" )
25972641 .put ("arrayDate" , "[2023-01-01, 2023-01-02]" )
25982642 .put ("arrayTime" , "[01:00:00, 02:00:00]" )
25992643 .put ("arrayDatetime" , "[2023-01-01 01:00:00.0, 2023-01-01 02:00:00.0]" )
0 commit comments