content: /*
   * Copyright (c) 2020, 2022, Oracle and/or its affiliates.
   *
   * This program is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, version 2.0, as published by the
   * Free Software Foundation.
   *
   * This program is also distributed with certain software (including but not
   * limited to OpenSSL) that is licensed under separate terms, as designated in a
   * particular file or component or in included license documentation. The
   * authors of MySQL hereby grant you an additional permission to link the
   * program and your derivative works with the separately licensed software that
   * they have included with MySQL.
   *
   * Without limiting anything contained in the foregoing, this file, which is
   * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
   * version 1.0, a copy of which can be found at
   * http://oss.oracle.com/licenses/universal-foss-exception.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
   * for more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
   */
  
  package testsuite.simple;
  
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  
  import java.sql.Connection;
  import java.sql.Date;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.SQLType;
  import java.sql.Statement;
  import java.sql.Time;
  import java.sql.Timestamp;
  import java.time.Duration;
  import java.time.Instant;
  import java.time.LocalDate;
  import java.time.LocalDateTime;
  import java.time.LocalTime;
  import java.time.OffsetDateTime;
  import java.time.OffsetTime;
  import java.time.ZoneId;
  import java.time.ZoneOffset;
  import java.time.ZonedDateTime;
  import java.time.format.DateTimeFormatter;
  import java.time.temporal.ChronoUnit;
  import java.util.Calendar;
  import java.util.GregorianCalendar;
  import java.util.Hashtable;
  import java.util.Properties;
  import java.util.TimeZone;
  import java.util.concurrent.Callable;
  
  import org.junit.jupiter.api.Test;
  
  import com.mysql.cj.Messages;
  import com.mysql.cj.MysqlConnection;
  import com.mysql.cj.MysqlType;
  import com.mysql.cj.conf.PropertyKey;
  import com.mysql.cj.util.TimeUtil;
  
  import testsuite.BaseTestCase;
  
  public class DateTimeTest extends BaseTestCase {
  
      enum UseMethod {
          setObject, setDate, setTime, setTimestamp, getTimestamp, getObject
      };
  
      private static String tYear = "testSetObjectYear";
      private static String tDate = "testSetObjectDate";
      private static String tTime = "testSetObjectTime";
      private static String tDatetime = "testSetObjectDatetime";
      private static String tTimestamp = "testSetObjectTimestamp";
      private static String tVarchar = "testSetObjectVarchar";
  
      private static TimeZone tz_minus_10_00 = TimeZone.getTimeZone("GMT-10:00");
      //private static TimeZone tz_plus_01_00 = TimeZone.getTimeZone("Europe/Berlin");
      private static TimeZone tz_plus_02_00 = TimeZone.getTimeZone("Europe/Helsinki");
      private static TimeZone tz_plus_05_00 = TimeZone.getTimeZone("GMT+05:00");
      private static TimeZone tz_UTC = TimeZone.getTimeZone("UTC");
  
      TimeZone[] senderTimeZones = new TimeZone[] { tz_minus_10_00, tz_plus_05_00 }; //, tz_UTC, tz_plus_01_00
      String[] connectionTimeZones = new String[] { null, "LOCAL", "SERVER", "GMT+04:00" };
  
      private static LocalDate ld_19700101 = LocalDate.of(1970, 1, 1);
      private static LocalDate ld_20191231 = LocalDate.of(2019, 12, 31);
      private static LocalDate ld_20200101 = LocalDate.of(2020, 1, 1);
  
      private static LocalTime lt_120000 = LocalTime.of(12, 0, 0);
      private static LocalTime lt_120000_123456 = LocalTime.of(12, 0, 0, 123456000);
      private static LocalTime lt_000000 = LocalTime.of(0, 0, 0);
  
      private static LocalDateTime ldt_20191231_0000 = LocalDateTime.of(2019, 12, 31, 0, 0);
      private static LocalDateTime ldt_20200101_0000 = LocalDateTime.of(2020, 1, 1, 0, 0);
      private static LocalDateTime ldt_20200101_120000_123456 = LocalDateTime.of(2020, 1, 1, 12, 00, 00, 123456000);
  
      private static LocalDateTime ldt_19700101_0000 = LocalDateTime.of(1970, 1, 1, 0, 0);
      private static LocalDateTime ldt_19700101_120000_123456 = LocalDateTime.of(1970, 1, 1, 12, 0, 0, 123456000);
      private static LocalDateTime ldt_19700101_020000_123000 = LocalDateTime.of(1970, 1, 1, 2, 0, 0, 123000000);
      private static LocalDateTime ldt_19700101_020000_123456 = LocalDateTime.of(1970, 1, 1, 2, 0, 0, 123456000);
  
      private static String s_2020 = "2020";
      private static String s_20200101 = "2020-01-01";
      private static String s_20191231 = "2019-12-31";
  
      private static String s_1970 = "1970";
      private static String s_19700101 = "1970-01-01";
  
      private static String s_120000 = "12:00:00";
      private static String s_120000_123456 = "12:00:00.123456";
  
      private static String s_000000 = "00:00:00";
      private static String s_000000_000000 = "00:00:00.000000";
  
      private static String dataTruncatedErr = "Data truncated for column 'd' at row 1";
      private static String incorrectDateErr = "Data truncation: Incorrect date value: 'X' for column 'd' at row 1";
      private static String incorrectTimeErr = "Data truncation: Incorrect time value: 'X' for column 'd' at row 1";
      private static String incorrectDatetimeErr = "Data truncation: Incorrect datetime value: 'X' for column 'd' at row 1";
  
      private static DateTimeFormatter YEAR_FORMATTER = DateTimeFormatter.ofPattern("yyyy");
      private static DateTimeFormatter TIME_FORMATTER_WITH_MILLIS_NO_OFFCET = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
      private static DateTimeFormatter TIME_FORMATTER_WITH_MICROS_NO_OFFCET = DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSS");
      private static DateTimeFormatter DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");
  
      private Hashtable<String, Connection> tzConnections = new Hashtable<>();
      private Hashtable<String, Connection> utcConnections = new Hashtable<>();
  
      private static int id = 0;
  
      private String getKey(Properties props) {
          StringBuilder sb = new StringBuilder();
          sb.append(props.getProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName()));
          sb.append(props.getProperty(PropertyKey.preserveInstants.getKeyName()));
          sb.append(props.getProperty(PropertyKey.useServerPrepStmts.getKeyName()));
          sb.append(props.getProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName()));
          sb.append(props.getProperty(PropertyKey.sendFractionalSeconds.getKeyName()));
          return sb.toString();
      }
  
      private void initConnections(TimeZone senderTz, String connectionTZ) throws Exception {
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
  
          // applying 8.0 defaults to old servers
          String sqlMode = getMysqlVariable("sql_mode");
          if (!sqlMode.contains("NO_ZERO_DATE")) {
              sqlMode += ",NO_ZERO_DATE";
          }
          if (!sqlMode.contains("NO_ZERO_IN_DATE")) {
              sqlMode += ",NO_ZERO_IN_DATE";
          }
          props.setProperty(PropertyKey.sessionVariables.getKeyName(), "sql_mode='" + sqlMode + "'");
  
          for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
              for (boolean preserveInstants : new boolean[] { false, true }) {
                  for (boolean useSSPS : new boolean[] { false, true }) {
                      for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                          for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                              if (connectionTZ == null) {
                                  props.remove(PropertyKey.connectionTimeZone.getKeyName());
                              } else {
                                  props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                              }
  
                              props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                              props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                              props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                              props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                              props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                              String key = getKey(props);
                              final TimeZone origTz = TimeZone.getDefault();
                              try {
                                  TimeZone.setDefault(senderTz);
                                  this.tzConnections.put(key, getConnectionWithProps(timeZoneFreeDbUrl, props));
  
                                  TimeZone.setDefault(tz_UTC);
                                  this.utcConnections.put(key, getConnectionWithProps(timeZoneFreeDbUrl, props));
  
                              } finally {
                                  TimeZone.setDefault(origTz);
                              }
                          }
                      }
                  }
              }
          }
      }
  
      private void closeConnections() throws Exception {
          for (Connection c : this.tzConnections.values()) {
              c.close();
          }
          for (Connection c : this.utcConnections.values()) {
              c.close();
          }
          this.tzConnections.clear();
          this.utcConnections.clear();
      }
  
      @Test
      public void testSqlDateSetters() throws Exception {
  
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, "(id INT, d TIME)");
          createTable(tDatetime, "(id INT, d DATETIME)");
          createTable(tTimestamp, "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(32))");
  
          id = 0;
  
          Calendar cal_02 = GregorianCalendar.getInstance(tz_plus_02_00);
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          ZonedDateTime zdt_20200101_at_senderTz = ld_20200101.atStartOfDay().atZone(senderTz.toZoneId());
                                          ZonedDateTime zdt_20200101_at_calendarTz = zdt_20200101_at_senderTz.withZoneSameInstant(tz_plus_02_00.toZoneId());
                                          ZonedDateTime zdt_20200101_senderTz_to_connTz = preserveInstants
                                                  && !(connectionTZ == null || "LOCAL".equals(connectionTZ))
                                                          ? zdt_20200101_at_senderTz.withZoneSameInstant(connTz.toZoneId())
                                                          : zdt_20200101_at_senderTz;
  
                                          final java.sql.Date sqlDate_at_senderTz = new java.sql.Date(zdt_20200101_at_senderTz.toInstant().toEpochMilli());
  
                                          /* Unsupported conversions */
  
                                          assertThrows(props, tVarchar, sqlDate_at_senderTz, MysqlType.TIME, senderTz,
                                                  ".*Conversion from java.sql.Date to TIME is not supported.");
                                          assertThrows(props, tDate, sqlDate_at_senderTz, MysqlType.INT, senderTz,
                                                  ".*Conversion from java.sql.Date to INT is not supported.");
  
                                          /* Into YEAR field */
  
                                          String expYear = zdt_20200101_at_senderTz.toLocalDate().getYear() + "";
                                          String expYearCal = zdt_20200101_at_calendarTz.toLocalDate().getYear() + "";
                                          String expYearTs = zdt_20200101_senderTz_to_connTz.toLocalDate().getYear() + "";
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, sqlDate_at_senderTz, null, senderTz, expYearCal, null, UseMethod.setDate, cal_02);
                                              setObjectFromTz(props, tYear, sqlDate_at_senderTz, null, senderTz, expYear, null, UseMethod.setDate);
                                              setObjectFromTz(props, tYear, sqlDate_at_senderTz, null, senderTz, expYear);
                                              setObjectFromTz(props, tYear, sqlDate_at_senderTz, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, expYearTs);
                                          } else {
                                              assertThrows(props, tYear, sqlDate_at_senderTz, null, senderTz, null, null, UseMethod.setDate, cal_02,
                                                      dataTruncatedErr);
                                              assertThrows(props, tYear, sqlDate_at_senderTz, null, senderTz, null, null, UseMethod.setDate, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlDate_at_senderTz, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlDate_at_senderTz, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
                                          setObjectFromTz(props, tYear, sqlDate_at_senderTz, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          String expDate = zdt_20200101_at_senderTz.toLocalDate().toString();
                                          String expDateCal = zdt_20200101_at_calendarTz.toLocalDate().toString();
                                          String expDateTs = zdt_20200101_senderTz_to_connTz.toLocalDate().toString();
  
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, null, senderTz, expDateCal, null, UseMethod.setDate, cal_02);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, null, senderTz, expDate, null, UseMethod.setDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, null, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.LONGTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, expDateTs);
                                          assertThrows(props, tDate, sqlDate_at_senderTz, MysqlType.YEAR, senderTz, incorrectDateErr.replace("X", expYear));
  
                                          /* Into TIME field */
  
                                          String expTimeTs = zdt_20200101_senderTz_to_connTz.toLocalTime().toString();
  
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, sqlDate_at_senderTz, null, senderTz, s_000000, null, UseMethod.setDate, cal_02);
                                              setObjectFromTz(props, tTime, sqlDate_at_senderTz, null, senderTz, s_000000, null, UseMethod.setDate);
                                              setObjectFromTz(props, tTime, sqlDate_at_senderTz, null, senderTz, s_000000);
                                              setObjectFromTz(props, tTime, sqlDate_at_senderTz, MysqlType.DATE, senderTz, s_000000);
                                          } else {
                                              assertThrows(props, tTime, sqlDate_at_senderTz, null, senderTz, null, null, UseMethod.setDate, cal_02,
                                                      incorrectTimeErr.replace("X", expDateCal));
                                              assertThrows(props, tTime, sqlDate_at_senderTz, null, senderTz, null, null, UseMethod.setDate,
                                                      incorrectTimeErr.replace("X", expDate));
                                              assertThrows(props, tTime, sqlDate_at_senderTz, null, senderTz, incorrectTimeErr.replace("X", expDate));
                                              assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.DATE, senderTz, incorrectTimeErr.replace("X", expDate));
                                          }
                                          assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.CHAR, senderTz, incorrectTimeErr.replace("X", expDate));
                                          assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.VARCHAR, senderTz, incorrectTimeErr.replace("X", expDate));
                                          assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.TINYTEXT, senderTz, incorrectTimeErr.replace("X", expDate));
                                          assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.TEXT, senderTz, incorrectTimeErr.replace("X", expDate));
                                          assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.MEDIUMTEXT, senderTz, incorrectTimeErr.replace("X", expDate));
                                          assertThrows(props, tTime, sqlDate_at_senderTz, MysqlType.LONGTEXT, senderTz, incorrectTimeErr.replace("X", expDate));
                                          setObjectFromTz(props, tTime, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, s_000000);
                                          setObjectFromTz(props, tTime, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, expTimeTs);
                                          setObjectFromTz(props, tTime, sqlDate_at_senderTz, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, sqlDate_at_senderTz, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          String expDatetime = zdt_20200101_at_senderTz.toLocalDate().atStartOfDay()
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expDatetimeCal = zdt_20200101_at_calendarTz.toLocalDate().atStartOfDay()
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expDatetimeTs = zdt_20200101_senderTz_to_connTz.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
  
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, null, senderTz, expDatetimeCal, null, UseMethod.setDate, cal_02);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, null, senderTz, expDatetime, null, UseMethod.setDate);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, null, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.DATE, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, expDatetimeTs);
                                          assertThrows(props, tDatetime, sqlDate_at_senderTz, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          ZonedDateTime zdt_default_on_wire = zdt_20200101_at_senderTz.toLocalDate().atStartOfDay().atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_calendar_on_wire = zdt_20200101_at_calendarTz.toLocalDate().atStartOfDay()
                                                  .atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_ts_on_wire = zdt_20200101_senderTz_to_connTz.withZoneSameLocal(sessionTz.toZoneId());
  
                                          String expTimestamp = zdt_default_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).toLocalDateTime()
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTimestampWithCal = zdt_calendar_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).toLocalDateTime()
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTimestampTs = zdt_ts_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).toLocalDateTime()
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
  
                                          String expUnixTs = "" + zdt_default_on_wire.toInstant().getEpochSecond();
                                          String expUnixTsWithCal = "" + zdt_calendar_on_wire.toInstant().getEpochSecond();
                                          String expUnixTsTs = "" + zdt_ts_on_wire.toInstant().getEpochSecond();
  
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, null, senderTz, expTimestampWithCal, expUnixTsWithCal,
                                                  UseMethod.setDate, cal_02);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, null, senderTz, expTimestamp, expUnixTs, UseMethod.setDate);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, null, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.DATE, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.CHAR, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.VARCHAR, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.TINYTEXT, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.TEXT, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.MEDIUMTEXT, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.LONGTEXT, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, expTimestamp, expUnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, expTimestampTs, expUnixTsTs);
                                          assertThrows(props, tTimestamp, sqlDate_at_senderTz, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, null, senderTz, expDateCal, null, UseMethod.setDate, cal_02);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, null, senderTz, expDate, null, UseMethod.setDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, null, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.LONGTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.TIMESTAMP, senderTz, expDatetimeTs);
                                          setObjectFromTz(props, tVarchar, sqlDate_at_senderTz, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testSqlTimeSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Calendar cal_02 = GregorianCalendar.getInstance(tz_plus_02_00);
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          ZonedDateTime zdt_19700101_120000_123_at_senderTz = (withFract ? ldt_19700101_120000_123456
                                                  : ldt_19700101_120000_123456.withNano(0)).atZone(senderTz.toZoneId());
                                          ZonedDateTime zdt_19700101_120000_123_at_calendarTz = zdt_19700101_120000_123_at_senderTz
                                                  .withZoneSameInstant(tz_plus_02_00.toZoneId());
  
                                          ZonedDateTime zdt_19700101_on_wire = (sendFractionalSeconds && sendTimeFract ? zdt_19700101_120000_123_at_senderTz
                                                  : zdt_19700101_120000_123_at_senderTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_default_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(zdt_19700101_120000_123_at_senderTz.toLocalTime()).atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_calendar_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(zdt_19700101_120000_123_at_calendarTz.toLocalTime()).atZone(sessionTz.toZoneId());
  
                                          java.sql.Time sqlTime_120000 = new java.sql.Time(
                                                  zdt_19700101_120000_123_at_senderTz.toInstant().getEpochSecond() * 1000);
                                          java.sql.Time sqlTime_120000_123 = new java.sql.Time(zdt_19700101_120000_123_at_senderTz.toInstant().toEpochMilli());
  
                                          String expYear = "" + LocalDateTime.now(sessionTz.toZoneId()).toLocalDate().getYear(); // TODO server applies the time_zone, bug?
                                          String expDate = "" + LocalDateTime.now(sessionTz.toZoneId()).toLocalDate(); // TODO server applies the time_zone, bug?
  
                                          String expTimeNoMs = zdt_19700101_120000_123_at_senderTz.toLocalTime()
                                                  .format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTimeSendTimeFract = sendFractionalSeconds && sendTimeFract
                                                  ? zdt_19700101_120000_123_at_senderTz.toLocalTime()
                                                          .format(withFract ? TIME_FORMATTER_WITH_MILLIS_NO_OFFCET : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expTimeNoMs;
  
                                          String expDate8_0_28 = zdt_19700101_120000_123_at_senderTz.format(DateTimeFormatter.ofPattern("20HH-mm-ss"));
  
                                          String expTimeNoMsCal = zdt_19700101_120000_123_at_calendarTz.toLocalTime()
                                                  .format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTimeCal = sendFractionalSeconds && sendTimeFract
                                                  ? zdt_19700101_120000_123_at_calendarTz.toLocalTime()
                                                          .format(withFract ? TIME_FORMATTER_WITH_MILLIS_NO_OFFCET : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expTimeNoMsCal;
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, null, senderTz, expYear, null, UseMethod.setTime, cal_02);
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, null, senderTz, expYear, null, UseMethod.setTime);
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, null, senderTz, expYear);
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, MysqlType.DATE, senderTz, s_19700101);
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, MysqlType.TIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, MysqlType.DATETIME, senderTz, s_19700101);
                                              setObjectFromTz(props, tYear, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz, s_19700101);
                                          } else {
                                              assertThrows(props, tYear, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, cal_02,
                                                      dataTruncatedErr);
                                              assertThrows(props, tYear, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlTime_120000_123, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlTime_120000_123, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlTime_120000_123, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlTime_120000_123, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, sqlTime_120000_123, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlTime_120000_123, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlTime_120000_123, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlTime_120000_123, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlTime_120000_123, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, sqlTime_120000_123, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
                                          setObjectFromTz(props, tYear, sqlTime_120000_123, MysqlType.YEAR, senderTz, s_1970);
  
                                          /* Into DATE field */
  
                                          String expDateErr = incorrectDateErr.replace("X",
                                                  useSSPS && !(sendTimeFract && sendFractionalSeconds) && versionMeetsMinimum(8, 0, 28) ? expDate8_0_28
                                                          : expTimeSendTimeFract);
                                          String expDateErrWithCal = incorrectDateErr.replace("X", expTimeCal);
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, sqlTime_120000_123, null, senderTz, expDate, null, UseMethod.setTime, cal_02);
                                                  setObjectFromTz(props, tDate, sqlTime_120000_123, null, senderTz, expDate, null, UseMethod.setTime);
                                                  setObjectFromTz(props, tDate, sqlTime_120000_123, null, senderTz, expDate);
                                                  setObjectFromTz(props, tDate, sqlTime_120000_123, MysqlType.TIME, senderTz, expDate);
                                              } else {
                                                  assertThrows(props, tDate, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, cal_02,
                                                          dataTruncatedErr);
                                                  assertThrows(props, tDate, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, dataTruncatedErr);
                                                  assertThrows(props, tDate, sqlTime_120000_123, null, senderTz, dataTruncatedErr);
                                                  assertThrows(props, tDate, sqlTime_120000_123, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, cal_02,
                                                      expDateErrWithCal);
                                              assertThrows(props, tDate, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, expDateErr);
                                              assertThrows(props, tDate, sqlTime_120000_123, null, senderTz, expDateErr);
                                              assertThrows(props, tDate, sqlTime_120000_123, MysqlType.TIME, senderTz, expDateErr);
                                          }
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.CHAR, senderTz, expDateErr);
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.VARCHAR, senderTz, expDateErr);
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.TINYTEXT, senderTz, expDateErr);
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.TEXT, senderTz, expDateErr);
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.MEDIUMTEXT, senderTz, expDateErr);
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.LONGTEXT, senderTz, expDateErr);
  
                                          setObjectFromTz(props, tDate, sqlTime_120000_123, MysqlType.DATE, senderTz, s_19700101);
                                          setObjectFromTz(props, tDate, sqlTime_120000_123, MysqlType.DATETIME, senderTz, s_19700101);
                                          setObjectFromTz(props, tDate, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz, s_19700101);
                                          assertThrows(props, tDate, sqlTime_120000_123, MysqlType.YEAR, senderTz, incorrectDateErr.replace("X", s_1970));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, null, senderTz, expTimeCal, null, UseMethod.setTime, cal_02);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, null, senderTz, expTimeSendTimeFract, null, UseMethod.setTime);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, null, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000, MysqlType.CHAR, senderTz, s_120000);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.CHAR, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.VARCHAR, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.TINYTEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.TEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.MEDIUMTEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.LONGTEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000, MysqlType.TIME, senderTz, s_120000);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.TIME, senderTz, expTimeSendTimeFract);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.DATE, senderTz, s_000000);
                                          } else {
                                              assertThrows(props, tTime, sqlTime_120000_123, MysqlType.DATE, senderTz, incorrectTimeErr.replace("X", s_19700101));
                                          }
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.DATETIME, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tTime, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz, expTimeSendTimeFract);
                                          assertThrows(props, tTime, sqlTime_120000_123, MysqlType.YEAR, senderTz, incorrectTimeErr.replace("X", s_1970));
  
                                          /* Into DATETIME field */
  
                                          String expDatetime = expDate + " " + expTimeSendTimeFract;
                                          String expDatetimeWithCal = expDate + " " + expTimeCal;
                                          String expDatetimeErr = incorrectDatetimeErr.replace("X",
                                                  useSSPS && !(sendTimeFract && sendFractionalSeconds) && versionMeetsMinimum(8, 0, 28) ? expDate8_0_28
                                                          : expTimeSendTimeFract);
                                          String expDatetimeErrWithCal = incorrectDatetimeErr.replace("X", expTimeCal);
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, sqlTime_120000_123, null, senderTz, expDatetimeWithCal, null,
                                                          UseMethod.setTime, cal_02);
                                                  setObjectFromTz(props, tDatetime, sqlTime_120000_123, null, senderTz, expDatetime, null, UseMethod.setTime);
                                                  setObjectFromTz(props, tDatetime, sqlTime_120000_123, null, senderTz, expDatetime);
                                                  setObjectFromTz(props, tDatetime, sqlTime_120000_123, MysqlType.TIME, senderTz, expDatetime);
                                              } else {
                                                  assertThrows(props, tDatetime, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, cal_02,
                                                          dataTruncatedErr);
                                                  assertThrows(props, tDatetime, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime,
                                                          dataTruncatedErr);
                                                  assertThrows(props, tDatetime, sqlTime_120000_123, null, senderTz, dataTruncatedErr);
                                                  assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, cal_02,
                                                      expDatetimeErrWithCal);
                                              assertThrows(props, tDatetime, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, expDatetimeErr);
                                              assertThrows(props, tDatetime, sqlTime_120000_123, null, senderTz, expDatetimeErr);
                                              assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.TIME, senderTz, expDatetimeErr);
                                          }
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.CHAR, senderTz, expDatetimeErr);
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.VARCHAR, senderTz, expDatetimeErr);
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.TINYTEXT, senderTz, expDatetimeErr);
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.TEXT, senderTz, expDatetimeErr);
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.MEDIUMTEXT, senderTz, expDatetimeErr);
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.LONGTEXT, senderTz, expDatetimeErr);
  
                                          setObjectFromTz(props, tDatetime, sqlTime_120000_123, MysqlType.DATE, senderTz, s_19700101);
                                          setObjectFromTz(props, tDatetime, sqlTime_120000_123, MysqlType.DATETIME, senderTz,
                                                  s_19700101 + " " + expTimeSendTimeFract);
                                          setObjectFromTz(props, tDatetime, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz,
                                                  s_19700101 + " " + expTimeSendTimeFract);
                                          assertThrows(props, tDatetime, sqlTime_120000_123, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", s_1970));
  
                                          /* Into TIMESTAMP field */
  
                                          LocalDateTime ldt_exp1970Timestamp = zdt_19700101_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).toLocalDateTime();
                                          LocalDateTime ldt_expDefTimestamp = zdt_default_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).toLocalDateTime();
                                          LocalDateTime ldt_expCalTimestamp = zdt_calendar_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).toLocalDateTime();
                                          ZonedDateTime zdt_expDATETimestamp = ldt_19700101_0000.atZone(sessionTz.toZoneId())
                                                  .withZoneSameInstant(tz_UTC.toZoneId());
  
                                          DateTimeFormatter df = sendFractionalSeconds && sendTimeFract
                                                  ? (withFract ? TimeUtil.DATETIME_FORMATTER_WITH_MILLIS_NO_OFFSET
                                                          : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          String exp1970Timestamp = ldt_exp1970Timestamp.format(df);
                                          String expDefTimestamp = ldt_expDefTimestamp.format(df);
                                          String expDefTimestampNoMs = ldt_expDefTimestamp.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expCalTimestamp = ldt_expCalTimestamp.format(df);
                                          String expDATETimestamp = zdt_expDATETimestamp.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
  
                                          String expDefUnixTsNoMs = "" + zdt_default_on_wire.toEpochSecond();
                                          String expDefUnixTs = expDefUnixTsNoMs + (sendFractionalSeconds && sendTimeFract && zdt_default_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_default_on_wire.getNano(), 3)
                                                  : "");
                                          String expCalUnixTs = zdt_calendar_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && sendTimeFract && zdt_calendar_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_calendar_on_wire.getNano(), 3)
                                                          : "");
                                          String exp1970UnixTs = zdt_19700101_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && sendTimeFract && zdt_19700101_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_19700101_on_wire.getNano(), 3)
                                                          : "");
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, sqlTime_120000_123, null, senderTz, expCalTimestamp, expCalUnixTs,
                                                      UseMethod.setTime, cal_02);
                                              setObjectFromTz(props, tTimestamp, sqlTime_120000_123, null, senderTz, expDefTimestamp, expDefUnixTs,
                                                      UseMethod.setTime);
                                              setObjectFromTz(props, tTimestamp, sqlTime_120000_123, null, senderTz, expDefTimestamp, expDefUnixTs);
                                              setObjectFromTz(props, tTimestamp, sqlTime_120000, null, senderTz, expDefTimestampNoMs, expDefUnixTsNoMs);
                                              setObjectFromTz(props, tTimestamp, sqlTime_120000_123, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, sqlTime_120000_123, null, senderTz, expDatetimeErr);
                                              assertThrows(props, tTimestamp, sqlTime_120000, null, senderTz, incorrectDatetimeErr.replace("X", s_120000));
                                              assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.TIME, senderTz, expDatetimeErr);
                                              assertThrows(props, tTimestamp, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, expDatetimeErr);
                                              assertThrows(props, tTimestamp, sqlTime_120000_123, null, senderTz, null, null, UseMethod.setTime, cal_02,
                                                      expDatetimeErrWithCal);
                                          }
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.CHAR, senderTz, expDatetimeErr);
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.VARCHAR, senderTz, expDatetimeErr);
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.TINYTEXT, senderTz, expDatetimeErr);
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.TEXT, senderTz, expDatetimeErr);
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.MEDIUMTEXT, senderTz, expDatetimeErr);
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.LONGTEXT, senderTz, expDatetimeErr);
                                          if (zdt_expDATETimestamp.toEpochSecond() <= 0) {
                                              assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.DATE, senderTz,
                                                      incorrectDatetimeErr.replace("X", s_19700101));
                                          } else {
                                              setObjectFromTz(props, tTimestamp, sqlTime_120000_123, MysqlType.DATE, senderTz, expDATETimestamp,
                                                      "" + zdt_expDATETimestamp.toEpochSecond());
                                          }
                                          setObjectFromTz(props, tTimestamp, sqlTime_120000_123, MysqlType.DATETIME, senderTz, exp1970Timestamp, exp1970UnixTs);
                                          setObjectFromTz(props, tTimestamp, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz, exp1970Timestamp, exp1970UnixTs);
                                          assertThrows(props, tTimestamp, sqlTime_120000_123, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", s_1970));
  
                                          /* Into VARCHAR field */
  
                                          String expTime2 = useSSPS ? expTimeNoMs : expTimeSendTimeFract; // TODO milliseconds are ignored by server. Bug ?
                                          String expTimeCal2 = useSSPS ? expTimeNoMsCal : expTimeCal; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, null, senderTz, expTimeCal2, null, UseMethod.setTime, cal_02);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, null, senderTz, expTime2, null, UseMethod.setTime);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, null, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.CHAR, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.VARCHAR, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.TINYTEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.TEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.MEDIUMTEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.LONGTEXT, senderTz, expTimeSendTimeFract);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.DATE, senderTz, s_19700101);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.DATETIME, senderTz, s_19700101 + " " + expTime2);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.TIMESTAMP, senderTz, s_19700101 + " " + expTime2);
                                          setObjectFromTz(props, tVarchar, sqlTime_120000_123, MysqlType.YEAR, senderTz, s_1970);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testSqlTimestampSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          Calendar cal_02 = GregorianCalendar.getInstance(tz_plus_02_00);
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
  
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = sendFractionalSeconds
                                                  ? (withFract ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmtsendFractionalSeconds = sendFractionalSeconds
                                                  ? (withFract ? TIME_FORMATTER_WITH_MICROS_NO_OFFCET : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = (withFract ? ldt_20200101_120000_123456
                                                  : ldt_20200101_120000_123456.withNano(0)).atZone(senderTz.toZoneId());
                                          ZonedDateTime zdt_20200101_120000_123456_at_calendarTz = zdt_20200101_120000_123456_at_senderTz
                                                  .withZoneSameInstant(tz_plus_02_00.toZoneId());
  
                                          ZonedDateTime zdt_20200101_120000_123456_senderTz_to_connTz = preserveInstants
                                                  && !(connectionTZ == null || "LOCAL".equals(connectionTZ))
                                                          ? zdt_20200101_120000_123456_at_senderTz.withZoneSameInstant(connTz.toZoneId())
                                                          : zdt_20200101_120000_123456_at_senderTz;
  
                                          java.sql.Timestamp ts = java.sql.Timestamp.from(zdt_20200101_120000_123456_at_senderTz.toInstant());
  
                                          ZonedDateTime zdt_20200101_120000_123456_on_wire = zdt_20200101_120000_123456_at_senderTz
                                                  .withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(zdt_20200101_120000_123456_at_senderTz.toLocalTime()).atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_calendar_on_wire = zdt_20200101_120000_123456_at_calendarTz.withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_20200101_no_time_on_wire = zdt_20200101_120000_123456_on_wire.withHour(0).withMinute(0).withSecond(0)
                                                  .withNano(0);
  
                                          ZonedDateTime zdt_TS_on_wire = zdt_20200101_120000_123456_senderTz_to_connTz.withZoneSameLocal(sessionTz.toZoneId());
  
                                          String expYear = zdt_20200101_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearCal = zdt_calendar_on_wire.format(YEAR_FORMATTER);
                                          String expYearTS = zdt_TS_on_wire.format(YEAR_FORMATTER);
  
                                          String expDate = zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
                                          String expDateCal = zdt_calendar_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateTS = zdt_TS_on_wire.format(TimeUtil.DATE_FORMATTER);
  
                                          String expTimeNoMs = zdt_20200101_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_20200101_120000_123456_on_wire.format(timeFmtsendFractionalSeconds);
                                          String expTimeCal = zdt_calendar_on_wire.format(timeFmtsendFractionalSeconds);
                                          String expTimeTS = zdt_TS_on_wire.format(timeFmtsendFractionalSeconds);
  
                                          String expDatetime = zdt_20200101_120000_123456_on_wire.format(dateTimeFmt);
                                          String expDatetimeTS = zdt_TS_on_wire.format(dateTimeFmt);
                                          String expDatetimeDef = LocalDate.now(sessionTz.toZoneId()).atTime(zdt_20200101_120000_123456_on_wire.toLocalTime())
                                                  .atZone(sessionTz.toZoneId())
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDatetimeCal = zdt_calendar_on_wire.format(dateTimeFmt);
                                          String expDatetimeNoTime = zdt_20200101_120000_123456_on_wire
                                                  .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
  
                                          String expTimestamp = zdt_20200101_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampTS = zdt_TS_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expCalTimestamp = zdt_calendar_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampNoTime = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
  
                                          String expDefUnixTs = zdt_no_date_120000_123456_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expCalUnixTs = zdt_calendar_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_calendar_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_calendar_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTs = zdt_20200101_120000_123456_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_20200101_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTsTS = zdt_TS_on_wire.toEpochSecond() + (sendFractionalSeconds && zdt_TS_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_TS_on_wire.getNano(), 6)
                                                  : "");
                                          String expUnixTsFromDate = zdt_20200101_no_time_on_wire.toEpochSecond() + "";
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, ts, null, senderTz, expYearCal, null, UseMethod.setTimestamp, cal_02);
                                              setObjectFromTz(props, tYear, ts, null, senderTz, expYear, null, UseMethod.setTimestamp);
                                              setObjectFromTz(props, tYear, ts, null, senderTz, expYear);
                                              setObjectFromTz(props, tYear, ts, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, ts, MysqlType.TIME, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, ts, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, ts, MysqlType.TIMESTAMP, senderTz, expYearTS);
                                          } else {
                                              assertThrows(props, tYear, ts, null, senderTz, null, null, UseMethod.setTimestamp, cal_02, dataTruncatedErr);
                                              assertThrows(props, tYear, ts, null, senderTz, null, null, UseMethod.setTimestamp, dataTruncatedErr);
                                              assertThrows(props, tYear, ts, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ts, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ts, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ts, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ts, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, ts, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ts, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ts, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ts, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ts, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ts, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
                                          setObjectFromTz(props, tYear, ts, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, ts, null, senderTz, expDateCal, null, UseMethod.setTimestamp, cal_02);
                                          setObjectFromTz(props, tDate, ts, null, senderTz, expDateTS, null, UseMethod.setTimestamp);
                                          setObjectFromTz(props, tDate, ts, null, senderTz, expDateTS);
                                          setObjectFromTz(props, tDate, ts, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.LONGTEXT, senderTz, expDate);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, ts, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, ts, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, ts, MysqlType.TIME, senderTz, incorrectDateErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDate, ts, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ts, MysqlType.TIMESTAMP, senderTz, expDateTS);
                                          assertThrows(props, tDate, ts, MysqlType.YEAR, senderTz, incorrectDateErr.replace("X", s_2020));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, ts, null, senderTz, expTimeCal, null, UseMethod.setTimestamp, cal_02);
                                          setObjectFromTz(props, tTime, ts, null, senderTz, expTimeTS, null, UseMethod.setTimestamp);
                                          setObjectFromTz(props, tTime, ts, null, senderTz, expTimeTS);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, ts, MysqlType.DATE, senderTz, s_000000);
                                          } else {
                                              assertThrows(props, tTime, ts, MysqlType.DATE, senderTz, incorrectTimeErr.replace("X", expDate));
                                          }
                                          setObjectFromTz(props, tTime, ts, MysqlType.CHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.VARCHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.TINYTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.TEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.MEDIUMTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.LONGTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.TIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ts, MysqlType.TIMESTAMP, senderTz, expTimeTS);
                                          setObjectFromTz(props, tTime, ts, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, ts, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          setObjectFromTz(props, tDatetime, ts, null, senderTz, expDatetimeCal, null, UseMethod.setTimestamp, cal_02);
                                          setObjectFromTz(props, tDatetime, ts, null, senderTz, expDatetimeTS, null, UseMethod.setTimestamp);
                                          setObjectFromTz(props, tDatetime, ts, null, senderTz, expDatetimeTS);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, ts, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, ts, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, ts, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, ts, MysqlType.TIMESTAMP, senderTz, expDatetimeTS);
                                          assertThrows(props, tDatetime, ts, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          setObjectFromTz(props, tTimestamp, ts, null, senderTz, expCalTimestamp, expCalUnixTs, UseMethod.setTimestamp, cal_02);
                                          setObjectFromTz(props, tTimestamp, ts, null, senderTz, expTimestampTS, expFullUnixTsTS, UseMethod.setTimestamp);
                                          setObjectFromTz(props, tTimestamp, ts, null, senderTz, expTimestampTS, expFullUnixTsTS);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.DATE, senderTz, expTimestampNoTime, expUnixTsFromDate);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.CHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.VARCHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.TINYTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.TEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.MEDIUMTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.LONGTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, ts, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, ts, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.DATETIME, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, ts, MysqlType.TIMESTAMP, senderTz, expTimestampTS, expFullUnixTsTS);
                                          assertThrows(props, tTimestamp, ts, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          String expDatetimeCal2 = useSSPS ? zdt_calendar_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expDatetimeCal; // TODO milliseconds are ignored by server. Bug ?
                                          String expDatetime2 = useSSPS
                                                  ? zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expDatetime; // TODO milliseconds are ignored by server. Bug ?
                                          String expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
                                          String expDatetimeTS2 = useSSPS ? zdt_TS_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET) : expDatetimeTS; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, ts, null, senderTz, expDatetimeCal2, null, UseMethod.setTimestamp, cal_02);
                                          setObjectFromTz(props, tVarchar, ts, null, senderTz, expDatetimeTS2, null, UseMethod.setTimestamp);
                                          setObjectFromTz(props, tVarchar, ts, null, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.DATETIME, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.TIMESTAMP, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, ts, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testUtilCalendarSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          ZonedDateTime zdt_20200101_120000_123456_02_00 = ZonedDateTime.of(2020, 1, 1, 12, 00, 00, withFract ? 123456000 : 0, tz_plus_02_00.toZoneId());
          Calendar cal_02 = GregorianCalendar.from(zdt_20200101_120000_123456_02_00);
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = sendFractionalSeconds
                                                  ? (withFract ? TimeUtil.DATETIME_FORMATTER_WITH_MILLIS_NO_OFFSET
                                                          : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = sendFractionalSeconds
                                                  ? (withFract ? TIME_FORMATTER_WITH_MILLIS_NO_OFFCET : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = zdt_20200101_120000_123456_02_00
                                                  .withZoneSameInstant(senderTz.toZoneId());
  
                                          ZonedDateTime zdt_20200101_120000_123456_senderTz_to_connTz = preserveInstants
                                                  && !(connectionTZ == null || "LOCAL".equals(connectionTZ))
                                                          ? zdt_20200101_120000_123456_at_senderTz.withZoneSameInstant(connTz.toZoneId())
                                                          : zdt_20200101_120000_123456_at_senderTz;
  
                                          ZonedDateTime zdt_20200101_120000_123456_on_wire = zdt_20200101_120000_123456_at_senderTz
                                                  .withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_TS_on_wire = zdt_20200101_120000_123456_senderTz_to_connTz.withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(zdt_20200101_120000_123456_at_senderTz.toLocalTime()).atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_20200101_no_time_on_wire = zdt_20200101_120000_123456_on_wire.withHour(0).withMinute(0).withSecond(0)
                                                  .withNano(0);
  
                                          String expYear = zdt_20200101_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearTS = zdt_TS_on_wire.format(YEAR_FORMATTER);
  
                                          String expDate = zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire.format(useSSPS ? TimeUtil.DATE_FORMATTER
                                                  : DateTimeFormatter.ofPattern(zdt_no_date_120000_123456_on_wire.getHour() == 0 ? "00HH-mm-ss" : "20HH-mm-ss"));
                                          String expDateTS = zdt_TS_on_wire.format(TimeUtil.DATE_FORMATTER);
  
                                          String expTimeNoMs = zdt_20200101_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_20200101_120000_123456_on_wire.format(timeFmt);
                                          String expTimeTS = zdt_TS_on_wire.format(timeFmt);
  
                                          String expDatetime = zdt_20200101_120000_123456_on_wire.format(dateTimeFmt);
                                          String expDatetimeTS = zdt_TS_on_wire.format(dateTimeFmt);
                                          ZonedDateTime zdtDatetimeDef = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(zdt_20200101_120000_123456_on_wire.toLocalTime()).atZone(sessionTz.toZoneId());
                                          String expDatetimeDef = zdtDatetimeDef.format(useSSPS ? dateTimeFmt
                                                  : DateTimeFormatter.ofPattern(zdtDatetimeDef.getHour() == 0 ? "00HH-mm-ss 00:00:00" : "20HH-mm-ss 00:00:00"));
                                          String expDatetimeNoTime = zdt_20200101_120000_123456_on_wire
                                                  .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
  
                                          String expTimestamp = zdt_20200101_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampTS = zdt_TS_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          ZonedDateTime zdtDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId());
                                          String expDefTimestamp = zdtDefTimestamp.format(useSSPS ? dateTimeFmt
                                                  : DateTimeFormatter.ofPattern(zdtDatetimeDef.getHour() == 0 ? "00HH-mm-ss 00:00:00" : "20HH-mm-ss 00:00:00"));
                                          String expTimestampNoTime = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
  
                                          String expDefUnixTs = zdt_no_date_120000_123456_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 3)
                                                          : "");
                                          String expFullUnixTs = zdt_20200101_120000_123456_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_20200101_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_on_wire.getNano(), 3)
                                                          : "");
                                          String expFullUnixTsTS = zdt_TS_on_wire.toEpochSecond() + (sendFractionalSeconds && zdt_TS_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_TS_on_wire.getNano(), 3)
                                                  : "");
                                          String expUnixTsFromDate = zdt_20200101_no_time_on_wire.toEpochSecond() + "";
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, cal_02, null, senderTz, expYearTS);
                                              setObjectFromTz(props, tYear, cal_02, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, cal_02, MysqlType.TIME, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, cal_02, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, cal_02, MysqlType.TIMESTAMP, senderTz, expYearTS);
                                          } else {
                                              assertThrows(props, tYear, cal_02, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, cal_02, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, cal_02, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, cal_02, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, cal_02, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, cal_02, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, cal_02, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, cal_02, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, cal_02, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, cal_02, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, cal_02, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
                                          setObjectFromTz(props, tYear, cal_02, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, cal_02, null, senderTz, expDateTS);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.LONGTEXT, senderTz, expDate);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, cal_02, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, cal_02, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, cal_02, MysqlType.TIME, senderTz, incorrectDateErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, cal_02, MysqlType.TIMESTAMP, senderTz, expDateTS);
                                          assertThrows(props, tDate, cal_02, MysqlType.YEAR, senderTz, incorrectDateErr.replace("X", s_2020));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, cal_02, null, senderTz, expTimeTS);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, cal_02, MysqlType.DATE, senderTz, s_000000);
                                          } else {
                                              assertThrows(props, tTime, cal_02, MysqlType.DATE, senderTz, incorrectTimeErr.replace("X", expDate));
                                          }
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.CHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.VARCHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.TINYTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.TEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.MEDIUMTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.LONGTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.TIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.TIMESTAMP, senderTz, expTimeTS);
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, cal_02, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          setObjectFromTz(props, tDatetime, cal_02, null, senderTz, expDatetimeTS);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, cal_02, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, cal_02, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, cal_02, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, cal_02, MysqlType.TIMESTAMP, senderTz, expDatetimeTS);
                                          assertThrows(props, tDatetime, cal_02, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          setObjectFromTz(props, tTimestamp, cal_02, null, senderTz, expTimestampTS, expFullUnixTsTS);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.DATE, senderTz, expTimestampNoTime, expUnixTsFromDate);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.CHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.VARCHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.TINYTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.TEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.MEDIUMTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.LONGTEXT, senderTz, expTimestamp, expFullUnixTs);
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tTimestamp, cal_02, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                              } else {
                                                  assertThrows(props, tTimestamp, cal_02, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                              }
                                          } else {
                                              assertThrows(props, tTimestamp, cal_02, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
  
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.DATETIME, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, cal_02, MysqlType.TIMESTAMP, senderTz, expTimestampTS, expFullUnixTsTS);
                                          assertThrows(props, tTimestamp, cal_02, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          String expDatetime2 = useSSPS
                                                  ? zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expDatetime; // TODO milliseconds are ignored by server. Bug ?
                                          String expDatetimeTS2 = useSSPS ? zdt_TS_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET) : expDatetimeTS; // TODO milliseconds are ignored by server. Bug ?
                                          String expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, cal_02, null, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.DATETIME, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.TIMESTAMP, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, cal_02, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testUtilDateSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = sendFractionalSeconds && withFract ? TimeUtil.DATETIME_FORMATTER_WITH_MILLIS_NO_OFFSET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = sendFractionalSeconds && withFract ? TIME_FORMATTER_WITH_MILLIS_NO_OFFCET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = (withFract ? ldt_20200101_120000_123456
                                                  : ldt_20200101_120000_123456.withNano(0)).atZone(senderTz.toZoneId());
  
                                          java.util.Date utilDate = java.util.Date.from(zdt_20200101_120000_123456_at_senderTz.toInstant());
  
                                          ZonedDateTime zdt_20200101_120000_123456_senderTz_to_connTz = preserveInstants
                                                  && !(connectionTZ == null || "LOCAL".equals(connectionTZ))
                                                          ? zdt_20200101_120000_123456_at_senderTz.withZoneSameInstant(connTz.toZoneId())
                                                          : zdt_20200101_120000_123456_at_senderTz;
  
                                          ZonedDateTime zdt_20200101_120000_123456_on_wire = zdt_20200101_120000_123456_at_senderTz
                                                  .withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_TS_on_wire = zdt_20200101_120000_123456_senderTz_to_connTz.withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(zdt_20200101_120000_123456_at_senderTz.toLocalTime()).atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_20200101_no_time_on_wire = zdt_20200101_120000_123456_on_wire.withHour(0).withMinute(0).withSecond(0)
                                                  .withNano(0);
  
                                          String expYear = zdt_20200101_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearTS = zdt_TS_on_wire.format(YEAR_FORMATTER);
  
                                          String expDate = zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
                                          String expDateTS = zdt_TS_on_wire.format(TimeUtil.DATE_FORMATTER);
  
                                          String expTimeNoMs = zdt_20200101_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_20200101_120000_123456_on_wire.format(timeFmt);
                                          String expTimeTS = zdt_TS_on_wire.format(timeFmt);
  
                                          String expDatetime = zdt_20200101_120000_123456_on_wire.format(dateTimeFmt);
                                          String expDatetimeTS = zdt_TS_on_wire.format(dateTimeFmt);
                                          String expDatetimeDef = LocalDate.now(sessionTz.toZoneId()).atTime(zdt_20200101_120000_123456_on_wire.toLocalTime())
                                                  .atZone(sessionTz.toZoneId())
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDatetimeNoTime = zdt_20200101_120000_123456_on_wire
                                                  .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
  
                                          String expTimestamp = zdt_20200101_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampTS = zdt_TS_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampNoTime = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
  
                                          String expDefUnixTs = zdt_no_date_120000_123456_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 3)
                                                          : "");
                                          String expFullUnixTs = zdt_20200101_120000_123456_on_wire.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_20200101_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_on_wire.getNano(), 3)
                                                          : "");
                                          String expFullUnixTsTS = zdt_TS_on_wire.toEpochSecond() + (sendFractionalSeconds && zdt_TS_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_TS_on_wire.getNano(), 3)
                                                  : "");
                                          String expUnixTsFromDate = zdt_20200101_no_time_on_wire.toEpochSecond() + "";
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, utilDate, null, senderTz, expYearTS);
                                              setObjectFromTz(props, tYear, utilDate, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, utilDate, MysqlType.TIME, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, utilDate, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, utilDate, MysqlType.TIMESTAMP, senderTz, expYearTS);
                                          } else {
                                              assertThrows(props, tYear, utilDate, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, utilDate, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, utilDate, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, utilDate, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, utilDate, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, utilDate, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, utilDate, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, utilDate, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, utilDate, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, utilDate, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, utilDate, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
                                          setObjectFromTz(props, tYear, utilDate, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, utilDate, null, senderTz, expDateTS);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.LONGTEXT, senderTz, expDate);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, utilDate, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, utilDate, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, utilDate, MysqlType.TIME, senderTz, incorrectDateErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, utilDate, MysqlType.TIMESTAMP, senderTz, expDateTS);
                                          assertThrows(props, tDate, utilDate, MysqlType.YEAR, senderTz, incorrectDateErr.replace("X", expYear));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, utilDate, null, senderTz, expTimeTS);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, utilDate, MysqlType.DATE, senderTz, s_000000_000000);
                                          } else {
                                              assertThrows(props, tTime, utilDate, MysqlType.DATE, senderTz, incorrectTimeErr.replace("X", s_20200101));
                                          }
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.CHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.VARCHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.TINYTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.TEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.MEDIUMTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.LONGTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.TIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.TIMESTAMP, senderTz, expTimeTS);
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, utilDate, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          setObjectFromTz(props, tDatetime, utilDate, null, senderTz, expDatetimeTS);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, utilDate, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, utilDate, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, utilDate, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, utilDate, MysqlType.TIMESTAMP, senderTz, expDatetimeTS);
                                          assertThrows(props, tDatetime, utilDate, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          setObjectFromTz(props, tTimestamp, utilDate, null, senderTz, expTimestampTS, expFullUnixTsTS);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.DATE, senderTz, expTimestampNoTime, expUnixTsFromDate);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.CHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.VARCHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.TINYTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.TEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.MEDIUMTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.LONGTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, utilDate, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, utilDate, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.DATETIME, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, utilDate, MysqlType.TIMESTAMP, senderTz, expTimestampTS, expFullUnixTsTS);
                                          assertThrows(props, tTimestamp, utilDate, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          String expDatetime2 = useSSPS
                                                  ? zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expDatetime; // TODO milliseconds are ignored by server. Bug ?
                                          String expDatetimeTS2 = useSSPS ? zdt_TS_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET) : expDatetimeTS; // TODO milliseconds are ignored by server. Bug ?
                                          String expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, utilDate, null, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.DATETIME, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.TIMESTAMP, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tVarchar, utilDate, MysqlType.YEAR, senderTz, expYear);
  
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testLocalDateSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, "(id INT, d TIME)");
          createTable(tDatetime, "(id INT, d DATETIME)");
          createTable(tTimestamp, "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          ZonedDateTime zdt_20200101_no_time_on_wire = ld_20200101.atStartOfDay(sessionTz.toZoneId());
  
                                          String expYear = zdt_20200101_no_time_on_wire.format(YEAR_FORMATTER);
                                          String expDate = zdt_20200101_no_time_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expTime = zdt_20200101_no_time_on_wire.format(TIME_FORMATTER_WITH_MICROS_NO_OFFCET);
                                          String expDatetimeNoTime = zdt_20200101_no_time_on_wire.format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
                                          String expTimestamp = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId())
                                                  .format(DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET);
                                          String expectedUnixTs = zdt_20200101_no_time_on_wire.toEpochSecond() + (zdt_20200101_no_time_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_20200101_no_time_on_wire.getNano(), 3)
                                                  : "");
  
                                          String expDateErr = incorrectDateErr.replace("X", expYear);
                                          String expTimeErr = incorrectTimeErr.replace("X", expDate);
                                          String expDatetimeErr = incorrectDatetimeErr.replace("X", expYear);
  
                                          /* Unsupported conversions */
  
                                          assertThrows(props, tVarchar, ld_20200101, MysqlType.TIME, senderTz,
                                                  ".*Conversion from java.time.LocalDate to TIME is not supported.");
                                          assertThrows(props, tDate, ld_20200101, MysqlType.INT, senderTz,
                                                  ".*Conversion from java.time.LocalDate to INT is not supported.");
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
  
                                              setObjectFromTz(props, tYear, ld_20200101, null, senderTz, expYear);
                                              setObjectFromTz(props, tYear, ld_20200101, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, ld_20200101, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, ld_20200101, MysqlType.TIMESTAMP, senderTz, expYear);
                                          } else {
                                              assertThrows(props, tYear, ld_20200101, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ld_20200101, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ld_20200101, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ld_20200101, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, ld_20200101, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ld_20200101, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ld_20200101, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ld_20200101, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ld_20200101, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ld_20200101, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
                                          setObjectFromTz(props, tYear, ld_20200101, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, ld_20200101, null, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.LONGTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.DATETIME, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDate, ld_20200101, MysqlType.TIMESTAMP, senderTz, expDatetimeNoTime);
                                          assertThrows(props, tDate, ld_20200101, MysqlType.YEAR, senderTz, expDateErr);
  
                                          /* Into TIME field */
  
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, ld_20200101, null, senderTz, expTime);
                                              setObjectFromTz(props, tTime, ld_20200101, MysqlType.DATE, senderTz, expTime);
                                          } else {
                                              assertThrows(props, tTime, ld_20200101, null, senderTz, expTimeErr);
                                              assertThrows(props, tTime, ld_20200101, MysqlType.DATE, senderTz, expTimeErr);
                                          }
                                          assertThrows(props, tTime, ld_20200101, MysqlType.CHAR, senderTz, expTimeErr);
                                          assertThrows(props, tTime, ld_20200101, MysqlType.VARCHAR, senderTz, expTimeErr);
                                          assertThrows(props, tTime, ld_20200101, MysqlType.TINYTEXT, senderTz, expTimeErr);
                                          assertThrows(props, tTime, ld_20200101, MysqlType.TEXT, senderTz, expTimeErr);
                                          assertThrows(props, tTime, ld_20200101, MysqlType.MEDIUMTEXT, senderTz, expTimeErr);
                                          assertThrows(props, tTime, ld_20200101, MysqlType.LONGTEXT, senderTz, expTimeErr);
                                          setObjectFromTz(props, tTime, ld_20200101, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ld_20200101, MysqlType.TIMESTAMP, senderTz, expTime);
                                          setObjectFromTz(props, tTime, ld_20200101, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, ld_20200101, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          setObjectFromTz(props, tDatetime, ld_20200101, null, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.CHAR, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.VARCHAR, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.TINYTEXT, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.TEXT, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.MEDIUMTEXT, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.LONGTEXT, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.DATETIME, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, ld_20200101, MysqlType.TIMESTAMP, senderTz, expDatetimeNoTime);
                                          assertThrows(props, tDatetime, ld_20200101, MysqlType.YEAR, senderTz, expDatetimeErr);
  
                                          /* Into TIMESTAMP field */
  
                                          setObjectFromTz(props, tTimestamp, ld_20200101, null, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.DATE, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.CHAR, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.VARCHAR, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.TINYTEXT, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.TEXT, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.MEDIUMTEXT, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.LONGTEXT, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.DATETIME, senderTz, expTimestamp, expectedUnixTs);
                                          setObjectFromTz(props, tTimestamp, ld_20200101, MysqlType.TIMESTAMP, senderTz, expTimestamp, expectedUnixTs);
                                          assertThrows(props, tTimestamp, ld_20200101, MysqlType.YEAR, senderTz, expDatetimeErr);
  
                                          /* Into VARCHAR field */
  
                                          setObjectFromTz(props, tVarchar, ld_20200101, null, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.LONGTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.DATETIME, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.TIMESTAMP, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tVarchar, ld_20200101, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testLocalTimeSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = withFract ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = withFract && sendFractionalSeconds ? TIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmtForChars = withFract && sendFractionalSeconds ? TimeUtil.TIME_FORMATTER_WITH_NANOS_NO_OFFSET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          LocalTime orig_lt = withFract ? lt_120000_123456 : lt_120000;
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(sendFractionalSeconds ? orig_lt : orig_lt.withNano(0)).atZone(sessionTz.toZoneId());
  
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
                                          String expTimeNoMs = zdt_no_date_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime6 = zdt_no_date_120000_123456_on_wire.format(timeFmt);
                                          String expTime9 = zdt_no_date_120000_123456_on_wire.format(timeFmtForChars);
                                          String expTime8_0_28 = zdt_no_date_120000_123456_on_wire.format(DateTimeFormatter.ofPattern("20HH-mm-ss"));
  
                                          String expDatetimeDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestampNoMs = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId())
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
  
                                          String expDefUnixTsNoMs = zdt_no_date_120000_123456_on_wire.toEpochSecond() + "";
                                          String expDefUnixTs = expDefUnixTsNoMs + (zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 6)
                                                  : "");
  
                                          String expDateErr6 = incorrectDateErr.replace("X", expTime6);
  
                                          String expDateErr9 = incorrectDateErr.replace("X",
                                                  useSSPS && !sendFractionalSeconds && versionMeetsMinimum(8, 0, 28) ? expTime8_0_28 : expTime9);
  
                                          String expDatetimeErr6 = incorrectDatetimeErr.replace("X", expTime6);
                                          String expDatetimeErr9 = incorrectDatetimeErr.replace("X",
                                                  useSSPS && !sendFractionalSeconds && versionMeetsMinimum(8, 0, 28) ? expTime8_0_28 : expTime9);
  
                                          /* Unsupported conversions */
  
                                          assertThrows(props, tVarchar, orig_lt, MysqlType.DATE, senderTz,
                                                  ".*Conversion from java.time.LocalTime to DATE is not supported.");
                                          assertThrows(props, tVarchar, orig_lt, MysqlType.DATETIME, senderTz,
                                                  ".*Conversion from java.time.LocalTime to DATETIME is not supported.");
                                          assertThrows(props, tVarchar, orig_lt, MysqlType.TIMESTAMP, senderTz,
                                                  ".*Conversion from java.time.LocalTime to TIMESTAMP is not supported.");
                                          assertThrows(props, tVarchar, orig_lt, MysqlType.YEAR, senderTz,
                                                  ".*Conversion from java.time.LocalTime to YEAR is not supported.");
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, orig_lt, null, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, orig_lt, MysqlType.TIME, senderTz, expYearDef);
                                          } else {
                                              assertThrows(props, tYear, orig_lt, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, orig_lt, MysqlType.TIME, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, orig_lt, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_lt, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_lt, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_lt, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_lt, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_lt, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
  
                                          /* Into DATE field */
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, orig_lt, null, senderTz, expDateDef);
                                                  setObjectFromTz(props, tDate, orig_lt, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, orig_lt, null, senderTz, dataTruncatedErr);
                                                  assertThrows(props, tDate, orig_lt, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, orig_lt, null, senderTz, expDateErr6);
                                              assertThrows(props, tDate, orig_lt, MysqlType.TIME, senderTz, expDateErr6);
                                          }
                                          assertThrows(props, tDate, orig_lt, MysqlType.CHAR, senderTz, expDateErr9);
                                          assertThrows(props, tDate, orig_lt, MysqlType.VARCHAR, senderTz, expDateErr9);
                                          assertThrows(props, tDate, orig_lt, MysqlType.TINYTEXT, senderTz, expDateErr9);
                                          assertThrows(props, tDate, orig_lt, MysqlType.TEXT, senderTz, expDateErr9);
                                          assertThrows(props, tDate, orig_lt, MysqlType.MEDIUMTEXT, senderTz, expDateErr9);
                                          assertThrows(props, tDate, orig_lt, MysqlType.LONGTEXT, senderTz, expDateErr9);
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, orig_lt, null, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, lt_120000, MysqlType.CHAR, senderTz, expTimeNoMs);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.CHAR, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.VARCHAR, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.TINYTEXT, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.TEXT, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.MEDIUMTEXT, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.LONGTEXT, senderTz, expTime6);
                                          setObjectFromTz(props, tTime, lt_120000, MysqlType.TIME, senderTz, expTimeNoMs);
                                          setObjectFromTz(props, tTime, orig_lt, MysqlType.TIME, senderTz, expTime6);
  
                                          /* Into DATETIME field */
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, orig_lt, null, senderTz, expDatetimeDef);
                                                  setObjectFromTz(props, tDatetime, orig_lt, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, orig_lt, null, senderTz, dataTruncatedErr);
                                                  assertThrows(props, tDatetime, orig_lt, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, orig_lt, null, senderTz, expDatetimeErr6);
                                              assertThrows(props, tDatetime, orig_lt, MysqlType.TIME, senderTz, expDatetimeErr6);
                                          }
                                          assertThrows(props, tDatetime, orig_lt, MysqlType.CHAR, senderTz, expDatetimeErr9);
                                          assertThrows(props, tDatetime, orig_lt, MysqlType.VARCHAR, senderTz, expDatetimeErr9);
                                          assertThrows(props, tDatetime, orig_lt, MysqlType.TINYTEXT, senderTz, expDatetimeErr9);
                                          assertThrows(props, tDatetime, orig_lt, MysqlType.TEXT, senderTz, expDatetimeErr9);
                                          assertThrows(props, tDatetime, orig_lt, MysqlType.MEDIUMTEXT, senderTz, expDatetimeErr9);
                                          assertThrows(props, tDatetime, orig_lt, MysqlType.LONGTEXT, senderTz, expDatetimeErr9);
  
                                          /* Into TIMESTAMP field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, orig_lt, null, senderTz, expDefTimestamp, expDefUnixTs);
                                              setObjectFromTz(props, tTimestamp, lt_120000, null, senderTz, expDefTimestampNoMs, expDefUnixTsNoMs);
                                              setObjectFromTz(props, tTimestamp, orig_lt, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, orig_lt, null, senderTz, expDatetimeErr6);
                                              assertThrows(props, tTimestamp, lt_120000, null, senderTz, incorrectDatetimeErr.replace("X", expTimeNoMs));
                                              assertThrows(props, tTimestamp, orig_lt, MysqlType.TIME, senderTz, expDatetimeErr6);
                                          }
                                          assertThrows(props, tTimestamp, orig_lt, MysqlType.CHAR, senderTz, expDatetimeErr9);
                                          assertThrows(props, tTimestamp, orig_lt, MysqlType.VARCHAR, senderTz, expDatetimeErr9);
                                          assertThrows(props, tTimestamp, orig_lt, MysqlType.TINYTEXT, senderTz, expDatetimeErr9);
                                          assertThrows(props, tTimestamp, orig_lt, MysqlType.TEXT, senderTz, expDatetimeErr9);
                                          assertThrows(props, tTimestamp, orig_lt, MysqlType.MEDIUMTEXT, senderTz, expDatetimeErr9);
                                          assertThrows(props, tTimestamp, orig_lt, MysqlType.LONGTEXT, senderTz, expDatetimeErr9);
  
                                          /* Into VARCHAR field */
  
                                          String expTime2 = useSSPS ? expTimeNoMs : expTime6; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, orig_lt, null, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.CHAR, senderTz, expTime9);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.VARCHAR, senderTz, expTime9);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.TINYTEXT, senderTz, expTime9);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.TEXT, senderTz, expTime9);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.MEDIUMTEXT, senderTz, expTime9);
                                          setObjectFromTz(props, tVarchar, orig_lt, MysqlType.LONGTEXT, senderTz, expTime9);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testLocalDateTimeSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = withFract && sendFractionalSeconds ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter dateTimeFmtForChars = withFract && sendFractionalSeconds
                                                  ? TimeUtil.DATETIME_FORMATTER_WITH_NANOS_NO_OFFSET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = withFract && sendFractionalSeconds ? TIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          LocalDateTime orig_ldt = withFract ? ldt_20200101_120000_123456 : ldt_20200101_120000_123456.withNano(0);
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = orig_ldt.atZone(senderTz.toZoneId());
  
                                          ZonedDateTime zdt_20200101_120000_123456_on_wire = (sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                  : zdt_20200101_120000_123456_at_senderTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime((sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                          : zdt_20200101_120000_123456_at_senderTz.withNano(0)).toLocalTime())
                                                  .atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_20200101_no_time_on_wire = zdt_20200101_120000_123456_on_wire.withHour(0).withMinute(0).withSecond(0)
                                                  .withNano(0);
  
                                          String expYear = zdt_20200101_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
  
                                          String expDate = zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
  
                                          String expTimeNoMs = zdt_20200101_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_20200101_120000_123456_on_wire.format(timeFmt);
  
                                          String expDatetime = zdt_20200101_120000_123456_on_wire.format(dateTimeFmt);
                                          String expDatetimeChars = zdt_20200101_120000_123456_on_wire.format(dateTimeFmtForChars);
                                          String expDatetimeDef = LocalDate.now(sessionTz.toZoneId()).atTime(zdt_20200101_120000_123456_on_wire.toLocalTime())
                                                  .atZone(sessionTz.toZoneId())
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDatetimeNoTime = zdt_20200101_120000_123456_on_wire
                                                  .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
  
                                          String expTimestamp = zdt_20200101_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampNoTime = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
  
                                          String expDefUnixTs = zdt_no_date_120000_123456_on_wire.toEpochSecond()
                                                  + (zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTs = zdt_20200101_120000_123456_on_wire.toEpochSecond()
                                                  + (zdt_20200101_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expUnixTsFromDate = zdt_20200101_no_time_on_wire.toEpochSecond() + "";
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, orig_ldt, null, senderTz, expYear);
                                              setObjectFromTz(props, tYear, orig_ldt, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, orig_ldt, MysqlType.TIME, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, orig_ldt, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, orig_ldt, MysqlType.TIMESTAMP, senderTz, expYear);
                                          } else {
                                              assertThrows(props, tYear, orig_ldt, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, orig_ldt, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, orig_ldt, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, orig_ldt, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, orig_ldt, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, orig_ldt, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_ldt, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_ldt, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_ldt, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_ldt, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, orig_ldt, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
  
                                          setObjectFromTz(props, tYear, orig_ldt, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, orig_ldt, null, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.CHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.VARCHAR, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.TINYTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.TEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.MEDIUMTEXT, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.LONGTEXT, senderTz, expDate);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, orig_ldt, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, orig_ldt, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, orig_ldt, MysqlType.TIME, senderTz, incorrectDateErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, orig_ldt, MysqlType.TIMESTAMP, senderTz, expDate);
  
                                          assertThrows(props, tDate, orig_ldt, MysqlType.YEAR, senderTz, incorrectDateErr.replace("X", expYear));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, orig_ldt, null, senderTz, expTime);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, orig_ldt, MysqlType.DATE, senderTz, s_000000_000000);
                                          } else {
                                              assertThrows(props, tTime, orig_ldt, MysqlType.DATE, senderTz, incorrectTimeErr.replace("X", expDate));
                                          }
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.CHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.VARCHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.TINYTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.TEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.MEDIUMTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.LONGTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.TIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.TIMESTAMP, senderTz, expTime);
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, orig_ldt, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          setObjectFromTz(props, tDatetime, orig_ldt, null, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.CHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.VARCHAR, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.TINYTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.TEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.MEDIUMTEXT, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.LONGTEXT, senderTz, expDatetime);
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, orig_ldt, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, orig_ldt, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
  
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, orig_ldt, MysqlType.TIMESTAMP, senderTz, expDatetime);
  
                                          assertThrows(props, tDatetime, orig_ldt, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          setObjectFromTz(props, tTimestamp, orig_ldt, null, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.DATE, senderTz, expTimestampNoTime, expUnixTsFromDate);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.CHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.VARCHAR, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.TINYTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.TEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.MEDIUMTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.LONGTEXT, senderTz, expTimestamp, expFullUnixTs);
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, orig_ldt, MysqlType.TIME, senderTz, incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.DATETIME, senderTz, expTimestamp, expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, orig_ldt, MysqlType.TIMESTAMP, senderTz, expTimestamp, expFullUnixTs);
                                          assertThrows(props, tTimestamp, orig_ldt, MysqlType.YEAR, senderTz, incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          String expDatetime2 = useSSPS
                                                  ? zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : zdt_20200101_120000_123456_on_wire.format(dateTimeFmt); // TODO milliseconds are ignored by server. Bug ?
                                          String expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, orig_ldt, null, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.DATETIME, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.TIMESTAMP, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.CHAR, senderTz, expDatetimeChars);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.VARCHAR, senderTz, expDatetimeChars);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.TINYTEXT, senderTz, expDatetimeChars);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.TEXT, senderTz, expDatetimeChars);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.MEDIUMTEXT, senderTz, expDatetimeChars);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.LONGTEXT, senderTz, expDatetimeChars);
                                          setObjectFromTz(props, tVarchar, orig_ldt, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testOffsetTimeSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          OffsetTime ot_120000_123456_05_00 = OffsetTime.of(12, 00, 00, withFract ? 123456000 : 0, ZoneOffset.ofHours(5));
          OffsetTime ot_120000_05_00 = OffsetTime.of(12, 00, 00, 0, ZoneOffset.ofHours(5));
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = withFract && sendFractionalSeconds ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = withFract && sendFractionalSeconds ? TIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmtTz = withFract && sendFractionalSeconds ? TimeUtil.TIME_FORMATTER_WITH_NANOS_WITH_OFFSET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_WITH_OFFSET;
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime(ot_120000_123456_05_00.atDate(LocalDate.now()).atZoneSameInstant(senderTz.toZoneId()).toLocalTime())
                                                  .atZone(senderTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = (sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                  : zdt_20200101_120000_123456_at_senderTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
  
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
                                          String expTimeNoMs = zdt_no_date_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_no_date_120000_123456_on_wire.format(timeFmt);
                                          String expTimeTz = ot_120000_123456_05_00.format(timeFmtTz).replace("+", "\\+");
                                          String expTimeTz8_0_28 = ot_120000_123456_05_00.format(DateTimeFormatter.ofPattern("20HH-mm-ss X:00:00")).replace("+",
                                                  "");
                                          String expDatetimeTz8_0_28 = ot_120000_123456_05_00.format(DateTimeFormatter.ofPattern("20HH-mm-ss X:00:00.000000"))
                                                  .replace("+", "");
  
                                          String expDatetimeDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestampNoMs = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId())
                                                  .format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET);
  
                                          String expDefUnixTsNoMs = zdt_no_date_120000_123456_on_wire.toEpochSecond() + "";
                                          String expDefUnixTs = expDefUnixTsNoMs + (zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                  ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 6)
                                                  : "");
  
                                          String expDateErr = incorrectDateErr.replace("X", expTime);
                                          String expDateErrTz = incorrectDateErr.replace("X",
                                                  useSSPS && !sendFractionalSeconds && versionMeetsMinimum(8, 0, 28) ? expTimeTz8_0_28 : expTimeTz);
                                          String expTimeErrTz = incorrectTimeErr.replace("X", expTimeTz);
                                          String expDatetimeErr = incorrectDatetimeErr.replace("X", expTime);
                                          String expDatetimeErrTz = incorrectDatetimeErr.replace("X",
                                                  useSSPS && !sendFractionalSeconds && versionMeetsMinimum(8, 0, 28) ? expDatetimeTz8_0_28 : expTimeTz);
  
                                          /* Unsupported conversions */
  
                                          assertThrows(props, tVarchar, ot_120000_123456_05_00, MysqlType.DATE, senderTz,
                                                  ".*Conversion from java.time.OffsetTime to DATE is not supported.");
                                          assertThrows(props, tVarchar, ot_120000_123456_05_00, MysqlType.DATETIME, senderTz,
                                                  ".*Conversion from java.time.OffsetTime to DATETIME is not supported.");
                                          assertThrows(props, tVarchar, ot_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz,
                                                  ".*Conversion from java.time.OffsetTime to TIMESTAMP is not supported.");
                                          assertThrows(props, tVarchar, ot_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  ".*Conversion from java.time.OffsetTime to YEAR is not supported.");
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tYear, ot_120000_123456_05_00, null, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expYearDef);
                                          } else {
                                              assertThrows(props, tYear, ot_120000_123456_05_00, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, ot_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
  
                                          /* Into DATE field */
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, ot_120000_123456_05_00, null, senderTz, expDateDef);
                                                  setObjectFromTz(props, tDate, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, ot_120000_123456_05_00, null, senderTz, dataTruncatedErr);
                                                  assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                          versionMeetsMinimum(5, 6) ? expDateErr : dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, ot_120000_123456_05_00, null, senderTz, expDateErr);
                                              assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expDateErr);
                                          }
                                          assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.CHAR, senderTz, expDateErrTz);
                                          assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDateErrTz);
                                          assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDateErrTz);
                                          assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.TEXT, senderTz, expDateErrTz);
                                          assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDateErrTz);
                                          assertThrows(props, tDate, ot_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDateErrTz);
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, ot_120000_123456_05_00, null, senderTz, expTime);
                                          assertThrows(props, tTime, ot_120000_123456_05_00, MysqlType.CHAR, senderTz, expTimeErrTz);
                                          assertThrows(props, tTime, ot_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expTimeErrTz);
                                          assertThrows(props, tTime, ot_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expTimeErrTz);
                                          assertThrows(props, tTime, ot_120000_123456_05_00, MysqlType.TEXT, senderTz, expTimeErrTz);
                                          assertThrows(props, tTime, ot_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expTimeErrTz);
                                          assertThrows(props, tTime, ot_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expTimeErrTz);
                                          setObjectFromTz(props, tTime, ot_120000_05_00, MysqlType.TIME, senderTz, expTimeNoMs);
                                          setObjectFromTz(props, tTime, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expTime);
  
                                          /* Into DATETIME field */
  
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, ot_120000_123456_05_00, null, senderTz, expDatetimeDef);
                                                  setObjectFromTz(props, tDatetime, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, ot_120000_123456_05_00, null, senderTz, dataTruncatedErr);
                                                  assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, ot_120000_123456_05_00, null, senderTz, expDatetimeErr);
                                              assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expDatetimeErr);
                                          }
                                          assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.CHAR, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.TEXT, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tDatetime, ot_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDatetimeErrTz);
  
                                          /* Into TIMESTAMP field */
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, ot_120000_123456_05_00, null, senderTz, expDefTimestamp, expDefUnixTs);
                                              setObjectFromTz(props, tTimestamp, ot_120000_05_00, null, senderTz, expDefTimestampNoMs, expDefUnixTsNoMs);
                                              setObjectFromTz(props, tTimestamp, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expDefTimestamp, expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, ot_120000_123456_05_00, null, senderTz, expDatetimeErr);
                                              assertThrows(props, tTimestamp, ot_120000_05_00, null, senderTz, incorrectDatetimeErr.replace("X", expTimeNoMs));
                                              assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expDatetimeErr);
                                          }
                                          assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.CHAR, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.TEXT, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDatetimeErrTz);
                                          assertThrows(props, tTimestamp, ot_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDatetimeErrTz);
  
                                          /* Into VARCHAR field */
  
                                          String expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, null, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.CHAR, senderTz, expTimeTz);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expTimeTz);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expTimeTz);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.TEXT, senderTz, expTimeTz);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expTimeTz);
                                          setObjectFromTz(props, tVarchar, ot_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expTimeTz);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testOffsetDatetimeSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
          boolean allowsOffset = versionMeetsMinimum(8, 0, 19);
          // Starting from MySQL 8.0.22 server also converts string values in TIMESTAMP_WITH_TIMEZONE format to the session time zone
          // for column types other than TIMESTAMP and DATETIME. In MySQL 8.0.26 it was reverted, restored in MySQL 8.0.28.
          boolean serverConvertsTzForAllTypes = versionMeetsMinimum(8, 0, 22) && !versionMeetsMinimum(8, 0, 26) || versionMeetsMinimum(8, 0, 28);
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(35))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
              System.out.println("Local tz: " + TimeZone.getDefault());
              System.out.println("Server tz: " + serverTz);
          }
  
          OffsetDateTime odt_20200101_120000_123456_05_00 = OffsetDateTime.of(2020, 1, 1, 12, 00, 00, withFract ? 123456000 : 0, ZoneOffset.ofHours(5));
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = withFract && sendFractionalSeconds ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = withFract && sendFractionalSeconds ? TIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = odt_20200101_120000_123456_05_00
                                                  .atZoneSameInstant(senderTz.toZoneId());
  
                                          ZonedDateTime zdt_20200101_120000_123456_senderTz_to_connTz = preserveInstants
                                                  && !(connectionTZ == null || "LOCAL".equals(connectionTZ))
                                                          ? zdt_20200101_120000_123456_at_senderTz.withZoneSameInstant(connTz.toZoneId())
                                                          : zdt_20200101_120000_123456_at_senderTz;
  
                                          ZonedDateTime zdt_20200101_120000_123456_on_wire = (sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                  : zdt_20200101_120000_123456_at_senderTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime((sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                          : zdt_20200101_120000_123456_at_senderTz.withNano(0)).toLocalTime())
                                                  .atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_20200101_no_time_on_wire = zdt_20200101_120000_123456_on_wire.withHour(0).withMinute(0).withSecond(0)
                                                  .withNano(0);
  
                                          ZonedDateTime zdt_TS_on_wire = (sendFractionalSeconds ? zdt_20200101_120000_123456_senderTz_to_connTz
                                                  : zdt_20200101_120000_123456_senderTz_to_connTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
  
                                          String expYear = zdt_20200101_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearTS = zdt_TS_on_wire.format(YEAR_FORMATTER);
  
                                          String expDate = zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
  
                                          String expDateChar = serverConvertsTzForAllTypes
                                                  ? odt_20200101_120000_123456_05_00.atZoneSameInstant(sessionTz.toZoneId()).format(TimeUtil.DATE_FORMATTER)
                                                  : odt_20200101_120000_123456_05_00.format(TimeUtil.DATE_FORMATTER);
                                          String expDateTS = zdt_TS_on_wire.format(TimeUtil.DATE_FORMATTER);
  
                                          String expTimeNoMs = zdt_20200101_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_20200101_120000_123456_on_wire.format(timeFmt);
  
                                          String expTime2 = serverConvertsTzForAllTypes
                                                  ? odt_20200101_120000_123456_05_00.atZoneSameInstant(sessionTz.toZoneId()).format(timeFmt)
                                                  : odt_20200101_120000_123456_05_00.format(timeFmt);
                                          String expTimeTS = zdt_TS_on_wire.format(timeFmt);
  
                                          String expDatetime = zdt_20200101_120000_123456_on_wire.format(dateTimeFmt);
                                          String expDatetimeTS = zdt_TS_on_wire.format(dateTimeFmt);
                                          String expDatetimeDef = LocalDate.now(sessionTz.toZoneId()).atTime(zdt_20200101_120000_123456_on_wire.toLocalTime())
                                                  .atZone(sessionTz.toZoneId())
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDatetimeNoTime = zdt_20200101_120000_123456_on_wire
                                                  .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
                                          String expDatetimeExt = (sendFractionalSeconds ? odt_20200101_120000_123456_05_00
                                                  : odt_20200101_120000_123456_05_00.withNano(0)).atZoneSameInstant(sessionTz.toZoneId()).format(dateTimeFmt);
                                          String expDatetimeExtOff = (sendFractionalSeconds ? odt_20200101_120000_123456_05_00
                                                  : odt_20200101_120000_123456_05_00.withNano(0))
                                                          .format(withFract && sendFractionalSeconds ? TimeUtil.DATETIME_FORMATTER_WITH_NANOS_WITH_OFFSET
                                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_WITH_OFFSET);
  
                                          String expTimestamp = zdt_20200101_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampTS = zdt_TS_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampNoTime = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
  
                                          String expDefUnixTs = zdt_no_date_120000_123456_on_wire.toEpochSecond()
                                                  + (zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTs = zdt_20200101_120000_123456_on_wire.toEpochSecond()
                                                  + (zdt_20200101_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTsTS = zdt_TS_on_wire.toEpochSecond()
                                                  + (zdt_TS_on_wire.getNano() > 0 ? "." + TimeUtil.formatNanos(zdt_TS_on_wire.getNano(), 6) : "");
                                          String expUnixTsFromDate = zdt_20200101_no_time_on_wire.toEpochSecond() + "";
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
  
                                              setObjectFromTz(props, tYear, odt_20200101_120000_123456_05_00, null, senderTz, expYearTS);
                                              setObjectFromTz(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expYearTS);
                                          } else {
                                              assertThrows(props, tYear, odt_20200101_120000_123456_05_00, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
  
                                          setObjectFromTz(props, tYear, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, null, senderTz, expDateTS);
                                          setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expDate);
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDateChar);
                                          } else {
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
  
                                          }
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                      incorrectDateErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expDateTS);
  
                                          assertThrows(props, tDate, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  incorrectDateErr.replace("X", expYear));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, null, senderTz, expTimeTS);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, s_000000_000000);
                                          } else {
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz,
                                                      incorrectTimeErr.replace("X", expDate));
                                          }
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expTime2);
                                          } else {
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                          }
  
                                          setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expTimeTS);
                                          setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, null, senderTz, expDatetimeTS);
                                          setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDatetimeExt);
                                          } else {
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                          }
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                      incorrectDatetimeErr.replace("X", expTime));
                                          }
  
                                          setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expDatetimeTS);
  
                                          assertThrows(props, tDatetime, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          String expTimestampExt = (sendFractionalSeconds ? odt_20200101_120000_123456_05_00
                                                  : odt_20200101_120000_123456_05_00.withNano(0)).atZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expFullUnixTsExt = odt_20200101_120000_123456_05_00.toEpochSecond()
                                                  + (sendFractionalSeconds && odt_20200101_120000_123456_05_00.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(odt_20200101_120000_123456_05_00.getNano(), 6)
                                                          : "");
  
                                          setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, null, senderTz, expTimestampTS, expFullUnixTsTS);
                                          setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expTimestampNoTime,
                                                  expUnixTsFromDate);
  
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      expTimestampExt, expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                          } else {
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeExtOff.replace("+", "\\+")));
                                          }
  
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expDefTimestamp,
                                                      expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                      incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expTimestamp,
                                                  expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expTimestampTS,
                                                  expFullUnixTsTS);
                                          assertThrows(props, tTimestamp, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          String expDatetime2 = useSSPS
                                                  ? zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expDatetime; // TODO milliseconds are ignored by server. Bug ?
                                          String expDatetimeTS2 = useSSPS ? zdt_TS_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET) : expDatetimeTS; // TODO milliseconds are ignored by server. Bug ?
  
                                          expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, null, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expDatetimeExtOff);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDatetimeExtOff);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDatetimeExtOff);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expDatetimeExtOff);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDatetimeExtOff);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDatetimeExtOff);
                                          setObjectFromTz(props, tVarchar, odt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testZonedDatetimeSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
          boolean allowsOffset = versionMeetsMinimum(8, 0, 19);
          // Starting from MySQL 8.0.22 server also converts string values in TIMESTAMP_WITH_TIMEZONE format to the session time zone
          // for column types other than TIMESTAMP and DATETIME. In MySQL 8.0.26 it was reverted, restored in MySQL 8.0.28.
          boolean serverConvertsTzForAllTypes = versionMeetsMinimum(8, 0, 22) && !versionMeetsMinimum(8, 0, 26) || versionMeetsMinimum(8, 0, 28);
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(35))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
              System.out.println("Local tz: " + TimeZone.getDefault());
              System.out.println("Server tz: " + serverTz);
          }
  
          ZonedDateTime zdt_20200101_120000_123456_05_00 = ZonedDateTime.of(withFract ? ldt_20200101_120000_123456 : ldt_20200101_120000_123456.withNano(0),
                  ZoneId.of("Asia/Yekaterinburg"));
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          DateTimeFormatter dateTimeFmt = withFract && sendFractionalSeconds ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
                                          DateTimeFormatter timeFmt = withFract && sendFractionalSeconds ? TIME_FORMATTER_WITH_MICROS_NO_OFFCET
                                                  : TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = zdt_20200101_120000_123456_05_00
                                                  .withZoneSameInstant(senderTz.toZoneId());
  
                                          ZonedDateTime zdt_20200101_120000_123456_senderTz_to_connTz = preserveInstants
                                                  && !(connectionTZ == null || "LOCAL".equals(connectionTZ))
                                                          ? zdt_20200101_120000_123456_at_senderTz.withZoneSameInstant(connTz.toZoneId())
                                                          : zdt_20200101_120000_123456_at_senderTz;
  
                                          ZonedDateTime zdt_20200101_120000_123456_on_wire = (sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                  : zdt_20200101_120000_123456_at_senderTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
                                          ZonedDateTime zdt_no_date_120000_123456_on_wire = LocalDate.now(sessionTz.toZoneId())
                                                  .atTime((sendFractionalSeconds ? zdt_20200101_120000_123456_at_senderTz
                                                          : zdt_20200101_120000_123456_at_senderTz.withNano(0)).toLocalTime())
                                                  .atZone(sessionTz.toZoneId());
                                          ZonedDateTime zdt_20200101_no_time_on_wire = zdt_20200101_120000_123456_on_wire.withHour(0).withMinute(0).withSecond(0)
                                                  .withNano(0);
                                          ZonedDateTime zdt_TS_on_wire = (sendFractionalSeconds ? zdt_20200101_120000_123456_senderTz_to_connTz
                                                  : zdt_20200101_120000_123456_senderTz_to_connTz.withNano(0)).withZoneSameLocal(sessionTz.toZoneId());
  
                                          String expYear = zdt_20200101_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearDef = zdt_no_date_120000_123456_on_wire.format(YEAR_FORMATTER);
                                          String expYearTS = zdt_TS_on_wire.format(YEAR_FORMATTER);
  
                                          String expDate = zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATE_FORMATTER);
                                          String expDateDef = zdt_no_date_120000_123456_on_wire
                                                  .format(useSSPS ? TimeUtil.DATE_FORMATTER : DateTimeFormatter.ofPattern("20HH-mm-ss"));
                                          String expDateChar = serverConvertsTzForAllTypes
                                                  ? zdt_20200101_120000_123456_05_00.withZoneSameInstant(sessionTz.toZoneId()).format(TimeUtil.DATE_FORMATTER)
                                                  : zdt_20200101_120000_123456_05_00.format(TimeUtil.DATE_FORMATTER);
                                          String expDateTS = zdt_TS_on_wire.format(TimeUtil.DATE_FORMATTER);
  
                                          String expTimeNoMs = zdt_20200101_120000_123456_on_wire.format(TimeUtil.TIME_FORMATTER_NO_FRACT_NO_OFFSET);
                                          String expTime = zdt_20200101_120000_123456_on_wire.format(timeFmt);
                                          String expTime2 = serverConvertsTzForAllTypes
                                                  ? zdt_20200101_120000_123456_05_00.withZoneSameInstant(sessionTz.toZoneId()).format(timeFmt)
                                                  : zdt_20200101_120000_123456_05_00.format(timeFmt);
                                          String expTimeTS = zdt_TS_on_wire.format(timeFmt);
  
                                          String expDatetime = zdt_20200101_120000_123456_on_wire.format(dateTimeFmt);
                                          String expDatetimeTS = zdt_TS_on_wire.format(dateTimeFmt);
                                          String expDatetimeDef = LocalDate.now(sessionTz.toZoneId()).atTime(zdt_20200101_120000_123456_on_wire.toLocalTime())
                                                  .atZone(sessionTz.toZoneId())
                                                  .format(useSSPS ? dateTimeFmt : DateTimeFormatter.ofPattern("20HH-mm-ss 00:00:00"));
                                          String expDatetimeNoTime = zdt_20200101_120000_123456_on_wire
                                                  .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
                                          String expDatetimeOff = zdt_20200101_120000_123456_05_00
                                                  .format(withFract && sendFractionalSeconds ? TimeUtil.DATETIME_FORMATTER_WITH_NANOS_WITH_OFFSET
                                                          : TimeUtil.DATETIME_FORMATTER_NO_FRACT_WITH_OFFSET);
  
                                          String expTimestamp = zdt_20200101_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampTS = zdt_TS_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expDefTimestamp = zdt_no_date_120000_123456_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expTimestampNoTime = zdt_20200101_no_time_on_wire.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
  
                                          String expDefUnixTs = zdt_no_date_120000_123456_on_wire.toEpochSecond()
                                                  + (zdt_no_date_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_no_date_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTs = zdt_20200101_120000_123456_on_wire.toEpochSecond()
                                                  + (zdt_20200101_120000_123456_on_wire.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_on_wire.getNano(), 6)
                                                          : "");
                                          String expFullUnixTsTS = zdt_TS_on_wire.toEpochSecond()
                                                  + (zdt_TS_on_wire.getNano() > 0 ? "." + TimeUtil.formatNanos(zdt_TS_on_wire.getNano(), 6) : "");
                                          String expUnixTsFromDate = zdt_20200101_no_time_on_wire.toEpochSecond() + "";
  
                                          /* Into YEAR field */
  
                                          if (useSSPS && withFract) {
  
                                              setObjectFromTz(props, tYear, zdt_20200101_120000_123456_05_00, null, senderTz, expYearTS);
                                              setObjectFromTz(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expYear);
                                              setObjectFromTz(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expYearDef);
                                              setObjectFromTz(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expYear);
                                              setObjectFromTz(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expYearTS);
                                          } else {
                                              assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, null, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, dataTruncatedErr);
                                              assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, dataTruncatedErr);
                                          }
                                          assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, dataTruncatedErr);
                                          assertThrows(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, dataTruncatedErr);
  
                                          setObjectFromTz(props, tYear, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz, expYear);
  
                                          /* Into DATE field */
  
                                          setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, null, senderTz, expDateTS);
                                          setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expDate);
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDateChar);
                                              setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDateChar);
                                          } else {
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectDateErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                          }
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expDateDef);
                                              } else {
                                                  assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                      incorrectDateErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expDate);
                                          setObjectFromTz(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expDateTS);
  
                                          assertThrows(props, tDate, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  incorrectDateErr.replace("X", expYear));
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, null, senderTz, expTimeTS);
                                          if (useSSPS) {
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, s_000000_000000);
                                          } else {
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz,
                                                      incorrectTimeErr.replace("X", expDate));
                                          }
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expTime2);
                                              setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expTime2);
                                          } else {
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectTimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                          }
                                          setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expTime);
                                          setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expTimeTS);
                                          setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz, expYear); // TIME takes numbers as a short notation, thus it works here
                                          setObjectFromTz(props, tTime, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  "00:" + expYear.substring(0, 2) + ":" + expYear.substring(2)); // TIME takes numbers as a short notation, thus it works here
  
                                          /* Into DATETIME field */
  
                                          String expDatetimeExt = zdt_20200101_120000_123456_05_00.withZoneSameInstant(sessionTz.toZoneId()).format(dateTimeFmt);
  
                                          setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, null, senderTz, expDatetimeTS);
                                          setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expDatetimeNoTime);
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDatetimeExt);
                                              setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDatetimeExt);
                                          } else {
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                          }
                                          if (useSSPS) {
                                              if (withFract) {
                                                  setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expDatetimeDef);
                                              } else {
                                                  assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, dataTruncatedErr);
                                              }
                                          } else {
                                              assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                      incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expDatetime);
                                          setObjectFromTz(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expDatetimeTS);
                                          assertThrows(props, tDatetime, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into TIMESTAMP field */
  
                                          String expTimestampExt = zdt_20200101_120000_123456_05_00.withZoneSameInstant(tz_UTC.toZoneId()).format(dateTimeFmt);
                                          String expFullUnixTsExt = zdt_20200101_120000_123456_05_00.toEpochSecond()
                                                  + (sendFractionalSeconds && zdt_20200101_120000_123456_05_00.getNano() > 0
                                                          ? "." + TimeUtil.formatNanos(zdt_20200101_120000_123456_05_00.getNano(), 6)
                                                          : "");
  
                                          setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, null, senderTz, expTimestampTS, expFullUnixTsTS);
                                          setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expTimestampNoTime,
                                                  expUnixTsFromDate);
                                          if (allowsOffset) {
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      expTimestampExt, expFullUnixTsExt);
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expTimestampExt,
                                                      expFullUnixTsExt);
                                          } else {
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz,
                                                      incorrectDatetimeErr.replace("X", expDatetimeOff.replace("+", "\\+")));
                                          }
                                          if (useSSPS && withFract) {
                                              setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expDefTimestamp,
                                                      expDefUnixTs);
                                          } else {
                                              assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz,
                                                      incorrectDatetimeErr.replace("X", expTime));
                                          }
                                          setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expTimestamp,
                                                  expFullUnixTs);
                                          setObjectFromTz(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expTimestampTS,
                                                  expFullUnixTsTS);
                                          assertThrows(props, tTimestamp, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz,
                                                  incorrectDatetimeErr.replace("X", expYear));
  
                                          /* Into VARCHAR field */
  
                                          String expDatetime2 = useSSPS
                                                  ? zdt_20200101_120000_123456_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET)
                                                  : expDatetime; // TODO milliseconds are ignored by server. Bug ?
                                          String expDatetimeTS2 = useSSPS ? zdt_TS_on_wire.format(TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET) : expDatetimeTS; // TODO milliseconds are ignored by server. Bug ?
  
                                          expTime2 = useSSPS ? expTimeNoMs : expTime; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, null, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.DATETIME, senderTz, expDatetime2);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.TIMESTAMP, senderTz, expDatetimeTS2);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.TIME, senderTz, expTime2);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.DATE, senderTz, expDate);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.CHAR, senderTz, expDatetimeOff);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.VARCHAR, senderTz, expDatetimeOff);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.TINYTEXT, senderTz, expDatetimeOff);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.TEXT, senderTz, expDatetimeOff);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.MEDIUMTEXT, senderTz, expDatetimeOff);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.LONGTEXT, senderTz, expDatetimeOff);
                                          setObjectFromTz(props, tVarchar, zdt_20200101_120000_123456_05_00, MysqlType.YEAR, senderTz, expYear);
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      @Test
      public void testDurationSetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tYear, "(id INT, d YEAR)");
          createTable(tDate, "(id INT, d DATE)");
          createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)");
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          Duration dur_no_fract = Duration.parse("PT300H10M20S");
                                          Duration dur_with_fract = Duration.parse("PT300H10M20.123S");
                                          Duration dur = withFract ? dur_with_fract : dur_no_fract;
                                          String expDur = TimeUtil.getDurationString(sendFractionalSeconds ? dur : dur_no_fract);
                                          String expTime = TimeUtil.getDurationString(dur);
  
                                          Duration neg_dur_no_fract = Duration.parse("-PT300H10M20S");
                                          Duration neg_dur = withFract ? Duration.parse("-PT300H10M20.123S") : neg_dur_no_fract;
                                          String expNegDur = TimeUtil.getDurationString(sendFractionalSeconds ? neg_dur : neg_dur_no_fract);
                                          String expNegTime = TimeUtil.getDurationString(neg_dur);
  
                                          /* Unsupported conversions */
  
                                          assertThrows(props, tVarchar, dur, MysqlType.DATE, senderTz,
                                                  ".*Conversion from java.time.Duration to DATE is not supported.");
                                          assertThrows(props, tVarchar, dur, MysqlType.DATETIME, senderTz,
                                                  ".*Conversion from java.time.Duration to DATETIME is not supported.");
                                          assertThrows(props, tVarchar, dur, MysqlType.TIMESTAMP, senderTz,
                                                  ".*Conversion from java.time.Duration to TIMESTAMP is not supported.");
                                          assertThrows(props, tVarchar, dur, MysqlType.YEAR, senderTz,
                                                  ".*Conversion from java.time.Duration to YEAR is not supported.");
  
                                          /* Into TIME field */
  
                                          setObjectFromTz(props, tTime, dur, null, senderTz, expDur);
                                          setObjectFromTz(props, tTime, neg_dur, null, senderTz, expNegDur);
                                          setObjectFromTz(props, tTime, dur, MysqlType.TIME, senderTz, expDur);
                                          setObjectFromTz(props, tTime, neg_dur, MysqlType.TIME, senderTz, expNegDur);
                                          setObjectFromTz(props, tTime, dur, MysqlType.CHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, neg_dur, MysqlType.CHAR, senderTz, expNegTime);
                                          setObjectFromTz(props, tTime, dur, MysqlType.VARCHAR, senderTz, expTime);
                                          setObjectFromTz(props, tTime, dur, MysqlType.TINYTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, dur, MysqlType.TEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, dur, MysqlType.MEDIUMTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tTime, dur, MysqlType.LONGTEXT, senderTz, expTime);
  
                                          /* Into VARCHAR field */
  
                                          String expChar = expDur + (withFract && sendFractionalSeconds && useSSPS ? "000" : "");
                                          String expChar2 = useSSPS ? TimeUtil.getDurationString(dur_no_fract) : expChar; // TODO milliseconds are ignored by server. Bug ?
                                          String expNegChar = expNegDur + (withFract && sendFractionalSeconds && useSSPS ? "000" : "");
                                          String expNegChar2 = useSSPS ? TimeUtil.getDurationString(neg_dur_no_fract) : expNegChar; // TODO milliseconds are ignored by server. Bug ?
  
                                          setObjectFromTz(props, tVarchar, dur, null, senderTz, expChar2);
                                          setObjectFromTz(props, tVarchar, neg_dur, null, senderTz, expNegChar2);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.TIME, senderTz, expChar2);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.TIME, senderTz, expNegChar2);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.CHAR, senderTz, expTime);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.CHAR, senderTz, expNegTime);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.VARCHAR, senderTz, expTime);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.VARCHAR, senderTz, expNegTime);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.TINYTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.TINYTEXT, senderTz, expNegTime);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.TEXT, senderTz, expTime);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.TEXT, senderTz, expNegTime);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.MEDIUMTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.MEDIUMTEXT, senderTz, expNegTime);
                                          setObjectFromTz(props, tVarchar, dur, MysqlType.LONGTEXT, senderTz, expTime);
                                          setObjectFromTz(props, tVarchar, neg_dur, MysqlType.LONGTEXT, senderTz, expNegTime);
  
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      void assertThrows(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String err) throws Exception {
          assertThrows(SQLException.class, err, new Callable<Void>() {
              public Void call() throws Exception {
                  setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz);
                  return null;
              }
          });
      }
  
      void assertThrows(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String expectedValue,
              String expectedUnixTimestamp, UseMethod useMethod, String err) throws Exception {
          assertThrows(SQLException.class, err, new Callable<Void>() {
              public Void call() throws Exception {
                  setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz, expectedValue, expectedUnixTimestamp, useMethod, null);
                  return null;
              }
          });
      }
  
      void assertThrows(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String expectedValue,
              String expectedUnixTimestamp, UseMethod useMethod, Calendar calendar, String err) throws Exception {
          assertThrows(SQLException.class, err, new Callable<Void>() {
              public Void call() throws Exception {
                  setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz, expectedValue, expectedUnixTimestamp, useMethod, calendar);
                  return null;
              }
          });
      }
  
      void setObjectFromTz(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz) throws Exception {
          setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz, null, null, UseMethod.setObject, null);
      }
  
      void setObjectFromTz(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String expectedUTCValue)
              throws Exception {
          setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz, expectedUTCValue, null, UseMethod.setObject, null);
      }
  
      void setObjectFromTz(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String expectedUTCValue,
              String expectedUnixTimestamp) throws Exception {
          setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz, expectedUTCValue, expectedUnixTimestamp, UseMethod.setObject, null);
      }
  
      void setObjectFromTz(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String expectedUTCValue,
              String expectedUnixTimestamp, UseMethod useMethod) throws Exception {
          setObjectFromTz(props, tableName, parameter, targetSqlType, senderTz, expectedUTCValue, expectedUnixTimestamp, useMethod, null);
      }
  
      void setObjectFromTz(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String expectedUTCValue,
              String expectedUnixTimestamp, UseMethod useMethod, Calendar calendar) throws Exception {
          if (props == null) {
              props = new Properties();
          }
          final TimeZone origTz = TimeZone.getDefault();
          try {
              TimeZone.setDefault(senderTz);
              Connection testConn = this.tzConnections.get(getKey(props));
  
              id++;
  
              PreparedStatement localPstmt = testConn.prepareStatement("INSERT INTO " + tableName + " VALUES (?, ?)");
              localPstmt.setInt(1, id);
              switch (useMethod) {
                  case setDate:
                      if (calendar == null) {
                          localPstmt.setDate(2, (Date) parameter);
                      } else {
                          localPstmt.setDate(2, (Date) parameter, calendar);
                      }
                      break;
  
                  case setTime:
                      if (calendar == null) {
                          localPstmt.setTime(2, (Time) parameter);
                      } else {
                          localPstmt.setTime(2, (Time) parameter, calendar);
                      }
                      break;
  
                  case setTimestamp:
                      if (calendar == null) {
                          localPstmt.setTimestamp(2, (Timestamp) parameter);
                      } else {
                          localPstmt.setTimestamp(2, (Timestamp) parameter, calendar);
                      }
                      break;
  
                  default:
                      if (targetSqlType == null) {
                          localPstmt.setObject(2, parameter);
                      } else {
                          localPstmt.setObject(2, parameter, targetSqlType);
                      }
                      break;
              }
              assertEquals(1, localPstmt.executeUpdate());
              localPstmt.close();
  
              if (expectedUTCValue != null) {
                  TimeZone.setDefault(tz_UTC);
                  testConn = this.utcConnections.get(getKey(props));
                  Statement localStmt = testConn.createStatement();
                  localStmt.execute("set @@time_zone='+00:00'");
                  String sql = "SELECT * FROM " + tableName + " WHERE id = " + id + " AND d = '" + expectedUTCValue + "'"
                          + (expectedUnixTimestamp != null ? " AND unix_timestamp(d) = " + expectedUnixTimestamp : "");
                  ResultSet localRs = localStmt.executeQuery(sql);
                  assertTrue(localRs.next(), parameter + "\n" + targetSqlType + "\n" + senderTz + "\n" + useMethod + "\n" + sql);
                  localStmt.close();
              }
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  
      @Test
      public void testDateGetters() throws Exception {
          createTable(tDate, "(d DATE)");
          this.stmt.executeUpdate("INSERT INTO " + tDate + " VALUES ('2020-01-01'), ('2019-12-31')");
  
          Calendar cal_05 = GregorianCalendar.getInstance(tz_plus_05_00);
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
  
          final TimeZone origTz = TimeZone.getDefault();
          try {
              for (TimeZone tz : this.senderTimeZones) {
                  for (String connectionTZ : this.connectionTimeZones) {
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone.setDefault(tz);
                                          ZoneId currZoneId = ZoneId.of(tz.getID());
  
                                          Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props);
                                          this.rs = testConn.createStatement().executeQuery("select * from " + tDate);
  
                                          assertTrue(this.rs.next());
                                          Instant exp_instant_tz = ldt_20200101_0000.atZone(currZoneId).toInstant();
                                          Instant exp_instant_05 = ldt_20200101_0000.atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                          Calendar exp_cal = GregorianCalendar.getInstance(tz);
                                          exp_cal.set(2020, 0, 1, 0, 0, 0);
                                          exp_cal.set(Calendar.MILLISECOND, 0);
                                          exp_cal.setLenient(false);
  
                                          assertEquals(java.sql.Date.from(exp_instant_tz), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.from(exp_instant_05), this.rs.getDate(1, cal_05));
                                          assertEquals(java.sql.Time.valueOf(lt_000000), this.rs.getTime(1));
                                          assertEquals(java.sql.Time.from(lt_000000.atOffset(ZoneOffset.ofHours(5)).atDate(ld_19700101).toInstant()),
                                                  this.rs.getTime(1, cal_05));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_tz), this.rs.getTimestamp(1));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_05), this.rs.getTimestamp(1, cal_05));
                                          assertEquals(java.sql.Date.from(exp_instant_tz), this.rs.getObject(1));
                                          assertEquals(java.sql.Date.from(exp_instant_tz), this.rs.getObject(1, java.sql.Date.class));
                                          assertEquals(java.sql.Time.valueOf(lt_000000), this.rs.getObject(1, java.sql.Time.class));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_tz), this.rs.getObject(1, java.sql.Timestamp.class));
                                          assertEquals(java.util.Date.from(exp_instant_tz), this.rs.getObject(1, java.util.Date.class));
                                          assertEquals(exp_cal, this.rs.getObject(1, java.util.Calendar.class));
                                          assertEquals(ld_20200101, this.rs.getObject(1, LocalDate.class));
                                          assertEquals(lt_000000, this.rs.getObject(1, LocalTime.class));
                                          assertEquals(ldt_20200101_0000, this.rs.getObject(1, LocalDateTime.class));
                                          assertEquals(ldt_20200101_0000.atZone(currZoneId).toOffsetDateTime().toOffsetTime(),
                                                  this.rs.getObject(1, OffsetTime.class));
                                          assertEquals(ldt_20200101_0000.atZone(currZoneId).toOffsetDateTime(), this.rs.getObject(1, OffsetDateTime.class));
                                          assertEquals(ldt_20200101_0000.atZone(currZoneId), this.rs.getObject(1, ZonedDateTime.class));
                                          assertEquals(s_20200101, this.rs.getString(1));
                                          assertThrows(SQLException.class,
                                                  Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "DATE", Duration.class.getName() }),
                                                  () -> {
                                                      this.rs.getObject(1, Duration.class);
                                                      return null;
                                                  });
  
                                          assertTrue(this.rs.next());
                                          exp_instant_tz = ldt_20191231_0000.atZone(currZoneId).toInstant();
                                          exp_instant_05 = ldt_20191231_0000.atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                          exp_cal = GregorianCalendar.getInstance(tz);
                                          exp_cal.set(2019, 11, 31, 0, 0, 0);
                                          exp_cal.set(Calendar.MILLISECOND, 0);
                                          exp_cal.setLenient(false);
  
                                          assertEquals(java.sql.Date.from(exp_instant_tz), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.from(exp_instant_05), this.rs.getDate(1, cal_05));
                                          assertEquals(java.sql.Time.valueOf(lt_000000), this.rs.getTime(1));
                                          assertEquals(java.sql.Time.from(lt_000000.atOffset(ZoneOffset.ofHours(5)).atDate(ld_19700101).toInstant()),
                                                  this.rs.getTime(1, cal_05));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_tz), this.rs.getTimestamp(1));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_05), this.rs.getTimestamp(1, cal_05));
                                          assertEquals(java.sql.Date.from(exp_instant_tz), this.rs.getObject(1));
                                          assertEquals(java.sql.Date.from(exp_instant_tz), this.rs.getObject(1, java.sql.Date.class));
                                          assertEquals(java.sql.Time.valueOf(lt_000000), this.rs.getObject(1, java.sql.Time.class));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_tz), this.rs.getObject(1, java.sql.Timestamp.class));
                                          assertEquals(java.util.Date.from(exp_instant_tz), this.rs.getObject(1, java.util.Date.class));
                                          assertEquals(exp_cal, this.rs.getObject(1, java.util.Calendar.class));
                                          assertEquals(ld_20191231, this.rs.getObject(1, LocalDate.class));
                                          assertEquals(lt_000000, this.rs.getObject(1, LocalTime.class));
                                          assertEquals(ldt_20191231_0000, this.rs.getObject(1, LocalDateTime.class));
                                          assertEquals(ldt_20191231_0000.atZone(currZoneId).toOffsetDateTime().toOffsetTime(),
                                                  this.rs.getObject(1, OffsetTime.class));
                                          assertEquals(ldt_20191231_0000.atZone(currZoneId).toOffsetDateTime(), this.rs.getObject(1, OffsetDateTime.class));
                                          assertEquals(ldt_20191231_0000.atZone(currZoneId), this.rs.getObject(1, ZonedDateTime.class));
                                          assertEquals(s_20191231, this.rs.getString(1));
  
                                          testConn.close();
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  
      @Test
      public void testTimeGetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          String dur1 = withFract ? "300:10:20.012300" : "300:10:20";
          String dur2 = withFract ? "-300:10:20.012300" : "-300:10:20";
  
          createTable(tTime, withFract ? "(d TIME(6))" : "(d TIME)");
          this.stmt.executeUpdate("INSERT INTO " + tTime + " VALUES ('" + lt_120000_123456.toString() + "')");
          this.stmt.executeUpdate("INSERT INTO " + tTime + " VALUES ('" + dur1 + "')");
          this.stmt.executeUpdate("INSERT INTO " + tTime + " VALUES ('" + dur2 + "')");
  
          Calendar cal_05 = GregorianCalendar.getInstance(tz_plus_05_00);
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
  
          final TimeZone origTz = TimeZone.getDefault();
          try {
              for (TimeZone tz : this.senderTimeZones) {
                  for (String connectionTZ : this.connectionTimeZones) {
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone.setDefault(tz);
                                          ZoneId currZoneId = ZoneId.of(tz.getID());
  
                                          Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props);
                                          this.rs = testConn.createStatement().executeQuery("select * from " + tTime);
                                          assertTrue(this.rs.next());
  
                                          LocalDateTime exp_ldt = withFract ? ldt_19700101_120000_123456 : ldt_19700101_120000_123456.withNano(0);
                                          LocalTime exp_lt = withFract ? lt_120000_123456 : lt_120000;
  
                                          Instant exp_instant_tz = exp_ldt.atZone(currZoneId).toInstant();
                                          Instant exp_instant_05 = exp_ldt.atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                          Calendar exp_cal = GregorianCalendar.getInstance(tz);
                                          exp_cal.clear();
                                          exp_cal.set(Calendar.HOUR_OF_DAY, 12);
                                          exp_cal.set(Calendar.MILLISECOND, withFract ? 123 : 0);
                                          exp_cal.setLenient(false);
  
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1, cal_05));
                                          assertEquals(java.sql.Time.from(exp_instant_tz), this.rs.getTime(1));
                                          assertEquals(java.sql.Time.from(exp_instant_05), this.rs.getTime(1, cal_05));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_tz), this.rs.getTimestamp(1));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_05), this.rs.getTimestamp(1, cal_05));
                                          assertEquals(java.sql.Time.from(exp_instant_tz), this.rs.getObject(1));
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getObject(1, java.sql.Date.class));
                                          assertEquals(java.sql.Time.from(exp_instant_tz), this.rs.getObject(1, java.sql.Time.class));
                                          assertEquals(java.sql.Timestamp.from(exp_instant_tz), this.rs.getObject(1, java.sql.Timestamp.class));
                                          assertEquals(java.util.Date.from(exp_instant_tz), this.rs.getObject(1, java.util.Date.class));
                                          assertEquals(exp_cal, this.rs.getObject(1, java.util.Calendar.class));
                                          assertEquals(ld_19700101, this.rs.getObject(1, LocalDate.class));
                                          assertEquals(exp_lt, this.rs.getObject(1, LocalTime.class));
                                          assertEquals(exp_ldt, this.rs.getObject(1, LocalDateTime.class));
                                          assertEquals(exp_ldt.atZone(currZoneId).toOffsetDateTime().toOffsetTime(), this.rs.getObject(1, OffsetTime.class));
                                          assertEquals(exp_ldt.atZone(currZoneId).toOffsetDateTime(), this.rs.getObject(1, OffsetDateTime.class));
                                          assertEquals(exp_ldt.atZone(currZoneId), this.rs.getObject(1, ZonedDateTime.class));
                                          assertEquals(withFract ? s_120000_123456 : s_120000, this.rs.getString(1));
  
                                          assertTrue(this.rs.next());
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1, cal_05));
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getTime(1);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getTime(1, cal_05);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getTimestamp(1);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getTimestamp(1, cal_05);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1);
                                              return null;
                                          });
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getObject(1, java.sql.Date.class));
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, java.sql.Time.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, java.sql.Timestamp.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, java.util.Date.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, java.util.Calendar.class);
                                              return null;
                                          });
                                          assertEquals(ld_19700101, this.rs.getObject(1, LocalDate.class));
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, LocalTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, LocalDateTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, OffsetTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, OffsetDateTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> {
                                              this.rs.getObject(1, ZonedDateTime.class);
                                              return null;
                                          });
                                          assertEquals(Duration.parse(withFract ? "PT300H10M20.123S" : "PT300H10M20S"), this.rs.getObject(1, Duration.class));
                                          assertEquals(dur1, this.rs.getString(1));
  
                                          assertTrue(this.rs.next());
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1, cal_05));
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              DateTimeTest.this.rs.getTime(1);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getTime(1, cal_05);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getTimestamp(1);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getTimestamp(1, cal_05);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1);
                                              return null;
                                          });
                                          assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getObject(1, java.sql.Date.class));
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, java.sql.Time.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, java.sql.Timestamp.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, java.util.Date.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, java.util.Calendar.class);
                                              return null;
                                          });
                                          assertEquals(ld_19700101, this.rs.getObject(1, LocalDate.class));
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, LocalTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, LocalDateTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, OffsetTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, OffsetDateTime.class);
                                              return null;
                                          });
                                          assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> {
                                              this.rs.getObject(1, ZonedDateTime.class);
                                              return null;
                                          });
                                          assertEquals(Duration.parse(withFract ? "-PT300H10M20.123S" : "-PT300H10M20S"), this.rs.getObject(1, Duration.class));
                                          assertEquals(dur2, this.rs.getString(1));
  
                                          testConn.close();
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  
      @Test
      public void testTimestampGetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          OffsetDateTime orig_UTC = OffsetDateTime.of(2019, 12, 31, 22, 0, 0, withFract ? 123456000 : 0, ZoneOffset.UTC);
  
          createTable(tTimestamp, withFract ? "(d TIMESTAMP(6))" : "(d TIMESTAMP)");
  
          DateTimeFormatter dateTimeFmt = withFract ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
  
          Calendar cal_05 = GregorianCalendar.getInstance(tz_plus_05_00);
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props)) {
              this.pstmt = testConn.prepareStatement("INSERT INTO " + tTimestamp + " VALUES (?)");
              this.pstmt.setObject(1, orig_UTC);
              this.pstmt.execute();
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          final TimeZone origTz = TimeZone.getDefault();
          try {
              for (TimeZone clientTz : this.senderTimeZones) {
                  for (String connectionTZ : this.connectionTimeZones) {
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone.setDefault(clientTz);
                                          ZoneId currZoneId = ZoneId.of(clientTz.getID());
  
                                          Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props);
                                          this.rs = testConn.createStatement().executeQuery("select * from " + tTimestamp);
                                          assertTrue(this.rs.next());
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? clientTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
  
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
  
                                          ZonedDateTime exp_on_wire = orig_UTC.atZoneSameInstant(ZoneId.of(sessionTz.getID()));
  
                                          ZonedDateTime exp_date = exp_on_wire.withHour(0).withNano(0).withZoneSameLocal(currZoneId);
                                          Instant exp_date_with_cal = exp_on_wire.withHour(0).withNano(0).toOffsetDateTime()
                                                  .withOffsetSameLocal(ZoneOffset.ofHours(5)).toInstant();
  
                                          ZonedDateTime exp_time = exp_on_wire.withYear(1970).withMonth(1).withDayOfMonth(1).withZoneSameLocal(currZoneId);
                                          Instant exp_time_with_cal = exp_on_wire.withYear(1970).withMonth(1).withDayOfMonth(1).toOffsetDateTime()
                                                  .withOffsetSameLocal(ZoneOffset.ofHours(5)).toInstant();
  
                                          ZonedDateTime exp_datetime = exp_on_wire.withZoneSameLocal(currZoneId);
                                          ZonedDateTime exp_timestamp = preserveInstants
                                                  ? exp_on_wire.withZoneSameLocal(connTz.toZoneId()).withZoneSameInstant(currZoneId)
                                                  : exp_on_wire.withZoneSameLocal(currZoneId);
  
                                          Instant exp_timestamp_with_cal = exp_on_wire.toOffsetDateTime().withOffsetSameLocal(ZoneOffset.ofHours(5)).toInstant();
  
                                          ZonedDateTime exp_odt = exp_on_wire.withZoneSameLocal(preserveInstants ? connTz.toZoneId() : currZoneId);
  
                                          Calendar exp_cal = GregorianCalendar.getInstance(preserveInstants ? connTz : clientTz);
                                          exp_cal.set(exp_on_wire.getYear(), exp_on_wire.getMonthValue() - 1, exp_on_wire.getDayOfMonth(), exp_on_wire.getHour(),
                                                  exp_on_wire.getMinute(), exp_on_wire.getSecond());
                                          exp_cal.set(Calendar.MILLISECOND, exp_on_wire.getNano() / 1000000);
                                          exp_cal.setLenient(false);
  
                                          assertEquals(java.sql.Date.from(exp_date.toInstant()), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.from(exp_date_with_cal), this.rs.getDate(1, cal_05));
                                          assertEquals(java.sql.Time.from(exp_time.toInstant()), this.rs.getTime(1));
                                          assertEquals(java.sql.Time.from(exp_time_with_cal), this.rs.getTime(1, cal_05));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp.toInstant()), this.rs.getTimestamp(1));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp_with_cal), this.rs.getTimestamp(1, cal_05));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp.toInstant()), this.rs.getObject(1));
                                          assertEquals(java.sql.Date.from(exp_date.toInstant()), this.rs.getObject(1, java.sql.Date.class));
                                          assertEquals(java.sql.Time.from(exp_time.toInstant()), this.rs.getObject(1, java.sql.Time.class));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp.toInstant()), this.rs.getObject(1, java.sql.Timestamp.class));
                                          assertEquals(java.util.Date.from(exp_timestamp.toInstant()), this.rs.getObject(1, java.util.Date.class));
                                          assertEquals(exp_cal, this.rs.getObject(1, java.util.Calendar.class));
                                          assertEquals(exp_datetime.toLocalDate(), this.rs.getObject(1, LocalDate.class));
                                          assertEquals(exp_time.toLocalTime(), this.rs.getObject(1, LocalTime.class));
                                          assertEquals(exp_datetime.toLocalDateTime(), this.rs.getObject(1, LocalDateTime.class));
                                          assertEquals(exp_datetime.toOffsetDateTime().toOffsetTime(), this.rs.getObject(1, OffsetTime.class));
                                          assertEquals(exp_odt.toOffsetDateTime(), this.rs.getObject(1, OffsetDateTime.class));
                                          assertEquals(exp_odt, this.rs.getObject(1, ZonedDateTime.class));
                                          assertEquals(exp_on_wire.toLocalDateTime().format(dateTimeFmt), this.rs.getString(1));
                                          assertThrows(SQLException.class,
                                                  Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "TIMESTAMP", Duration.class.getName() }),
                                                  () -> {
                                                      this.rs.getObject(1, Duration.class);
                                                      return null;
                                                  });
  
                                          testConn.close();
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  
      @Test
      public void testDatetimeGetters() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
          LocalDateTime ldt_20200101_020000_123456 = LocalDateTime.of(2020, 1, 1, 2, 0, 0, withFract ? 123456000 : 0);
          DateTimeFormatter dateTimeFmt = withFract ? DATETIME_FORMATTER_WITH_MICROS_NO_OFFCET : TimeUtil.DATETIME_FORMATTER_NO_FRACT_NO_OFFSET;
          OffsetDateTime orig_UTC = OffsetDateTime.of(2020, 1, 1, 2, 0, 0, withFract ? 123456000 : 0, ZoneOffset.UTC);
  
          Calendar cal_05 = GregorianCalendar.getInstance(tz_plus_05_00);
  
          createTable(tDatetime, withFract ? "(d DATETIME(6))" : "(d DATETIME)");
          this.stmt.executeUpdate("INSERT INTO " + tDatetime + " VALUES ('" + ldt_20200101_020000_123456.toString() + "')");
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          final TimeZone origTz = TimeZone.getDefault();
          try {
              for (TimeZone clientTz : this.senderTimeZones) {
                  for (String connectionTZ : this.connectionTimeZones) {
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          TimeZone.setDefault(clientTz);
                                          ZoneId currZoneId = ZoneId.of(clientTz.getID());
  
                                          Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props);
                                          this.rs = testConn.createStatement().executeQuery("select * from " + tDatetime);
                                          assertTrue(this.rs.next());
  
                                          Instant exp_date_instant_tz = ldt_20200101_0000.atZone(currZoneId).toInstant();
                                          Instant exp_date_instant_05 = ldt_20200101_0000.atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                          Instant exp_time_instant_tz = (withFract ? ldt_19700101_020000_123000 : ldt_19700101_020000_123000.withNano(0))
                                                  .atZone(currZoneId).toInstant();
                                          Instant exp_time_instant_05 = (withFract ? ldt_19700101_020000_123000 : ldt_19700101_020000_123000.withNano(0))
                                                  .atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                          Instant exp_timestamp_instant_05 = ldt_20200101_020000_123456.atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? clientTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          TimeZone sessionTz = forceConnectionTimeZoneToSession ? connTz : serverTz;
                                          ZonedDateTime exp_on_wire = orig_UTC.atZoneSimilarLocal(ZoneId.of(sessionTz.getID()));
                                          Instant exp_timestamp_instant = (preserveInstants
                                                  ? exp_on_wire.withZoneSameLocal(connTz.toZoneId()).withZoneSameInstant(currZoneId)
                                                  : exp_on_wire.withZoneSameLocal(currZoneId)).toInstant();
  
                                          Calendar exp_cal = GregorianCalendar.getInstance(preserveInstants ? connTz : clientTz);
                                          exp_cal.set(exp_on_wire.getYear(), exp_on_wire.getMonthValue() - 1, exp_on_wire.getDayOfMonth(), exp_on_wire.getHour(),
                                                  exp_on_wire.getMinute(), exp_on_wire.getSecond());
                                          exp_cal.set(Calendar.MILLISECOND, exp_on_wire.getNano() / 1000000);
                                          exp_cal.setLenient(false);
  
                                          assertEquals(java.sql.Date.from(exp_date_instant_tz), this.rs.getDate(1));
                                          assertEquals(java.sql.Date.from(exp_date_instant_05), this.rs.getDate(1, cal_05));
                                          assertEquals(java.sql.Time.from(exp_time_instant_tz), this.rs.getTime(1));
                                          assertEquals(java.sql.Time.from(exp_time_instant_05), this.rs.getTime(1, cal_05));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp_instant), this.rs.getTimestamp(1));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp_instant_05), this.rs.getTimestamp(1, cal_05));
                                          assertEquals(ldt_20200101_020000_123456, this.rs.getObject(1));
                                          assertEquals(java.sql.Date.from(exp_date_instant_tz), this.rs.getObject(1, java.sql.Date.class));
                                          assertEquals(java.sql.Time.from(exp_time_instant_tz), this.rs.getObject(1, java.sql.Time.class));
                                          assertEquals(java.sql.Timestamp.from(exp_timestamp_instant), this.rs.getObject(1, java.sql.Timestamp.class));
                                          assertEquals(java.util.Date.from(exp_timestamp_instant), this.rs.getObject(1, java.util.Date.class));
                                          assertEquals(exp_cal, this.rs.getObject(1, java.util.Calendar.class));
                                          assertEquals(ldt_20200101_020000_123456.toLocalDate(), this.rs.getObject(1, LocalDate.class));
                                          assertEquals(ldt_20200101_020000_123456.toLocalTime(), this.rs.getObject(1, LocalTime.class));
                                          assertEquals(ldt_20200101_020000_123456, this.rs.getObject(1, LocalDateTime.class));
                                          assertEquals((withFract ? ldt_19700101_020000_123456 : ldt_19700101_020000_123456.withNano(0)).atZone(currZoneId)
                                                  .toOffsetDateTime().toOffsetTime(), this.rs.getObject(1, OffsetTime.class));
                                          assertEquals(ldt_20200101_020000_123456.atZone(preserveInstants ? connTz.toZoneId() : currZoneId).toOffsetDateTime(),
                                                  this.rs.getObject(1, OffsetDateTime.class));
                                          assertEquals(ldt_20200101_020000_123456.atZone(preserveInstants ? connTz.toZoneId() : currZoneId),
                                                  this.rs.getObject(1, ZonedDateTime.class));
                                          assertEquals(ldt_20200101_020000_123456.format(dateTimeFmt), this.rs.getString(1));
                                          assertThrows(SQLException.class,
                                                  Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "DATETIME", Duration.class.getName() }),
                                                  () -> {
                                                      this.rs.getObject(1, Duration.class);
                                                      return null;
                                                  });
  
                                          testConn.close();
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  
      @Test
      public void testYearGetters() throws Exception {
          createTable(tYear, "(d YEAR, s VARCHAR(30))");
          this.stmt.executeUpdate("INSERT INTO " + tYear + " VALUES ('" + s_2020 + "', '" + s_2020 + "')");
  
          Calendar cal_05 = GregorianCalendar.getInstance(tz_plus_05_00);
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
  
          final TimeZone origTz = TimeZone.getDefault();
          try {
              for (boolean yearIsDateType : new boolean[] { true, false }) {
                  for (TimeZone tz : this.senderTimeZones) {
                      for (String connectionTZ : this.connectionTimeZones) {
                          for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                              for (boolean preserveInstants : new boolean[] { false, true }) {
                                  for (boolean useSSPS : new boolean[] { false, true }) {
                                      for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                          for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                              System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession="
                                                      + forceConnectionTimeZoneToSession + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts="
                                                      + useSSPS + "; sendFractSeconds=" + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                              props.setProperty(PropertyKey.yearIsDateType.getKeyName(), "" + yearIsDateType);
                                              if (connectionTZ == null) {
                                                  props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                              } else {
                                                  props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                              }
                                              props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                              props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                              props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                              props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                              props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                              TimeZone.setDefault(tz);
                                              ZoneId currZoneId = ZoneId.of(tz.getID());
  
                                              Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props);
                                              ResultSet rs1 = testConn.createStatement().executeQuery("select * from " + tYear);
  
                                              assertTrue(rs1.next());
                                              Instant exp_instant_tz = ldt_20200101_0000.atZone(currZoneId).toInstant();
                                              Instant exp_instant_05 = ldt_20200101_0000.atOffset(ZoneOffset.ofHours(5)).toInstant();
  
                                              Calendar exp_cal = GregorianCalendar.getInstance(tz);
                                              exp_cal.set(2020, 0, 1, 0, 0, 0);
                                              exp_cal.set(Calendar.MILLISECOND, 0);
                                              exp_cal.setLenient(false);
  
                                              if (yearIsDateType) {
                                                  assertEquals(java.sql.Date.from(exp_instant_tz), rs1.getDate(1));
                                                  assertEquals(java.sql.Date.from(exp_instant_05), rs1.getDate(1, cal_05));
                                                  assertEquals(java.sql.Time.valueOf(lt_000000), rs1.getTime(1));
                                                  assertEquals(java.sql.Time.from(lt_000000.atOffset(ZoneOffset.ofHours(5)).atDate(ld_19700101).toInstant()),
                                                          rs1.getTime(1, cal_05));
                                                  assertEquals(java.sql.Timestamp.from(exp_instant_tz), rs1.getTimestamp(1));
                                                  assertEquals(java.sql.Timestamp.from(exp_instant_05), rs1.getTimestamp(1, cal_05));
                                                  assertEquals(java.sql.Date.from(exp_instant_tz), rs1.getObject(1));
                                                  assertEquals(java.sql.Date.from(exp_instant_tz), rs1.getObject(1, java.sql.Date.class));
                                                  assertEquals(java.sql.Time.valueOf(lt_000000), rs1.getObject(1, java.sql.Time.class));
                                                  assertEquals(java.sql.Timestamp.from(exp_instant_tz), rs1.getObject(1, java.sql.Timestamp.class));
                                                  assertEquals(java.util.Date.from(exp_instant_tz), rs1.getObject(1, java.util.Date.class));
                                                  assertEquals(exp_cal, rs1.getObject(1, java.util.Calendar.class));
                                                  assertEquals(ld_20200101, rs1.getObject(1, LocalDate.class));
                                                  assertEquals(lt_000000, rs1.getObject(1, LocalTime.class));
                                                  assertEquals(ldt_20200101_0000, rs1.getObject(1, LocalDateTime.class));
                                                  assertEquals(ldt_20200101_0000.atZone(currZoneId).toOffsetDateTime().toOffsetTime(),
                                                          rs1.getObject(1, OffsetTime.class));
                                                  assertEquals(ldt_20200101_0000.atZone(currZoneId).toOffsetDateTime(), rs1.getObject(1, OffsetDateTime.class));
                                                  assertEquals(ldt_20200101_0000.atZone(currZoneId), rs1.getObject(1, ZonedDateTime.class));
                                                  assertEquals(s_20200101, rs1.getString(1));
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "DATE", Duration.class.getName() }), () -> {
                                                              rs1.getObject(1, Duration.class);
                                                              return null;
                                                          });
                                              } else {
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Date.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getDate(1);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Date.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getDate(1, cal_05);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Time.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getTime(1);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Time.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getTime(1, cal_05);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getTimestamp(1);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getTimestamp(1, cal_05);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Date.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, java.sql.Date.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Time.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, java.sql.Time.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, java.sql.Timestamp.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, java.util.Date.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", Calendar.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, java.util.Calendar.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", LocalDate.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, LocalDate.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", LocalTime.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, LocalTime.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", LocalDateTime.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, LocalDateTime.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", OffsetTime.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, OffsetTime.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", OffsetDateTime.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, OffsetDateTime.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", ZonedDateTime.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, ZonedDateTime.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion",
                                                          new Object[] { "LONG", Duration.class.getName() }), new Callable<Void>() {
                                                              public Void call() throws Exception {
                                                                  rs1.getObject(1, Duration.class);
                                                                  return null;
                                                              }
                                                          });
                                                  assertEquals(s_2020, rs1.getString(1));
                                                  assertEquals(Short.valueOf((short) 2020), rs1.getObject(1));
                                              }
  
                                              assertEquals(s_2020, rs1.getObject(2));
                                              assertEquals(s_2020, rs1.getString(2));
  
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Date.class.getName() }), new Callable<Void>() {
  
                                                          public Void call() throws Exception {
                                                              rs1.getDate(2);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Date.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getDate(2, cal_05);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Time.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getTime(2);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Time.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getTime(2, cal_05);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getTimestamp(2, cal_05);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getTimestamp(2, cal_05);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Date.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, java.sql.Date.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Time.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, java.sql.Time.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, java.sql.Timestamp.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.sql.Timestamp.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, java.util.Date.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, java.util.Calendar.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, java.util.Calendar.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class,
                                                      Messages.getString("ResultSet.UnableToConvertString", new Object[] { s_2020, LocalDate.class.getName() }),
                                                      new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, LocalDate.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class,
                                                      Messages.getString("ResultSet.UnableToConvertString", new Object[] { s_2020, LocalTime.class.getName() }),
                                                      new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, LocalTime.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, LocalDateTime.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, LocalDateTime.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class,
                                                      Messages.getString("ResultSet.UnableToConvertString", new Object[] { s_2020, OffsetTime.class.getName() }),
                                                      new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, OffsetTime.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, OffsetDateTime.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, OffsetDateTime.class);
                                                              return null;
                                                          }
                                                      });
                                              assertThrows(SQLException.class, Messages.getString("ResultSet.UnableToConvertString",
                                                      new Object[] { s_2020, ZonedDateTime.class.getName() }), new Callable<Void>() {
                                                          public Void call() throws Exception {
                                                              rs1.getObject(2, ZonedDateTime.class);
                                                              return null;
                                                          }
                                                      });
  
                                              testConn.close();
                                          }
  
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  
      @Test
      public void testSymmetricInstantRetrieval() throws Exception {
          boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions
  
          createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)");
          createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)");
          createTable(tVarchar, "(id INT, d VARCHAR(30))");
  
          id = 0;
  
          Properties props = new Properties();
          props.setProperty(PropertyKey.sslMode.getKeyName(), "DISABLED");
          props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
          props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
          props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "SERVER");
  
          TimeZone serverTz;
          try (Connection testConn = getConnectionWithProps(props)) {
              serverTz = ((MysqlConnection) testConn).getSession().getServerSession().getSessionTimeZone();
          }
  
          Calendar cal_02 = GregorianCalendar.getInstance(tz_plus_02_00);
  
          for (TimeZone senderTz : this.senderTimeZones) {
              try {
                  for (String connectionTZ : this.connectionTimeZones) {
                      initConnections(senderTz, connectionTZ);
  
                      for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
                          for (boolean preserveInstants : new boolean[] { false, true }) {
                              for (boolean useSSPS : new boolean[] { false, true }) {
                                  for (boolean sendFractionalSeconds : new boolean[] { false, true }) {
                                      for (boolean sendTimeFract : new boolean[] { false, true }) {
  
                                          System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession
                                                  + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds="
                                                  + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract);
  
                                          if (connectionTZ == null) {
                                              props.remove(PropertyKey.connectionTimeZone.getKeyName());
                                          } else {
                                              props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ);
                                          }
                                          props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                                          props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                                          props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                                          props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract);
                                          props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds);
  
                                          ZonedDateTime zdt_20200101_120000_123456_at_senderTz = (withFract ? ldt_20200101_120000_123456
                                                  : ldt_20200101_120000_123456.withNano(0)).atZone(senderTz.toZoneId());
  
                                          java.sql.Timestamp ts = java.sql.Timestamp.from(zdt_20200101_120000_123456_at_senderTz.toInstant());
                                          java.util.Date ud = java.util.Date.from(zdt_20200101_120000_123456_at_senderTz.toInstant());
  
                                          java.sql.Timestamp expTsNoFract = java.sql.Timestamp
                                                  .from(zdt_20200101_120000_123456_at_senderTz.withNano(0).toInstant());
                                          java.sql.Timestamp expTsNanos = sendFractionalSeconds
                                                  ? java.sql.Timestamp.from(zdt_20200101_120000_123456_at_senderTz.toInstant())
                                                  : expTsNoFract;
                                          java.sql.Timestamp expTsMillis = sendFractionalSeconds
                                                  ? java.sql.Timestamp.from(zdt_20200101_120000_123456_at_senderTz.truncatedTo(ChronoUnit.MILLIS).toInstant())
                                                  : expTsNoFract;
  
                                          java.util.Date expUdNoFract = java.util.Date.from(zdt_20200101_120000_123456_at_senderTz.withNano(0).toInstant());
                                          java.util.Date expUd = sendFractionalSeconds ? java.util.Date.from(zdt_20200101_120000_123456_at_senderTz.toInstant())
                                                  : expUdNoFract;
  
                                          TimeZone connTz = connectionTZ == null || "LOCAL".equals(connectionTZ) ? senderTz
                                                  : ("SERVER".equals(connectionTZ) ? serverTz : TimeZone.getTimeZone(connectionTZ));
                                          ZonedDateTime expZdtNoFract = zdt_20200101_120000_123456_at_senderTz.withNano(0)
                                                  .withZoneSameInstant(preserveInstants ? connTz.toZoneId() : senderTz.toZoneId());
                                          ZonedDateTime expZdt = sendFractionalSeconds
                                                  ? zdt_20200101_120000_123456_at_senderTz
                                                          .withZoneSameInstant(preserveInstants ? connTz.toZoneId() : senderTz.toZoneId())
                                                  : expZdtNoFract;
  
                                          /* java.sql.Timestamp into DATETIME field */
  
                                          checkSymmetricSetAndGet(props, tDatetime, ts, null, senderTz, UseMethod.setTimestamp, cal_02, UseMethod.getTimestamp,
                                                  null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getTimestamp,
                                                  null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getObject,
                                                  Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getTimestamp, null,
                                                  expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getObject,
                                                  Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getTimestamp, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, Timestamp.class, expTsNanos);
  
                                          /* java.util.Date into DATETIME field */
  
                                          checkSymmetricSetAndGet(props, tDatetime, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getTimestamp, null,
                                                  expTsMillis);
                                          checkSymmetricSetAndGet(props, tDatetime, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getObject,
                                                  Timestamp.class, expTsMillis);
                                          checkSymmetricSetAndGet(props, tDatetime, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getTimestamp, null, expTsMillis);
                                          checkSymmetricSetAndGet(props, tDatetime, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, Timestamp.class, expTsMillis);
                                          checkSymmetricSetAndGet(props, tDatetime, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, java.util.Date.class, expUd);
  
                                          /* java.time.ZonedDateTime into DATETIME field */
  
                                          checkSymmetricSetAndGet(props, tDatetime, zdt_20200101_120000_123456_at_senderTz, null, senderTz, UseMethod.setObject,
                                                  null, UseMethod.getTimestamp, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, zdt_20200101_120000_123456_at_senderTz, null, senderTz, UseMethod.setObject,
                                                  null, UseMethod.getObject, Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getTimestamp, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tDatetime, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, java.util.Date.class, expUd);
                                          checkSymmetricSetAndGet(props, tDatetime, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, ZonedDateTime.class, expZdt);
  
                                          /* java.sql.Timestamp into TIMESTAMP field */
  
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setTimestamp, cal_02, UseMethod.getTimestamp,
                                                  null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getTimestamp,
                                                  null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getObject, null,
                                                  expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getObject,
                                                  Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getTimestamp, null,
                                                  expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getObject, null,
                                                  expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getObject,
                                                  Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getTimestamp, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, Timestamp.class, expTsNanos);
  
                                          /* java.util.Date into TIMESTAMP field */
  
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getTimestamp, null,
                                                  expTsMillis);
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getObject, null,
                                                  expTsMillis);
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getObject,
                                                  Timestamp.class, expTsMillis);
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getTimestamp, null, expTsMillis);
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, null, expTsMillis);
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, Timestamp.class, expTsMillis);
                                          checkSymmetricSetAndGet(props, tTimestamp, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, java.util.Date.class, expUd);
  
                                          /* java.time.ZonedDateTime into TIMESTAMP field */
  
                                          checkSymmetricSetAndGet(props, tTimestamp, zdt_20200101_120000_123456_at_senderTz, null, senderTz, UseMethod.setObject,
                                                  null, UseMethod.getTimestamp, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, zdt_20200101_120000_123456_at_senderTz, null, senderTz, UseMethod.setObject,
                                                  null, UseMethod.getObject, Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getTimestamp, null, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, Timestamp.class, expTsNanos);
                                          checkSymmetricSetAndGet(props, tTimestamp, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, java.util.Date.class, expUd);
                                          checkSymmetricSetAndGet(props, tTimestamp, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, ZonedDateTime.class, expZdt);
  
                                          /* java.sql.Timestamp into VARCHAR field */
  
                                          Timestamp expTsNanosFromChars = useSSPS ? expTsNoFract : expTsNanos; // TODO milliseconds are ignored by server. Bug ?
                                          Timestamp expTsMillisFromChars = useSSPS ? expTsNoFract : expTsMillis; // TODO milliseconds are ignored by server. Bug ?
                                          java.util.Date expUdFromChars = useSSPS ? expUdNoFract : expUd; // TODO milliseconds are ignored by server. Bug ?
                                          ZonedDateTime expZdtFromChars = useSSPS ? expZdtNoFract : expZdt; // TODO milliseconds are ignored by server. Bug ?
  
                                          checkSymmetricSetAndGet(props, tVarchar, ts, null, senderTz, UseMethod.setTimestamp, cal_02, UseMethod.getTimestamp,
                                                  null, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getTimestamp, null,
                                                  expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ts, null, senderTz, UseMethod.setTimestamp, null, UseMethod.getObject,
                                                  Timestamp.class, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getTimestamp, null,
                                                  expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ts, null, senderTz, UseMethod.setObject, null, UseMethod.getObject,
                                                  Timestamp.class, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getTimestamp, null, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ts, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, Timestamp.class, expTsNanosFromChars);
  
                                          /* java.util.Date into VARCHAR field */
  
                                          checkSymmetricSetAndGet(props, tVarchar, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getTimestamp, null,
                                                  expTsMillisFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ud, null, senderTz, UseMethod.setObject, null, UseMethod.getObject,
                                                  Timestamp.class, expTsMillisFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getTimestamp, null, expTsMillisFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, Timestamp.class, expTsMillisFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, ud, MysqlType.TIMESTAMP, senderTz, UseMethod.setObject, null,
                                                  UseMethod.getObject, java.util.Date.class, expUdFromChars);
  
                                          /* java.time.ZonedDateTime into VARCHAR field */
  
                                          checkSymmetricSetAndGet(props, tVarchar, zdt_20200101_120000_123456_at_senderTz, null, senderTz, UseMethod.setObject,
                                                  null, UseMethod.getTimestamp, null, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, zdt_20200101_120000_123456_at_senderTz, null, senderTz, UseMethod.setObject,
                                                  null, UseMethod.getObject, Timestamp.class, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getTimestamp, null, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, Timestamp.class, expTsNanosFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, java.util.Date.class, expUdFromChars);
                                          checkSymmetricSetAndGet(props, tVarchar, zdt_20200101_120000_123456_at_senderTz, MysqlType.TIMESTAMP, senderTz,
                                                  UseMethod.setObject, null, UseMethod.getObject, ZonedDateTime.class, expZdtFromChars);
  
                                      }
                                  }
                              }
                          }
                      }
                      closeConnections();
                  }
              } finally {
                  closeConnections();
              }
          }
      }
  
      private void checkSymmetricSetAndGet(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, UseMethod useSetter,
              Calendar calendar, UseMethod useGetter, Class<?> targetClass, Object expResult) throws Exception {
          if (props == null) {
              props = new Properties();
          }
          final TimeZone origTz = TimeZone.getDefault();
          try {
              TimeZone.setDefault(senderTz);
              Connection testConn = this.tzConnections.get(getKey(props));
  
              id++;
  
              PreparedStatement insertPstmt = testConn.prepareStatement("INSERT INTO " + tableName + " VALUES (?, ?)");
              insertPstmt.setInt(1, id);
              switch (useSetter) {
                  case setTimestamp:
                      if (calendar == null) {
                          insertPstmt.setTimestamp(2, (Timestamp) parameter);
                      } else {
                          insertPstmt.setTimestamp(2, (Timestamp) parameter, calendar);
                      }
                      break;
  
                  default:
                      if (targetSqlType == null) {
                          insertPstmt.setObject(2, parameter);
                      } else {
                          insertPstmt.setObject(2, parameter, targetSqlType);
                      }
                      break;
              }
              assertEquals(1, insertPstmt.executeUpdate());
  
              ResultSet res = testConn.createStatement().executeQuery("SELECT * FROM " + tableName + " WHERE id = " + id);
              assertTrue(res.next());
              switch (useGetter) {
                  case getTimestamp:
                      if (calendar == null) {
                          assertEquals(expResult, res.getTimestamp(2));
                      } else {
                          assertEquals(expResult, res.getTimestamp(2, calendar));
                      }
                      break;
  
                  default:
                      if (targetClass == null) {
                          assertEquals(expResult, res.getObject(2));
                      } else {
                          assertEquals(expResult, res.getObject(2, targetClass));
                      }
                      break;
              }
  
          } finally {
              TimeZone.setDefault(origTz);
          }
      }
  }
  