无法插入零秒的 java.time.LocalDateTime

Cannot insert java.time.LocalDateTime with zero seconds

科特林:1.6.20

Ktorm: 3.4.1

MySQL Connector/J: 8.0.28

C3P0 连接池:0.9.5.5

我似乎对零秒的“日期时间”(LocalDateTime) 有疑问。我知道这看起来像是 JDBC 驱动程序的问题,但我测试了该驱动程序,它甚至不接受 LocalDateTime(仅 java.util.Date)。如果 LDT 确实有秒数,则不会出现此问题。

我用来隔离问题的最低测试

const val tableName = "test"
const val fieldName = "ldt"

interface TestRecord : Entity<TestRecord> {

    companion object : Entity.Factory<TestRecord>()

    val ldt: LocalDateTime
}

object TestTable : Table<TestRecord>(tableName) {

    val ldt = datetime(fieldName).bindTo { it.ldt }
}

class BlahTest {

    @Test
    fun a() {

        val dataSource = DbConfig.from("test.properties").provideDataSource()
        dataSource.connection.use {
            it.prepareStatement("DROP TABLE IF EXISTS $tableName").execute()
            it.prepareStatement("CREATE TABLE IF NOT EXISTS $tableName ($fieldName TIMESTAMP)").execute()
        }
        val db = Database.connect(dataSource = dataSource, dialect = MySqlDialect())
        val tb = TestTable
        val ldt = LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC)
            .truncatedTo(ChronoUnit.SECONDS)
            //.plusSeconds(1) // with anything but 0 it will work
        db.insertOrUpdate(TestTable) {
            set(tb.ldt, ldt)
        }
    }
}

产生错误

com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Incorrect datetime value: '1970-01-01 00:00:00' for column 'ldt' at row 1

Ktorm 问题:https://github.com/kotlin-orm/ktorm/issues/391

异常是将时间戳值 0 格式化为“1970-01-01 00:00:00”,但 MySQL 不是。

https://dev.mysql.com/doc/refman/8.0/en/datetime.html 说:

MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.)

例如,我在 PDT,所以当我尝试插入时间戳值 0 时,它会将其调整 8 小时为“1969-12-31 16:00:00”,这是不合法的TIMESTAMP 类型范围内的值。

mysql> create table tablename (ldt timestamp);

mysql> insert into tablename values (from_unixtime(0));
ERROR 1292 (22007): Incorrect datetime value: '1969-12-31 16:00:00' for column 'ldt' at row 1

因此,当您生成要插入 TIMESTAMP 的“零”值时,您可以确保考虑时区。

或者您可以使用 DATETIME.

mysql> alter table tablename modify ldt datetime;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> insert into tablename values (from_unixtime(0));
Query OK, 1 row affected (0.00 sec)

我还是推荐使用 DATETIME,因为它支持比 TIMESTAMP 更大的日期范围。这将变得很重要 if you need to store dates past 2038-01-19.