1111import java .sql .ResultSet ;
1212import java .sql .SQLException ;
1313import java .sql .Statement ;
14+ import java .sql .Time ;
1415import java .sql .Timestamp ;
1516import java .time .ZoneId ;
1617import java .time .ZonedDateTime ;
18+ import java .util .Arrays ;
1719import java .util .Calendar ;
20+ import java .util .List ;
21+ import java .util .Properties ;
1822import java .util .TimeZone ;
1923import net .snowflake .client .AbstractDriverIT ;
2024import net .snowflake .client .annotations .DontRunOnGithubActions ;
2125import net .snowflake .client .category .TestTags ;
26+ import net .snowflake .client .util .SFPair ;
2227import org .junit .jupiter .api .Tag ;
2328import org .junit .jupiter .api .Test ;
2429
@@ -35,6 +40,34 @@ public class BindingDataLatestIT extends AbstractDriverIT {
3540 TimeZone australiaTz = TimeZone .getTimeZone ("Australia/Sydney" );
3641 Calendar tokyo = Calendar .getInstance (tokyoTz );
3742
43+ List <Time > times =
44+ Arrays .asList (
45+ Time .valueOf ("00:00:00" ),
46+ Time .valueOf ("11:59:59" ),
47+ Time .valueOf ("12:00:00" ),
48+ Time .valueOf ("12:34:56" ),
49+ Time .valueOf ("13:01:01" ),
50+ Time .valueOf ("15:30:00" ),
51+ Time .valueOf ("23:59:59" ));
52+
53+ List <SFPair <String , Date >> gregorianJulianDates =
54+ Arrays .asList (
55+ SFPair .of ("0001-01-01" , Date .valueOf ("0001-01-01" )),
56+ SFPair .of ("0100-03-01" , Date .valueOf ("0100-03-01" )),
57+ SFPair .of ("0400-02-29" , Date .valueOf ("0400-02-29" )),
58+ SFPair .of ("0400-03-01" , Date .valueOf ("0400-03-01" )),
59+ SFPair .of ("1400-03-01" , Date .valueOf ("1400-03-01" )),
60+ SFPair .of ("1582-10-15" , Date .valueOf ("1582-10-15" )),
61+ SFPair .of ("1900-02-28" , Date .valueOf ("1900-02-28" )),
62+ SFPair .of ("1900-03-01" , Date .valueOf ("1900-03-01" )),
63+ SFPair .of ("1969-12-31" , Date .valueOf ("1969-12-31" )),
64+ SFPair .of ("1970-01-01" , Date .valueOf ("1970-01-01" )),
65+ SFPair .of ("2000-02-28" , Date .valueOf ("2000-02-28" )),
66+ SFPair .of ("2000-02-29" , Date .valueOf ("2000-02-29" )),
67+ SFPair .of ("2000-03-01" , Date .valueOf ("2000-03-01" )),
68+ SFPair .of ("2023-10-26" , Date .valueOf ("2023-10-26" )),
69+ SFPair .of ("2024-02-29" , Date .valueOf ("2024-02-29" )));
70+
3871 @ Test
3972 public void testBindTimestampTZ () throws SQLException {
4073 try (Connection connection = getConnection ();
@@ -340,6 +373,196 @@ public void testTimestampLtzBindingNoLongerBreaksOtherDatetimeBindings() throws
340373 }
341374 }
342375
376+ @ Test
377+ public void testGregorianJulianConversions () throws SQLException {
378+ try (Connection connection = getConnection ();
379+ Statement statement = connection .createStatement ()) {
380+ statement .execute ("create or replace table stageinsertdates(ind int, d1 date)" );
381+
382+ try (PreparedStatement prepStatement =
383+ connection .prepareStatement ("insert into stageinsertdates values (?,?)" )) {
384+ for (int i = 0 ; i < gregorianJulianDates .size (); i ++) {
385+ prepStatement .setInt (1 , i );
386+ prepStatement .setDate (2 , gregorianJulianDates .get (i ).right );
387+ prepStatement .addBatch ();
388+ }
389+
390+ prepStatement .executeBatch ();
391+ prepStatement .getConnection ().commit ();
392+ }
393+
394+ try (ResultSet rs1 = statement .executeQuery ("select * from stageinsertdates order by ind" )) {
395+ for (int i = 0 ; i < gregorianJulianDates .size (); i ++) {
396+ assertTrue (rs1 .next ());
397+ assertEquals (i , rs1 .getInt (1 ));
398+ assertEquals (gregorianJulianDates .get (i ).left , rs1 .getDate (2 ).toLocalDate ().toString ());
399+ }
400+ }
401+ }
402+ }
403+
404+ /**
405+ * This test cannot run on the GitHub testing because of the "ALTER SESSION SET
406+ * CLIENT_STAGE_ARRAY_BINDING_THRESHOLD" This command should be executed with the system admin.
407+ *
408+ * @throws SQLException
409+ */
410+ @ Test
411+ @ DontRunOnGithubActions
412+ public void testGregorianJulianConversionsWithStageBindings () throws SQLException {
413+ try (Connection connection = getConnection ();
414+ Statement statement = connection .createStatement ()) {
415+ statement .execute ("create or replace table stageinsertdates(ind int, d1 date)" );
416+ statement .execute ("alter session set CLIENT_STAGE_ARRAY_BINDING_THRESHOLD = 1" );
417+
418+ try (PreparedStatement prepStatement =
419+ connection .prepareStatement ("insert into stageinsertdates values (?,?)" )) {
420+ for (int i = 0 ; i < gregorianJulianDates .size (); i ++) {
421+ prepStatement .setInt (1 , i );
422+ prepStatement .setDate (2 , gregorianJulianDates .get (i ).right );
423+ prepStatement .addBatch ();
424+ }
425+
426+ prepStatement .executeBatch ();
427+ prepStatement .getConnection ().commit ();
428+ }
429+
430+ try (ResultSet rs1 = statement .executeQuery ("select * from stageinsertdates order by ind" )) {
431+ for (int i = 0 ; i < gregorianJulianDates .size (); i ++) {
432+ assertTrue (rs1 .next ());
433+ assertEquals (i , rs1 .getInt (1 ));
434+ assertEquals (gregorianJulianDates .get (i ).left , rs1 .getDate (2 ).toLocalDate ().toString ());
435+ }
436+ }
437+ }
438+ }
439+
440+ @ Test
441+ public void testInsertTimeColumnAsWallClockTimeRegardlessOfTimezone () throws SQLException {
442+ TimeZone .setDefault (TimeZone .getTimeZone ("Pacific/Honolulu" ));
443+
444+ Properties props = new Properties ();
445+ props .put ("CLIENT_TREAT_TIME_AS_WALL_CLOCK_TIME" , true );
446+ try (Connection connection = getConnection (DONT_INJECT_SOCKET_TIMEOUT , props , false , false );
447+ Statement statement = connection .createStatement ()) {
448+ statement .execute ("create or replace table test_wall_clock_time(ind int, t1 time)" );
449+ statement .execute ("alter session set TIMEZONE='America/Los_Angeles';" );
450+
451+ try (PreparedStatement prepStatement =
452+ connection .prepareStatement ("insert into test_wall_clock_time values (?,?)" )) {
453+ for (int i = 0 ; i < times .size (); i ++) {
454+ prepStatement .setInt (1 , i );
455+ prepStatement .setTime (2 , times .get (i ));
456+ prepStatement .addBatch ();
457+ }
458+
459+ prepStatement .executeBatch ();
460+ prepStatement .getConnection ().commit ();
461+ }
462+
463+ try (ResultSet rs =
464+ statement .executeQuery ("select * from test_wall_clock_time order by ind" )) {
465+ for (int i = 0 ; i < times .size (); i ++) {
466+ assertTrue (rs .next ());
467+ assertEquals (i , rs .getInt (1 ));
468+ assertEquals (times .get (i ).toLocalTime (), rs .getTime (2 ).toLocalTime ());
469+ // check if inserted time is wall clock time
470+ assertEquals (times .get (i ).toString (), rs .getString (2 ));
471+ }
472+ }
473+ } finally {
474+ TimeZone .setDefault (origTz );
475+ }
476+ }
477+
478+ /**
479+ * This test cannot run on the GitHub testing because of the "ALTER SESSION SET
480+ * CLIENT_STAGE_ARRAY_BINDING_THRESHOLD" This command should be executed with the system admin.
481+ *
482+ * @throws SQLException
483+ */
484+ @ Test
485+ @ DontRunOnGithubActions
486+ public void testInsertTimeColumnAsWallClockTimeRegardlessOfTimezoneWithStageBinding ()
487+ throws SQLException {
488+ TimeZone .setDefault (TimeZone .getTimeZone ("Pacific/Honolulu" ));
489+
490+ Properties props = new Properties ();
491+ props .put ("CLIENT_TREAT_TIME_AS_WALL_CLOCK_TIME" , true );
492+ try (Connection connection = getConnection (DONT_INJECT_SOCKET_TIMEOUT , props , false , false );
493+ Statement statement = connection .createStatement ()) {
494+ statement .execute ("create or replace table test_wall_clock_time(ind int, t1 time)" );
495+ statement .execute ("alter session set TIMEZONE='America/Los_Angeles';" );
496+ statement .execute ("alter session set CLIENT_STAGE_ARRAY_BINDING_THRESHOLD = 1" );
497+
498+ try (PreparedStatement prepStatement =
499+ connection .prepareStatement ("insert into test_wall_clock_time values (?,?)" )) {
500+ for (int i = 0 ; i < times .size (); i ++) {
501+ prepStatement .setInt (1 , i );
502+ prepStatement .setTime (2 , times .get (i ));
503+ prepStatement .addBatch ();
504+ }
505+
506+ prepStatement .executeBatch ();
507+ prepStatement .getConnection ().commit ();
508+ }
509+
510+ try (ResultSet rs =
511+ statement .executeQuery ("select * from test_wall_clock_time order by ind" )) {
512+ for (int i = 0 ; i < times .size (); i ++) {
513+ assertTrue (rs .next ());
514+ assertEquals (i , rs .getInt (1 ));
515+ assertEquals (times .get (i ).toLocalTime (), rs .getTime (2 ).toLocalTime ());
516+ // check if inserted time is wall clock time
517+ assertEquals (times .get (i ).toString (), rs .getString (2 ));
518+ }
519+ }
520+ } finally {
521+ TimeZone .setDefault (origTz );
522+ }
523+ }
524+
525+ /***
526+ * Verifies that without enabling CLIENT_TREAT_TIME_AS_WALL_CLOCK_TIME time column gets shifted to UTC on insert
527+ *
528+ * @throws SQLException
529+ */
530+ @ Test
531+ public void testInsertTimeColumnNotAsWallClockTimeAsUtc () throws SQLException {
532+ TimeZone .setDefault (TimeZone .getTimeZone ("Pacific/Honolulu" ));
533+
534+ Properties props = new Properties ();
535+ props .put ("CLIENT_TREAT_TIME_AS_WALL_CLOCK_TIME" , false );
536+ try (Connection connection = getConnection (DONT_INJECT_SOCKET_TIMEOUT , props , false , false );
537+ Statement statement = connection .createStatement ()) {
538+ statement .execute ("create or replace table test_wall_clock_time(ind int, t1 time)" );
539+ statement .execute ("alter session set TIMEZONE='America/Los_Angeles';" );
540+
541+ String localTimeValue = "00:00:00" ;
542+ String utcTimeValue = "10:00:00" ; // 00:00 in Pacific/Honolulu is 10:00 UTC
543+ Time localTime = Time .valueOf (localTimeValue );
544+
545+ try (PreparedStatement prepStatement =
546+ connection .prepareStatement ("insert into test_wall_clock_time values (?,?)" )) {
547+ prepStatement .setInt (1 , 0 );
548+ prepStatement .setTime (2 , localTime );
549+ prepStatement .addBatch ();
550+ prepStatement .executeBatch ();
551+ prepStatement .getConnection ().commit ();
552+ }
553+
554+ try (ResultSet rs =
555+ statement .executeQuery ("select * from test_wall_clock_time order by ind" )) {
556+ assertTrue (rs .next ());
557+ assertEquals (0 , rs .getInt (1 ));
558+ // check if time gets shifted to UTC time on insert
559+ assertEquals (utcTimeValue , rs .getString (2 ));
560+ }
561+ } finally {
562+ TimeZone .setDefault (origTz );
563+ }
564+ }
565+
343566 public void executePsStatementForTimestampTest (
344567 Connection connection , String tableName , Timestamp timestamp ) throws SQLException {
345568 try (PreparedStatement prepStatement =
0 commit comments