/*
 * Copyright (c) 2021, 2023, 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.regression;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Properties;
import java.util.TimeZone;

import org.junit.jupiter.api.Test;

import com.mysql.cj.MysqlType;
import com.mysql.cj.conf.PropertyDefinitions.SslMode;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.util.TimeUtil;

import testsuite.BaseTestCase;

public class DateTimeRegressionTest extends BaseTestCase {

    /**
     * Tests fix for BUG#3620 -- Timezone not respected correctly.
     *
     * @throws SQLException
     */
    @Test
    public void testBug3620() throws SQLException {
        // FIXME: This test is sensitive to being in CST/CDT it seems
        assumeTrue(TimeZone.getDefault().equals(TimeZone.getTimeZone("America/Chicago")), "This test is running only in America/Chicago time zone.");

        long epsillon = 3000; // 3 seconds time difference

        try {
            this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3620");
            this.stmt.executeUpdate("CREATE TABLE testBug3620 (field1 TIMESTAMP)");

            PreparedStatement tsPstmt = this.conn.prepareStatement("INSERT INTO testBug3620 VALUES (?)");

            Calendar pointInTime = Calendar.getInstance();
            pointInTime.set(2004, 02, 29, 10, 0, 0);

            long pointInTimeOffset = pointInTime.getTimeZone().getRawOffset();

            java.sql.Timestamp ts = new java.sql.Timestamp(pointInTime.getTime().getTime());

            tsPstmt.setTimestamp(1, ts);
            tsPstmt.executeUpdate();

            String tsValueAsString = getSingleValue("testBug3620", "field1", null).toString();

            System.out.println("Timestamp as string with no calendar: " + tsValueAsString.toString());

            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

            this.stmt.executeUpdate("DELETE FROM testBug3620");

            Statement tsStmt = this.conn.createStatement();

            tsPstmt = this.conn.prepareStatement("INSERT INTO testBug3620 VALUES (?)");

            tsPstmt.setTimestamp(1, ts, cal);
            tsPstmt.executeUpdate();

            tsValueAsString = getSingleValue("testBug3620", "field1", null).toString();

            Timestamp tsValueAsTimestamp = (Timestamp) getSingleValue("testBug3620", "field1", null);

            System.out.println("Timestamp as string with UTC calendar: " + tsValueAsString.toString());
            System.out.println("Timestamp as Timestamp with UTC calendar: " + tsValueAsTimestamp);

            this.rs = tsStmt.executeQuery("SELECT field1 FROM testBug3620");
            this.rs.next();

            Timestamp tsValueUTC = this.rs.getTimestamp(1, cal);

            //
            // We use this testcase with other vendors, JDBC spec requires result set fields can only be read once, although MySQL doesn't require this ;)
            //
            this.rs = tsStmt.executeQuery("SELECT field1 FROM testBug3620");
            this.rs.next();

            Timestamp tsValueStmtNoCal = this.rs.getTimestamp(1);

            System.out.println("Timestamp specifying UTC calendar from normal statement: " + tsValueUTC.toString());

            PreparedStatement tsPstmtRetr = this.conn.prepareStatement("SELECT field1 FROM testBug3620");

            this.rs = tsPstmtRetr.executeQuery();
            this.rs.next();

            Timestamp tsValuePstmtUTC = this.rs.getTimestamp(1, cal);

            System.out.println("Timestamp specifying UTC calendar from prepared statement: " + tsValuePstmtUTC.toString());

            //
            // We use this testcase with other vendors, JDBC spec requires result set fields can only be read once, although MySQL doesn't require this ;)
            //
            this.rs = tsPstmtRetr.executeQuery();
            this.rs.next();

            Timestamp tsValuePstmtNoCal = this.rs.getTimestamp(1);

            System.out.println("Timestamp specifying no calendar from prepared statement: " + tsValuePstmtNoCal.toString());

            long stmtDeltaTWithCal = ts.getTime() - tsValueStmtNoCal.getTime();

            long deltaOrig = Math.abs(stmtDeltaTWithCal - pointInTimeOffset);

            assertTrue(deltaOrig < epsillon, "Difference between original timestamp and timestamp retrieved using java.sql.Statement "
                    + "set in database using UTC calendar is not ~= " + epsillon + ", it is actually " + deltaOrig);

            long pStmtDeltaTWithCal = ts.getTime() - tsValuePstmtNoCal.getTime();

            System.out.println(
                    Math.abs(pStmtDeltaTWithCal - pointInTimeOffset) + " < " + epsillon + (Math.abs(pStmtDeltaTWithCal - pointInTimeOffset) < epsillon));
            assertTrue(Math.abs(pStmtDeltaTWithCal - pointInTimeOffset) < epsillon,
                    "Difference between original timestamp and timestamp retrieved using java.sql.PreparedStatement "
                            + "set in database using UTC calendar is not ~= " + epsillon + ", it is actually " + pStmtDeltaTWithCal);

            System.out.println("Difference between original ts and ts with no calendar: " + (ts.getTime() - tsValuePstmtNoCal.getTime()) + ", offset should be "
                    + pointInTimeOffset);
        } finally {
            this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3620");
        }
    }

    /**
     * Tests fix for Bug#5874, Timezone conversion goes in the wrong direction
     * (when useTimezone=true and server timezone differs from client timezone).
     *
     * @throws Exception
     */
    @Test
    public void testBug5874() throws Exception {
        assumeTrue(supportsTimeZoneNames(this.stmt), "This test requies the server with populated time zone tables.");

        TimeZone defaultTimeZone = TimeZone.getDefault();

        try {
            String clientTimeZoneName = "America/Los_Angeles";
            String connectionTimeZoneName = "America/Chicago";

            TimeZone.setDefault(TimeZone.getTimeZone(clientTimeZoneName));

            long offsetDifference = TimeZone.getDefault().getRawOffset() - TimeZone.getTimeZone(connectionTimeZoneName).getRawOffset();

            SimpleDateFormat timestampFormat = TimeUtil.getSimpleDateFormat(null, "yyyy-MM-dd HH:mm:ss", null);
            SimpleDateFormat timeFormat = TimeUtil.getSimpleDateFormat(null, "HH:mm:ss", null);

            long pointInTime = timestampFormat.parse("2004-10-04 09:19:00").getTime();

            Properties props = new Properties();
            props.put("useTimezone", "true");
            props.put(PropertyKey.connectionTimeZone.getKeyName(), connectionTimeZoneName);
            props.put(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "true");
            props.put(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false");
            props.setProperty(PropertyKey.preserveInstants.getKeyName(), "true");

            Connection tzConn = getConnectionWithProps(props);
            Statement tzStmt = tzConn.createStatement();
            createTable("testBug5874", "(tstamp DATETIME, t TIME)");

            PreparedStatement tsPstmt = tzConn.prepareStatement("INSERT INTO testBug5874 VALUES (?, ?)");

            tsPstmt.setTimestamp(1, new Timestamp(pointInTime));
            tsPstmt.setTime(2, new Time(pointInTime));
            tsPstmt.executeUpdate();

            this.rs = tzStmt.executeQuery("SELECT * from testBug5874");

            while (this.rs.next()) { // Driver now converts/checks DATE/TIME/TIMESTAMP/DATETIME types when calling getString()...
                String retrTimestampString = new String(this.rs.getBytes(1));
                Timestamp retrTimestamp = this.rs.getTimestamp(1);

                java.util.Date timestampOnServer = timestampFormat.parse(retrTimestampString);

                long retrievedOffsetForTimestamp = retrTimestamp.getTime() - timestampOnServer.getTime();

                assertEquals(offsetDifference, retrievedOffsetForTimestamp,
                        "Original timestamp and timestamp retrieved using client timezone are not the same");

                String retrTimeString = new String(this.rs.getBytes(2));
                Time retrTime = this.rs.getTime(2);

                java.util.Date timeOnServerAsDate = timeFormat.parse(retrTimeString);
                Time timeOnServer = new Time(timeOnServerAsDate.getTime());

                long retrievedOffsetForTime = retrTime.getTime() - timeOnServer.getTime();

                assertEquals(0, retrievedOffsetForTime, "Original time and time retrieved using client timezone are not the same");
            }

            tzConn.close();
        } finally {
            TimeZone.setDefault(defaultTimeZone);
        }
    }

    /**
     * Tests fix for Bug#15604 (11745460), TIMEZONE DISCARDED STORING JAVA.UTIL.CALENDAR INTO DATETIME.
     *
     * @throws Exception
     */
    @Test
    public void testBug15604() throws Exception {
        assumeTrue(supportsTimeZoneNames(this.stmt), "This test requies the server with populated time zone tables.");

        createTable("testBug15604_date_cal", "(field1 DATE)");
        Properties props = new Properties();
        props.setProperty(PropertyKey.sessionVariables.getKeyName(), "time_zone='America/Chicago'");

        Connection nonLegacyConn = getConnectionWithProps(props);

        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

        cal.set(Calendar.YEAR, 2005);
        cal.set(Calendar.MONTH, 4);
        cal.set(Calendar.DAY_OF_MONTH, 15);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        java.sql.Date sqlDate = new java.sql.Date(cal.getTime().getTime());

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(sqlDate);
        System.out.println(new java.sql.Date(cal2.getTime().getTime()));
        this.pstmt = nonLegacyConn.prepareStatement("INSERT INTO testBug15604_date_cal VALUES (?)");

        this.pstmt.setDate(1, sqlDate, cal);
        this.pstmt.executeUpdate();
        this.rs = nonLegacyConn.createStatement().executeQuery("SELECT field1 FROM testBug15604_date_cal");
        this.rs.next();

        assertEquals(sqlDate.getTime(), this.rs.getDate(1, cal).getTime());
    }

    /**
     * Tests fix for Bug#20391832, SETOBJECT() FOR TYPES.TIME RESULTS IN EXCEPTION WHEN VALUE HAS FRACTIONAL PART.
     *
     * @throws Exception
     */
    @Test
    public void testBug20391832() throws Exception {
        createTable("testBug20391832", "(v varchar(40))");

        boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions

        Properties props = new Properties();
        props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
        props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
        props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "LOCAL");
        for (boolean useSSPS : new boolean[] { false, true }) {
            for (boolean sendFr : new boolean[] { false, true }) {

                System.out.println("useServerPrepStmts=" + useSSPS + "; sendFractSeconds=" + sendFr);

                props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFr);
                Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props);

                this.pstmt = testConn.prepareStatement("insert into testBug20391832 values(?)");

                for (MysqlType type : new MysqlType[] { MysqlType.DATETIME, MysqlType.TIMESTAMP }) {
                    subTestBug20391832(props, type, "2038-01-19", "2038-01-19 00:00:00");
                    subTestBug20391832(props, type, "38-01-19", "2038-01-19 00:00:00");
                    subTestBug20391832(props, type, "2038#01$19", "2038-01-19 00:00:00");
                    subTestBug20391832(props, type, "38#01$19", "2038-01-19 00:00:00");
                    subTestBug20391832(props, type, "20380119", "2038-01-19 00:00:00");
                    subTestBug20391832(props, type, "380119", "2038-01-19 00:00:00");
                    subTestBug20391832(props, type, "030417", "2003-04-17 00:00:00"); // resolved as a DATE literal

                    assertThrows(SQLException.class, "Conversion from java.time.Duration to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "12 1", null);
                        return null;
                    });
                    assertThrows(SQLException.class, "Conversion from java.time.Duration to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "12 13:04", null);
                        return null;
                    });
                    assertThrows(SQLException.class, "Conversion from java.time.Duration to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "12 1:4:1", null);
                        return null;
                    });
                    assertThrows(SQLException.class, "Conversion from java.time.Duration to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "12 1:04:17.123456789", null);
                        return null;
                    });
                    assertThrows(SQLException.class, "Conversion from java.time.Duration to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "-838:59:59", null);
                        return null;
                    });

                    assertThrows(SQLException.class, "Conversion from java.time.LocalTime to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "13:04:17", null);
                        return null;
                    });
                    assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "13:04", null);
                        return null;
                    });
                    assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "0417", null);
                        return null;
                    });
                    assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "03:14:07.012", null);
                        return null;
                    });
                    assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to " + type + " is not supported.", () -> {
                        subTestBug20391832(props, type, "031407.123", null);
                        return null;
                    });

                    assertThrows(SQLException.class, ".*There is no known date-time pattern for.*", () -> {
                        subTestBug20391832(props, type, "031407#12", null); // wrong delimiter
                        return null;
                    });

                    subTestBug20391832(props, type, "2038-01-19 03:14:07", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038-01-19 03:14:07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "38-01-19 03:14:07", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "38-01-19 03:14:07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38-01-19 03:14:07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "2038#01$19 03@14%07", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19 03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "38#01$19 03@14%07", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "38#01$19 03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19 03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "2038#01$19T03@14%07", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#01$19T03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "38#01$19T03@14%07", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "38#01$19T03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "38#01$19T03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "2038#1$19 3@14%7", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "2038#1$19T3@14%7.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19 3@14%7.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19T3@14%7.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19 3@14%7.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19T3@14%7.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19 3@14%7.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19T3@14%7.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19 3@14%7.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "2038#1$19T3@14%7.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "38#1$9 3@4%7", "2038-01-09 03:04:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "38#1$9T3@4%7.1", sendFr ? "2038-01-09 03:04:07.1" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.01", sendFr ? "2038-01-09 03:04:07.01" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.012", sendFr ? "2038-01-09 03:04:07.012" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.0123", sendFr ? "2038-01-09 03:04:07.0123" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.01234", sendFr ? "2038-01-09 03:04:07.01234" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.012345", sendFr ? "2038-01-09 03:04:07.012345" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.0123456", sendFr ? "2038-01-09 03:04:07.012346" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.01234567", sendFr ? "2038-01-09 03:04:07.012346" : "2038-01-09 03:04:07");
                        subTestBug20391832(props, type, "38#1$9T3@4%7.012345678", sendFr ? "2038-01-09 03:04:07.012346" : "2038-01-09 03:04:07");
                    }

                    subTestBug20391832(props, type, "20380119031407", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "20380119031407.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "20380119031407.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }

                    subTestBug20391832(props, type, "380119031407", "2038-01-19 03:14:07");
                    if (withFract) {
                        subTestBug20391832(props, type, "380119031407.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                        subTestBug20391832(props, type, "380119031407.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07");
                    }
                }

                // ================================================

                subTestBug20391832(props, MysqlType.TIME, "12 1", "289:0:0");
                subTestBug20391832(props, MysqlType.TIME, "-12 1", "-289:0:0");
                subTestBug20391832(props, MysqlType.TIME, "12 12:04", "300:4:0");
                subTestBug20391832(props, MysqlType.TIME, "-12 12:04", "-300:4:0");
                subTestBug20391832(props, MysqlType.TIME, "12 1:4:1", "289:4:1");
                subTestBug20391832(props, MysqlType.TIME, "-12 1:4:7", "-289:4:7");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "5 1:04:17.123456789", sendFr ? "121:4:17.123457" : "121:4:17");
                    subTestBug20391832(props, MysqlType.TIME, "-5 1:04:17.123456789", sendFr ? "-121:4:17.123457" : "-121:4:17");
                }
                subTestBug20391832(props, MysqlType.TIME, "25:59:59", "25:59:59");
                subTestBug20391832(props, MysqlType.TIME, "-838:59:59", "-838:59:59");

                assertThrows(SQLException.class, ".*Conversion from java.time.LocalDate to TIME is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalDate to TIME is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalDate to TIME is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalDate to TIME is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalDate to TIME is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.TIME, "20380119", null);
                    return null;
                });
                subTestBug20391832(props, MysqlType.TIME, "120119", "12:01:19");
                subTestBug20391832(props, MysqlType.TIME, "13:04:17", "13:04:17");
                subTestBug20391832(props, MysqlType.TIME, "13:04", "13:04:00");
                subTestBug20391832(props, MysqlType.TIME, "130417", "13:04:17");
                subTestBug20391832(props, MysqlType.TIME, "0417", "00:04:17");
                subTestBug20391832(props, MysqlType.TIME, "17", "00:00:17");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "0417.1234567", sendFr ? "00:04:17.123457" : "00:04:17");
                    subTestBug20391832(props, MysqlType.TIME, "17.1234567", sendFr ? "00:00:17.123457" : "00:00:17");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "3:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:4:07.012345", sendFr ? "03:04:07.012345" : "03:04:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "3:4:7.0123456", sendFr ? "03:04:07.012346" : "03:04:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:7.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "03:14:07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "031407.123", sendFr ? "03:14:07.123" : "03:14:07");
                }

                assertThrows(SQLException.class, ".*There is no known date-time pattern for.*", () -> {
                    subTestBug20391832(props, MysqlType.TIME, "031407#12", null); // wrong delimiter
                    return null;
                });

                subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "2038#1$19 3@14%7", "03:14:07");
                subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "20380119031407", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "20380119031407.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                subTestBug20391832(props, MysqlType.TIME, "380119031407", "03:14:07");
                if (withFract) {
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.1", sendFr ? "03:14:07.1" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.01", sendFr ? "03:14:07.01" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.012", sendFr ? "03:14:07.012" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.0123", sendFr ? "03:14:07.0123" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.01234", sendFr ? "03:14:07.01234" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.012345", sendFr ? "03:14:07.012345" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.0123456", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.01234567", sendFr ? "03:14:07.012346" : "03:14:07");
                    subTestBug20391832(props, MysqlType.TIME, "380119031407.012345678", sendFr ? "03:14:07.012346" : "03:14:07");
                }

                // ================================================

                subTestBug20391832(props, MysqlType.DATE, "2038-01-19", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-1-9", "2038-01-09");
                subTestBug20391832(props, MysqlType.DATE, "38-1-9", "2038-01-09");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "030417", "2003-04-17");

                assertThrows(SQLException.class, ".*Conversion from java.time.Duration to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "12 1", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.Duration to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "12 13:04", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.Duration to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "12 1:4:1", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.Duration to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "12 1:04:17.123456789", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.Duration to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "-838:59:59", null);
                    return null;
                });

                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "13:04:17", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "13:04", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "0417", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "03:14:07.012", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to DATE is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "031407.123", null);
                    return null;
                });

                assertThrows(SQLException.class, ".*There is no known date-time pattern for.*", () -> {
                    subTestBug20391832(props, MysqlType.DATE, "031407#12", null); // wrong delimiter
                    return null;
                });

                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.12", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.12", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.12", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.12", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.12", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.12", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "2038#1$19 3@14%7", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "38#1$9 3@4%7", "2038-01-09");

                subTestBug20391832(props, MysqlType.DATE, "20380119031407", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.01", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "20380119031407.012345678", "2038-01-19");

                subTestBug20391832(props, MysqlType.DATE, "380119031407", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.1", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.01", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.012", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.0123", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.01234", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.012345", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.0123456", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.01234567", "2038-01-19");
                subTestBug20391832(props, MysqlType.DATE, "380119031407.012345678", "2038-01-19");

                // ================================================

                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "030417", "2003");

                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to YEAR is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "13:04:17", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to YEAR is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "13:04", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to YEAR is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "0417", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to YEAR is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "03:14:07.012", null);
                    return null;
                });
                assertThrows(SQLException.class, ".*Conversion from java.time.LocalTime to YEAR is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "031407.123", null);
                    return null;
                });

                assertThrows(SQLException.class, ".*There is no known date-time pattern for.*", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "031407#12", null); // wrong delimiter
                    return null;
                });

                assertThrows(SQLException.class, ".*Conversion from java.time.Duration to YEAR is not supported.", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "-838:59:59", null);
                    return null;
                });

                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.12", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.012345678", "2038");

                assertThrows(SQLException.class, ".*There is no known date-time pattern for.*", () -> {
                    subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.0123456789", null); // nanos part is too long
                    return null;
                });

                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.12", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.012345678", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.12", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.012345678", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.12", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.012345678", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.12", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.012345678", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.12", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.012345678", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "2038#1$19 3@14%7", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "38#1$9 3@4%7", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "20380119031407", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.01", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "20380119031407.012345678", "2038");

                subTestBug20391832(props, MysqlType.YEAR, "380119031407", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.1", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.01", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.012", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.0123", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.01234", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.012345", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.0123456", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.01234567", "2038");
                subTestBug20391832(props, MysqlType.YEAR, "380119031407.012345678", "2038");

                testConn.close();
            }
        }
    }

    private void subTestBug20391832(Properties props, MysqlType targetType, String val, String exp) throws Exception {
        boolean useSSPS = Boolean.parseBoolean(props.getProperty(PropertyKey.useServerPrepStmts.getKeyName()));

        this.pstmt.setObject(1, val, targetType);
        String query = this.pstmt.toString().replace(".0)", ")");

        assertEquals((useSSPS ? "com.mysql.cj.jdbc.ServerPreparedStatement[1]: " : "com.mysql.cj.jdbc.ClientPreparedStatement: ")
                + "insert into testBug20391832 values(" + (targetType == MysqlType.YEAR ? exp : "'" + exp + "'") + ")", query);
    }

    /**
     * Tests fix for Bug#20316640, GETTIMESTAMP() CALL WITH CALANDER VALUE NULL RESULTS IN NULLPOINTEREXCEPTION.
     *
     * @throws Exception
     */
    @Test
    public void testBug20316640() throws Exception {
        createTable("testBug20316640", "(c1 timestamp, c2 time, c3 date)");
        this.stmt.execute("insert into testBug20316640 values('2038-01-19 03:14:07','18:59:59','9999-12-31')");
        this.rs = this.stmt.executeQuery("select * from testBug20316640");
        this.rs.next();
        System.out.println("Col 1 [" + this.rs.getTimestamp("c1", null) + "]");
        System.out.println("Col 2 [" + this.rs.getTime("c2", null) + "]");
        System.out.println("Col 3 [" + this.rs.getDate("c3", null) + "]");
    }

    /**
     * Tests fix for Bug#20818678, GETFLOAT() AND GETDOUBLE() CALL ON YEAR COLUMN RESULTS IN EXCEPTION
     *
     * @throws Exception
     */
    @Test
    public void testBug20818678() throws Exception {
        createTable("testBug20818678", "(c1 YEAR)");
        this.stmt.execute("insert into testBug20818678 values(2155)");

        Connection con = null;
        PreparedStatement ps = null;

        try {

            Properties props = new Properties();
            props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
            props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
            for (boolean yearIsDateType : new boolean[] { true, false }) {
                for (boolean useSSPS : new boolean[] { false, true }) {
                    props.setProperty(PropertyKey.yearIsDateType.getKeyName(), "" + yearIsDateType);
                    props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);

                    con = getConnectionWithProps(props);
                    ps = con.prepareStatement("select * from testBug20818678 ");
                    this.rs = ps.executeQuery();
                    this.rs.next();

                    if (yearIsDateType) {
                        assertEquals("2155-01-01", this.rs.getString("c1"));
                    } else {
                        assertEquals("2155", this.rs.getString("c1"));
                    }
                    assertEquals(Float.valueOf(2155), this.rs.getFloat("c1"));
                    assertEquals(Double.valueOf(2155), this.rs.getDouble("c1"));

                    ps.close();
                    con.close();
                }
            }

        } finally {
            if (con != null) {
                con.close();
            }
        }
    }

    /**
     * Tests fix for Bug#21308907, NO WAY TO GET THE FRACTIONAL PART OF A TIME FIELD WHEN USESERVERPREPSTMTS=TRUE.
     *
     * @throws Exception
     */
    @Test
    public void testBug21308907() throws Exception {
        assumeTrue(versionMeetsMinimum(5, 6, 4), "Fractional seconds are not supported in this server version.");

        createTable("testBug21308907", "(c1 time(6))");
        this.stmt.execute("insert into testBug21308907 values('12:59:59.123456')");

        Connection con = null;
        try {
            Properties props = new Properties();
            props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
            props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
            props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "true");
            con = getConnectionWithProps(props);

            this.stmt = con.createStatement();
            this.rs = this.stmt.executeQuery("select * from testBug21308907");
            this.rs.next();

            assertEquals(123456000, this.rs.getTimestamp("c1").getNanos());
            String s = this.rs.getString("c1");
            assertEquals(".123456", s.substring(s.indexOf('.')));

            this.rs.close();

            this.pstmt = con.prepareStatement("select * from testBug21308907 ");
            this.rs = this.pstmt.executeQuery();
            this.rs.next();

            assertEquals(123456000, this.rs.getTimestamp("c1").getNanos());
            s = this.rs.getString("c1");
            assertEquals(".123456", s.substring(s.indexOf('.')));

        } finally {
            if (con != null) {
                con.close();
            }
        }
    }

    /**
     * Tests fix for Bug#22305930, GETTIMESTAMP() CALL ON CLOSED RESULT SET PRODUCES NPE.
     *
     * @throws Exception
     */
    @Test
    public void testBug22305930() throws Exception {
        ResultSet rs1 = this.stmt.executeQuery("select '2015-12-09 16:28:01' as tm");
        rs1.next();
        rs1.close();

        assertThrows(SQLException.class, "Operation not allowed after ResultSet closed", () -> {
            rs1.getTimestamp("tm");
            return null;
        });
    }

    /**
     * Tests fix for Bug#101413 (32099505), JAVA.TIME.LOCALDATETIME CANNOT BE CAST TO JAVA.SQL.TIMESTAMP.
     *
     * @throws Exception
     */
    @Test
    public void testBug101413() throws Exception {
        assumeTrue(supportsTimeZoneNames(this.stmt), "This test requies the server with populated time zone tables.");

        createTable("testBug101413", "(createtime1 TIMESTAMP, createtime2 DATETIME)");

        Properties props = new Properties();
        props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
        props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
        for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) {
            for (boolean preserveInstants : new boolean[] { false, true }) {
                for (boolean useSSPS : new boolean[] { false, true }) {
                    props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession);
                    props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants);
                    props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS);
                    props.setProperty(PropertyKey.rewriteBatchedStatements.getKeyName(), "true");

                    Connection con = getConnectionWithProps(props);
                    PreparedStatement ps = con.prepareStatement("insert into testBug101413(createtime1, createtime2) values(?, ?)");

                    con.setAutoCommit(false);
                    for (int i = 1; i <= 20000; i++) {
                        ps.setObject(1, LocalDateTime.now());
                        ps.setObject(2, LocalDateTime.now());
                        ps.addBatch();
                        if (i % 500 == 0) {
                            ps.executeBatch();
                            ps.clearBatch();
                        }
                    }
                    con.commit();
                }
            }
        }
    }

    /**
     * Tests fix for Bug#104559 (33232419), ResultSet.getObject(i, java.util.Date.class) throws NPE when the value is null.
     *
     * @throws Exception
     */
    @Test
    public void testBug104559() throws Exception {
        createTable("testBug104559", "(`dt` DATE DEFAULT NULL)");
        this.stmt.executeUpdate("INSERT INTO testBug104559 VALUES (null)");

        Properties props = new Properties();
        props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
        props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
        Connection con = getConnectionWithProps(props);
        Statement st = con.createStatement();
        this.rs = st.executeQuery("SELECT * FROM testBug104559");
        assertTrue(this.rs.next());
        assertNull(this.rs.getObject("dt", java.util.Date.class));
        assertFalse(this.rs.next());
    }

}
