From b0aab75ed9f941dcbafe6e891f1e2b82963126e8 Mon Sep 17 00:00:00 2001 From: dragneelfps Date: Sat, 29 May 2021 03:40:53 +0530 Subject: [PATCH 1/2] Switch to JSR 310 Date and Time API - Removes joda from encoding/decoding - Using Threeten for PeriodDuration --- CHANGELOG.md | 5 ++ build.gradle.kts | 2 +- db-async-common/build.gradle.kts | 4 -- .../java/com/github/jasync/sql/db/RowData.kt | 2 +- .../sql/db/column/DateEncoderDecoder.kt | 14 ++-- .../db/column/LocalDateTimeEncoderDecoder.kt | 14 ++-- .../jasync/sql/db/column/SQLTimeEncoder.kt | 5 +- .../sql/db/column/TimeEncoderDecoder.kt | 14 ++-- .../column/TimeWithTimezoneEncoderDecoder.kt | 10 ++- .../sql/db/column/TimestampEncoderDecoder.kt | 28 ++++---- .../TimestampWithTimezoneEncoderDecoder.kt | 13 ++-- .../jasync/sql/db/util/EncodingUtils.kt | 9 +++ .../com/github/jasync/sql/db/RowDataTest.kt | 2 +- .../db/column/TimestampEncoderDecoderSpec.kt | 18 ++--- gradle.properties | 1 + mysql-async/build.gradle.kts | 4 -- .../sql/db/mysql/binary/BinaryRowEncoder.kt | 17 +++-- .../db/mysql/binary/decoder/DateDecoder.kt | 2 +- .../mysql/binary/decoder/TimestampDecoder.kt | 60 ++++++++-------- .../mysql/binary/encoder/CalendarEncoder.kt | 5 +- .../mysql/binary/encoder/DateTimeEncoder.kt | 7 +- .../mysql/binary/encoder/JavaDateEncoder.kt | 5 +- .../mysql/binary/encoder/LocalDateEncoder.kt | 4 +- .../binary/encoder/LocalDateTimeEncoder.kt | 18 ++--- .../mysql/binary/encoder/LocalTimeEncoder.kt | 18 ++--- .../binary/encoder/ReadableInstantEncoder.kt | 9 +-- .../db/mysql/binary/encoder/SQLDateEncoder.kt | 3 +- .../db/mysql/binary/encoder/SQLTimeEncoder.kt | 4 +- .../binary/encoder/SQLTimestampEncoder.kt | 3 +- .../jasync/sql/db/mysql/ConnectionHelper.kt | 4 +- .../sql/db/mysql/PreparedStatementsSpec.kt | 40 +++++------ .../github/jasync/sql/db/mysql/QuerySpec.kt | 34 ++++----- postgresql-async/README.md | 3 +- postgresql-async/build.gradle.kts | 2 + .../column/PostgreSQLColumnEncoderRegistry.kt | 31 ++++---- .../PostgreSQLIntervalEncoderDecoder.kt | 60 ++++++++-------- .../PostgreSQLTimestampEncoderDecoder.kt | 48 ++++++------- .../sql/db/integration/ArrayTypesSpec.kt | 20 ++++-- .../integration/PostgreSQLConnectionSpec.kt | 4 +- .../db/integration/PreparedStatementSpec.kt | 2 +- .../sql/db/integration/TimeAndDateSpec.kt | 70 ++++++++++--------- 41 files changed, 324 insertions(+), 294 deletions(-) create mode 100644 db-async-common/src/main/java/com/github/jasync/sql/db/util/EncodingUtils.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index a3d011194..87b0109ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2.0.0 + +* Switch to Java Date/Time API from Joda Time [#233](https://github.com/jasync-sql/jasync-sql/pull/233) +* For postgresql, add support for threeten.PeriodDuration for interval data type. [#233](https://github.com/jasync-sql/jasync-sql/pull/233) + ## 1.2.1 * Deploy directly to maven central [#232](https://github.com/jasync-sql/jasync-sql/pull/232). diff --git a/build.gradle.kts b/build.gradle.kts index c537f8a56..8d87f59eb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,7 @@ apply(plugin = "io.github.gradle-nexus.publish-plugin") allprojects { group = "com.github.jasync-sql" - version = "1.2.3" + version = "2.0.0" apply(plugin = "kotlin") apply(plugin = "maven-publish") diff --git a/db-async-common/build.gradle.kts b/db-async-common/build.gradle.kts index 0832ac80c..a80ece15a 100644 --- a/db-async-common/build.gradle.kts +++ b/db-async-common/build.gradle.kts @@ -1,8 +1,6 @@ val KOTLIN_VERSION: String by project val KOTLIN_COROUTINES_VERSION: String by project val SL4J_VERSION: String by project -val JODA_VERSION: String by project -val JODA_CONVERT_VERSION: String by project val NETTY_VERSION: String by project val KOTLIN_LOGGING_VERSION: String by project @@ -17,8 +15,6 @@ dependencies { api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION") compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:$KOTLIN_COROUTINES_VERSION") implementation("org.slf4j:slf4j-api:$SL4J_VERSION") - implementation("joda-time:joda-time:$JODA_VERSION") - implementation("org.joda:joda-convert:$JODA_CONVERT_VERSION") implementation("io.netty:netty-transport:$NETTY_VERSION") implementation("io.netty:netty-handler:$NETTY_VERSION") compileOnly("io.netty:netty-transport-native-epoll:$NETTY_VERSION:linux-x86_64") diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/RowData.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/RowData.kt index 195f6b750..7130f0fde 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/RowData.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/RowData.kt @@ -1,7 +1,7 @@ package com.github.jasync.sql.db import com.github.jasync.sql.db.util.XXX -import org.joda.time.LocalDateTime +import java.time.LocalDateTime /** * diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/DateEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/DateEncoderDecoder.kt index 87252a236..e6c9f75fd 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/DateEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/DateEncoderDecoder.kt @@ -1,27 +1,27 @@ package com.github.jasync.sql.db.column import com.github.jasync.sql.db.exceptions.DateEncoderNotAvailableException -import org.joda.time.LocalDate -import org.joda.time.ReadablePartial -import org.joda.time.format.DateTimeFormat +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAccessor object DateEncoderDecoder : ColumnEncoderDecoder { private const val ZeroedDate = "0000-00-00" - private val formatter = DateTimeFormat.forPattern("yyyy-MM-dd") + private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") override fun decode(value: String): LocalDate? = if (ZeroedDate == value) { null } else { - this.formatter.parseLocalDate(value) + LocalDate.parse(value, this.formatter) } override fun encode(value: Any): String { return when (value) { - is java.sql.Date -> this.formatter.print(LocalDate(value)) - is ReadablePartial -> this.formatter.print(value) + is java.sql.Date -> value.toLocalDate().format(this.formatter) + is TemporalAccessor -> this.formatter.format(value) else -> throw DateEncoderNotAvailableException(value) } } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt index 07ba88a48..18830df0b 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt @@ -1,27 +1,25 @@ package com.github.jasync.sql.db.column -import org.joda.time.LocalDateTime -import org.joda.time.format.DateTimeFormatterBuilder +import com.github.jasync.sql.db.util.microsecondsFormatter +import java.time.LocalDateTime +import java.time.format.DateTimeFormatterBuilder object LocalDateTimeEncoderDecoder : ColumnEncoderDecoder { private const val ZeroedTimestamp = "0000-00-00 00:00:00" - private val optional = DateTimeFormatterBuilder() - .appendPattern(".SSSSSS").toParser() - private val format = DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd HH:mm:ss") - .appendOptional(optional) + .append(microsecondsFormatter) .toFormatter() override fun encode(value: Any): String = - format.print(value as LocalDateTime) + (value as LocalDateTime).format(format) override fun decode(value: String): LocalDateTime? = if (ZeroedTimestamp == value) { null } else { - format.parseLocalDateTime(value) + LocalDateTime.parse(value, format) } } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/SQLTimeEncoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/SQLTimeEncoder.kt index b0ca9a3c3..c1cc9949c 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/SQLTimeEncoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/SQLTimeEncoder.kt @@ -1,7 +1,6 @@ package com.github.jasync.sql.db.column -import org.joda.time.LocalTime -import org.joda.time.format.DateTimeFormatterBuilder +import java.time.format.DateTimeFormatterBuilder object SQLTimeEncoder : ColumnEncoder { @@ -12,6 +11,6 @@ object SQLTimeEncoder : ColumnEncoder { override fun encode(value: Any): String { val time = value as java.sql.Time - return format.print(LocalTime(time.time)) + return time.toLocalTime().format(format) } } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeEncoderDecoder.kt index 083a3f1eb..69ae1cb35 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeEncoderDecoder.kt @@ -1,19 +1,17 @@ package com.github.jasync.sql.db.column -import org.joda.time.LocalTime -import org.joda.time.format.DateTimeFormatterBuilder +import com.github.jasync.sql.db.util.microsecondsFormatter +import java.time.LocalTime +import java.time.format.DateTimeFormatterBuilder open class TimeEncoderDecoder : ColumnEncoderDecoder { companion object { val Instance = TimeEncoderDecoder() } - private val optional = DateTimeFormatterBuilder() - .appendPattern(".SSSSSS").toParser() - private val format = DateTimeFormatterBuilder() .appendPattern("HH:mm:ss") - .appendOptional(optional) + .appendOptional(microsecondsFormatter) .toFormatter() private val printer = DateTimeFormatterBuilder() @@ -23,8 +21,8 @@ open class TimeEncoderDecoder : ColumnEncoderDecoder { open fun formatter() = format override fun decode(value: String): LocalTime = - format.parseLocalTime(value) + LocalTime.parse(value, formatter()) override fun encode(value: Any): String = - this.printer.print(value as LocalTime) + (value as LocalTime).format(printer) } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeWithTimezoneEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeWithTimezoneEncoderDecoder.kt index 0c981c562..5853c2b74 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeWithTimezoneEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimeWithTimezoneEncoderDecoder.kt @@ -1,11 +1,15 @@ package com.github.jasync.sql.db.column -import org.joda.time.format.DateTimeFormat -import org.joda.time.format.DateTimeFormatter +import com.github.jasync.sql.db.util.microsecondsFormatter +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder object TimeWithTimezoneEncoderDecoder : TimeEncoderDecoder() { - private val format = DateTimeFormat.forPattern("HH:mm:ss.SSSSSSZ") + private val format = DateTimeFormatterBuilder().appendPattern("HH:mm:ss") + .appendOptional(microsecondsFormatter) + .appendPattern("[X][Z]") + .toFormatter() override fun formatter(): DateTimeFormatter = format } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampEncoderDecoder.kt index 84b0974d6..5351ada17 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampEncoderDecoder.kt @@ -2,12 +2,12 @@ package com.github.jasync.sql.db.column import com.github.jasync.sql.db.exceptions.DateEncoderNotAvailableException import java.sql.Timestamp +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatterBuilder +import java.time.temporal.TemporalAccessor import java.util.Calendar import java.util.Date -import org.joda.time.DateTime -import org.joda.time.LocalDateTime -import org.joda.time.ReadableDateTime -import org.joda.time.format.DateTimeFormatterBuilder open class TimestampEncoderDecoder : ColumnEncoderDecoder { companion object { @@ -17,9 +17,9 @@ open class TimestampEncoderDecoder : ColumnEncoderDecoder { } private val optional = DateTimeFormatterBuilder() - .appendPattern(MillisFormat).toParser() + .appendPattern(MillisFormat).toFormatter() private val optionalTimeZone = DateTimeFormatterBuilder() - .appendPattern("Z").toParser() + .appendPattern("[X][Z]").toFormatter() private val builder = DateTimeFormatterBuilder() .appendPattern(BaseFormat) @@ -34,19 +34,23 @@ open class TimestampEncoderDecoder : ColumnEncoderDecoder { private val format = builder.toFormatter() + // java.util.Dates are constructed using the system default timezone, replicate this behavior when encoding a legacy date + private fun encodeLegacyDate(legacyDate: Date): String = + legacyDate.toInstant().atOffset(ZoneOffset.UTC).format(this.timezonedPrinter) + open fun formatter() = format override fun decode(value: String): Any { - return formatter().parseLocalDateTime(value) + return LocalDateTime.parse(value, formatter()) } override fun encode(value: Any): String { return when (value) { - is Timestamp -> this.timezonedPrinter.print(DateTime(value)) - is Date -> this.timezonedPrinter.print(DateTime(value)) - is Calendar -> this.timezonedPrinter.print(DateTime(value)) - is LocalDateTime -> this.nonTimezonedPrinter.print(value) - is ReadableDateTime -> this.timezonedPrinter.print(value) + is Timestamp -> encodeLegacyDate(value) + is Date -> encodeLegacyDate(value) + is Calendar -> encodeLegacyDate(value.time) + is LocalDateTime -> this.nonTimezonedPrinter.format(value) + is TemporalAccessor -> this.timezonedPrinter.format(value) else -> throw DateEncoderNotAvailableException(value) } } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampWithTimezoneEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampWithTimezoneEncoderDecoder.kt index ac42f84ef..42e1e9ab7 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampWithTimezoneEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/TimestampWithTimezoneEncoderDecoder.kt @@ -1,15 +1,20 @@ package com.github.jasync.sql.db.column -import org.joda.time.format.DateTimeFormat -import org.joda.time.format.DateTimeFormatter +import com.github.jasync.sql.db.util.microsecondsFormatter +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder object TimestampWithTimezoneEncoderDecoder : TimestampEncoderDecoder() { - private val format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSSSSSZ") + private val format = DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss") + .appendOptional(microsecondsFormatter) + .appendPattern("[X][Z]") + .toFormatter() override fun formatter(): DateTimeFormatter = format override fun decode(value: String): Any { - return formatter().parseDateTime(value) + return OffsetDateTime.parse(value, formatter()) } } diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/util/EncodingUtils.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/util/EncodingUtils.kt new file mode 100644 index 000000000..9e0344efb --- /dev/null +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/util/EncodingUtils.kt @@ -0,0 +1,9 @@ +package com.github.jasync.sql.db.util + +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder + +internal const val ALL_MICROS_FORMAT = ".[SSSSSS][SSSSS][SSSS][SSS][SS][S]" + +val microsecondsFormatter: DateTimeFormatter = DateTimeFormatterBuilder() + .appendPattern(ALL_MICROS_FORMAT).toFormatter() diff --git a/db-async-common/src/test/java/com/github/jasync/sql/db/RowDataTest.kt b/db-async-common/src/test/java/com/github/jasync/sql/db/RowDataTest.kt index 13670e895..5010c8186 100644 --- a/db-async-common/src/test/java/com/github/jasync/sql/db/RowDataTest.kt +++ b/db-async-common/src/test/java/com/github/jasync/sql/db/RowDataTest.kt @@ -1,7 +1,7 @@ package com.github.jasync.sql.db +import java.time.LocalDateTime import org.assertj.core.api.Assertions.assertThat -import org.joda.time.LocalDateTime import org.junit.Test class RowDataTest { diff --git a/db-async-common/src/test/java/com/github/jasync/sql/db/column/TimestampEncoderDecoderSpec.kt b/db-async-common/src/test/java/com/github/jasync/sql/db/column/TimestampEncoderDecoderSpec.kt index 0e8f86538..5ef0d1377 100644 --- a/db-async-common/src/test/java/com/github/jasync/sql/db/column/TimestampEncoderDecoderSpec.kt +++ b/db-async-common/src/test/java/com/github/jasync/sql/db/column/TimestampEncoderDecoderSpec.kt @@ -1,25 +1,25 @@ package com.github.jasync.sql.db.column +import java.sql.Date import java.sql.Timestamp +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatterBuilder import kotlin.test.assertEquals -import org.joda.time.DateTime -import org.joda.time.format.DateTimeFormatterBuilder import org.junit.Test class TimestampEncoderDecoderSpec { val encoder = TimestampEncoderDecoder() - val dateTime = DateTime() - .withDate(2013, 12, 27) - .withTime(8, 40, 50, 800) + val dateTime = OffsetDateTime.of(2013, 12, 27, 8, 40, 50, 800000000, ZoneOffset.UTC) val result = "2013-12-27 08:40:50.800000" val formatter = DateTimeFormatterBuilder().appendPattern("Z").toFormatter() - val resultWithTimezone = "2013-12-27 08:40:50.800000${formatter.print(dateTime)}" + val resultWithTimezone = "2013-12-27 08:40:50.800000${dateTime.format(formatter)}" @Test fun `should print a timestamp`() { - val timestamp = Timestamp(dateTime.toDate().time) + val timestamp = Timestamp.from(dateTime.toInstant()) assertEquals(encoder.encode(timestamp), resultWithTimezone) } @@ -30,13 +30,13 @@ class TimestampEncoderDecoderSpec { @Test fun `should print a date`() { - assertEquals(encoder.encode(dateTime.toDate()), resultWithTimezone) + assertEquals(encoder.encode(Date.from(dateTime.toInstant())), resultWithTimezone) } @Test fun `should print a calendar`() { val calendar = java.util.Calendar.getInstance() - calendar.time = dateTime.toDate() + calendar.time = Date.from(dateTime.toInstant()) encoder.encode(calendar) === resultWithTimezone } diff --git a/gradle.properties b/gradle.properties index 8f09ba81c..6de07205c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,4 @@ LOGBACK_VERSION=1.1.8 TEST_CONTAINERS_VERSION=1.15.1 MYSQL_CONNECTOR_VERSION=5.1.47 AWAITILITY_VERSION=3.1.5 +THREETEN_EXTRA=1.6.0 diff --git a/mysql-async/build.gradle.kts b/mysql-async/build.gradle.kts index aca208b1f..5be33d702 100644 --- a/mysql-async/build.gradle.kts +++ b/mysql-async/build.gradle.kts @@ -1,8 +1,6 @@ val KOTLIN_VERSION: String by project val KOTLIN_COROUTINES_VERSION: String by project val SL4J_VERSION: String by project -val JODA_VERSION: String by project -val JODA_CONVERT_VERSION: String by project val NETTY_VERSION: String by project val KOTLIN_LOGGING_VERSION: String by project @@ -20,8 +18,6 @@ dependencies { compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION") compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:$KOTLIN_COROUTINES_VERSION") compile("org.slf4j:slf4j-api:$SL4J_VERSION") - compile("joda-time:joda-time:$JODA_VERSION") - compile("org.joda:joda-convert:$JODA_CONVERT_VERSION") compile("io.netty:netty-transport:$NETTY_VERSION") compile("io.netty:netty-handler:$NETTY_VERSION") compile("io.github.microutils:kotlin-logging:$KOTLIN_LOGGING_VERSION") diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/BinaryRowEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/BinaryRowEncoder.kt index 6f5d5c5b6..7a661a138 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/BinaryRowEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/BinaryRowEncoder.kt @@ -30,12 +30,11 @@ import java.math.BigInteger import java.nio.ByteBuffer import java.nio.charset.Charset import java.time.Duration -import org.joda.time.DateTime -import org.joda.time.LocalDate -import org.joda.time.LocalDateTime -import org.joda.time.LocalTime -import org.joda.time.ReadableDateTime -import org.joda.time.ReadableInstant +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime class BinaryRowEncoder(charset: Charset) { @@ -59,7 +58,7 @@ class BinaryRowEncoder(charset: Charset) { Double::class.java to DoubleEncoder, java.lang.Double::class.java to DoubleEncoder, LocalDateTime::class.java to LocalDateTimeEncoder, - DateTime::class.java to DateTimeEncoder, + OffsetDateTime::class.java to DateTimeEncoder, LocalDate::class.java to LocalDateEncoder, java.util.Date::class.java to JavaDateEncoder, java.sql.Timestamp::class.java to SQLTimestampEncoder, @@ -78,8 +77,8 @@ class BinaryRowEncoder(charset: Charset) { is CharSequence -> this.stringEncoder is java.math.BigInteger -> this.stringEncoder is BigDecimal -> this.stringEncoder - is ReadableDateTime -> DateTimeEncoder - is ReadableInstant -> ReadableInstantEncoder + is OffsetDateTime -> DateTimeEncoder + is Instant -> ReadableInstantEncoder is LocalDateTime -> LocalDateTimeEncoder is java.sql.Timestamp -> SQLTimestampEncoder is java.sql.Date -> SQLDateEncoder diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/DateDecoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/DateDecoder.kt index 453c2f09d..1f44e51f3 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/DateDecoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/DateDecoder.kt @@ -1,7 +1,7 @@ package com.github.jasync.sql.db.mysql.binary.decoder import io.netty.buffer.ByteBuf -import org.joda.time.LocalDate +import java.time.LocalDate object DateDecoder : BinaryDecoder { override fun decode(buffer: ByteBuf): LocalDate? { diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/TimestampDecoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/TimestampDecoder.kt index 07dcf499d..1d71d4a2b 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/TimestampDecoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/decoder/TimestampDecoder.kt @@ -1,8 +1,8 @@ package com.github.jasync.sql.db.mysql.binary.decoder import io.netty.buffer.ByteBuf +import java.time.LocalDateTime import mu.KotlinLogging -import org.joda.time.LocalDateTime private val logger = KotlinLogging.logger {} @@ -12,37 +12,33 @@ object TimestampDecoder : BinaryDecoder { return when (size) { 0.toShort() -> null - 4.toShort() -> LocalDateTime() - .withDate( - buffer.readUnsignedShort(), - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt() - ) - .withTime(0, 0, 0, 0) - 7.toShort() -> LocalDateTime() - .withDate( - buffer.readUnsignedShort(), - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt() - ) - .withTime( - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt(), - 0 - ) - 11.toShort() -> LocalDateTime() - .withDate( - buffer.readUnsignedShort(), - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt() - ) - .withTime( - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedByte().toInt(), - buffer.readUnsignedInt().toInt() / 1000 - ) + 4.toShort() -> LocalDateTime.of( + buffer.readUnsignedShort(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + 0, 0, 0, 0 + ) + 7.toShort() -> LocalDateTime.of( + buffer.readUnsignedShort(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + 0 + ) + 11.toShort() -> LocalDateTime.of( + buffer.readUnsignedShort(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedByte().toInt(), + buffer.readUnsignedInt().toInt() * 1000 + ) + // millis = x / 1000 + // nanos = millis * 1000000 + // x * 1000 else -> { logger.warn { "unknown decoded size $size" } null diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/CalendarEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/CalendarEncoder.kt index 6830a3d5d..e3ac85bc4 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/CalendarEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/CalendarEncoder.kt @@ -2,13 +2,14 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf +import java.time.LocalDateTime +import java.time.ZoneOffset import java.util.Calendar -import org.joda.time.LocalDateTime object CalendarEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { val calendar = value as Calendar - LocalDateTimeEncoder.encode(LocalDateTime(calendar.timeInMillis), buffer) + LocalDateTimeEncoder.encode(LocalDateTime.ofInstant(calendar.toInstant(), ZoneOffset.UTC), buffer) } override fun encodesTo(): Int = ColumnTypes.FIELD_TYPE_TIMESTAMP diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/DateTimeEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/DateTimeEncoder.kt index 7b65c82fd..6784024d4 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/DateTimeEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/DateTimeEncoder.kt @@ -2,14 +2,13 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDateTime -import org.joda.time.ReadableDateTime +import java.time.OffsetDateTime object DateTimeEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { - val instant = value as ReadableDateTime + val zdt = value as OffsetDateTime - return LocalDateTimeEncoder.encode(LocalDateTime(instant.millis), buffer) + return LocalDateTimeEncoder.encode(zdt.toLocalDateTime(), buffer) } override fun encodesTo(): Int = ColumnTypes.FIELD_TYPE_TIMESTAMP diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/JavaDateEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/JavaDateEncoder.kt index af30a3bb0..d4ea189fc 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/JavaDateEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/JavaDateEncoder.kt @@ -2,12 +2,13 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDateTime +import java.time.LocalDateTime +import java.time.ZoneOffset object JavaDateEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { val date = value as java.util.Date - LocalDateTimeEncoder.encode(LocalDateTime(date.time), buffer) + LocalDateTimeEncoder.encode(LocalDateTime.ofInstant(date.toInstant(), ZoneOffset.UTC), buffer) } override fun encodesTo(): Int = ColumnTypes.FIELD_TYPE_TIMESTAMP diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateEncoder.kt index 8e2a03700..0ea3e389c 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateEncoder.kt @@ -2,7 +2,7 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDate +import java.time.LocalDate object LocalDateEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { @@ -10,7 +10,7 @@ object LocalDateEncoder : BinaryEncoder { buffer.writeByte(4) buffer.writeShort(date.year) - buffer.writeByte(date.monthOfYear) + buffer.writeByte(date.monthValue) buffer.writeByte(date.dayOfMonth) } diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateTimeEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateTimeEncoder.kt index fd924c87e..eac73b528 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateTimeEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalDateTimeEncoder.kt @@ -2,30 +2,30 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDateTime +import java.time.LocalDateTime object LocalDateTimeEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { val instant = value as LocalDateTime - val hasMillis = instant.millisOfSecond != 0 + val hasNano = instant.nano != 0 - if (hasMillis) { + if (hasNano) { buffer.writeByte(11) } else { buffer.writeByte(7) } buffer.writeShort(instant.year) - buffer.writeByte(instant.monthOfYear) + buffer.writeByte(instant.monthValue) buffer.writeByte(instant.dayOfMonth) - buffer.writeByte(instant.hourOfDay) - buffer.writeByte(instant.minuteOfHour) - buffer.writeByte(instant.secondOfMinute) + buffer.writeByte(instant.hour) + buffer.writeByte(instant.minute) + buffer.writeByte(instant.second) - if (hasMillis) { - buffer.writeInt(instant.millisOfSecond * 1000) + if (hasNano) { + buffer.writeInt(instant.nano / 1000) } } diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalTimeEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalTimeEncoder.kt index d56c55eb6..2887b584f 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalTimeEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/LocalTimeEncoder.kt @@ -2,21 +2,21 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalTime +import java.time.LocalTime object LocalTimeEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { val time = value as LocalTime - val hasMillis = time.millisOfSecond != 0 + val hasNanos = time.nano != 0 - if (hasMillis) { + if (hasNanos) { buffer.writeByte(12) } else { buffer.writeByte(8) } - if (time.millisOfDay > 0) { + if (time.nano > 0) { buffer.writeByte(0) } else { buffer.writeByte(1) @@ -24,12 +24,12 @@ object LocalTimeEncoder : BinaryEncoder { buffer.writeInt(0) - buffer.writeByte(time.hourOfDay) - buffer.writeByte(time.minuteOfHour) - buffer.writeByte(time.secondOfMinute) + buffer.writeByte(time.hour) + buffer.writeByte(time.minute) + buffer.writeByte(time.second) - if (hasMillis) { - buffer.writeInt(time.millisOfSecond * 1000) + if (hasNanos) { + buffer.writeInt(time.nano) } } diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/ReadableInstantEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/ReadableInstantEncoder.kt index db8775ae6..50c6a51d7 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/ReadableInstantEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/ReadableInstantEncoder.kt @@ -2,13 +2,14 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDateTime -import org.joda.time.ReadableInstant +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneOffset object ReadableInstantEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { - val date = value as ReadableInstant - LocalDateTimeEncoder.encode(LocalDateTime(date.millis), buffer) + val date = value as Instant + LocalDateTimeEncoder.encode(LocalDateTime.ofInstant(date, ZoneOffset.UTC), buffer) } override fun encodesTo(): Int = ColumnTypes.FIELD_TYPE_TIMESTAMP diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLDateEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLDateEncoder.kt index 463e550ef..474d269a5 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLDateEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLDateEncoder.kt @@ -2,13 +2,12 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDate object SQLDateEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { val date = value as java.sql.Date - LocalDateEncoder.encode(LocalDate(date), buffer) + LocalDateEncoder.encode(date.toLocalDate(), buffer) } override fun encodesTo(): Int = ColumnTypes.FIELD_TYPE_DATE diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimeEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimeEncoder.kt index 3d8ef6c77..788d286e9 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimeEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimeEncoder.kt @@ -2,12 +2,10 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalTime object SQLTimeEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { - val sqlTime = (value as java.sql.Time).time - val time = LocalTime(sqlTime) + val time = (value as java.sql.Time).toLocalTime() LocalTimeEncoder.encode(time, buffer) } diff --git a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimestampEncoder.kt b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimestampEncoder.kt index f1ce37db7..b779a8b07 100644 --- a/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimestampEncoder.kt +++ b/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/binary/encoder/SQLTimestampEncoder.kt @@ -2,12 +2,11 @@ package com.github.jasync.sql.db.mysql.binary.encoder import com.github.jasync.sql.db.mysql.column.ColumnTypes import io.netty.buffer.ByteBuf -import org.joda.time.LocalDateTime object SQLTimestampEncoder : BinaryEncoder { override fun encode(value: Any, buffer: ByteBuf) { val date = value as java.sql.Timestamp - LocalDateTimeEncoder.encode(LocalDateTime(date.time), buffer) + LocalDateTimeEncoder.encode(date.toLocalDateTime(), buffer) } override fun encodesTo(): Int = ColumnTypes.FIELD_TYPE_TIMESTAMP diff --git a/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/ConnectionHelper.kt b/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/ConnectionHelper.kt index 4811b3ba6..0cbd0affe 100644 --- a/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/ConnectionHelper.kt +++ b/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/ConnectionHelper.kt @@ -66,8 +66,8 @@ open class ConnectionHelper : ContainerHelper() { """CREATE TEMPORARY TABLE posts ( id INT NOT NULL AUTO_INCREMENT, created_at_date DATE not null, - created_at_datetime DATETIME not null, - created_at_timestamp TIMESTAMP not null, + created_at_datetime DATETIME(6) not null, + created_at_timestamp TIMESTAMP(6) not null, created_at_time TIME not null, created_at_year YEAR not null, primary key (id) diff --git a/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/PreparedStatementsSpec.kt b/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/PreparedStatementsSpec.kt index d1e2e56b5..2175c290c 100644 --- a/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/PreparedStatementsSpec.kt +++ b/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/PreparedStatementsSpec.kt @@ -7,13 +7,13 @@ import com.github.jasync.sql.db.util.map import java.math.BigDecimal import java.sql.Timestamp import java.time.Duration +import java.time.LocalDate +import java.time.LocalDateTime import java.util.function.Supplier import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull import org.assertj.core.api.Assertions.assertThat -import org.joda.time.LocalDate -import org.joda.time.LocalDateTime import org.junit.Test import org.slf4j.MDC @@ -80,29 +80,29 @@ class PreparedStatementsSpec : ConnectionHelper() { executeQuery(connection, insertTableTimeColumns) val result = assertNotNull(assertNotNull(executePreparedStatement(connection, "SELECT * FROM posts").rows)[0]) - val date = result["created_at_date"] as org.joda.time.LocalDate + val date = result["created_at_date"] as LocalDate assertEquals(2038, date.year) - assertEquals(1, date.monthOfYear) + assertEquals(1, date.monthValue) assertEquals(19, date.dayOfMonth) - val dateTime = result["created_at_datetime"] as org.joda.time.LocalDateTime + val dateTime = result["created_at_datetime"] as LocalDateTime assertEquals(2013, dateTime.year) - assertEquals(1, dateTime.monthOfYear) + assertEquals(1, dateTime.monthValue) assertEquals(19, dateTime.dayOfMonth) - assertEquals(3, dateTime.hourOfDay) - assertEquals(14, dateTime.minuteOfHour) - assertEquals(7, dateTime.secondOfMinute) + assertEquals(3, dateTime.hour) + assertEquals(14, dateTime.minute) + assertEquals(7, dateTime.second) - val timestamp = result["created_at_timestamp"] as org.joda.time.LocalDateTime + val timestamp = result["created_at_timestamp"] as LocalDateTime assertEquals(2020, timestamp.year) - assertEquals(1, timestamp.monthOfYear) + assertEquals(1, timestamp.monthValue) assertEquals(19, timestamp.dayOfMonth) - assertEquals(3, timestamp.hourOfDay) - assertEquals(14, timestamp.minuteOfHour) - assertEquals(7, timestamp.secondOfMinute) + assertEquals(3, timestamp.hour) + assertEquals(14, timestamp.minute) + assertEquals(7, timestamp.second) assertEquals( Duration.ofHours(3).plus(Duration.ofMinutes(14).plus(Duration.ofSeconds(7))), @@ -214,9 +214,9 @@ class PreparedStatementsSpec : ConnectionHelper() { values ( ?, ?, ?, ?, ? ) """ - val date = LocalDate(2011, 9, 8) - val dateTime = LocalDateTime(2012, 5, 27, 15, 29, 55) - val timestamp = Timestamp(dateTime.toDateTime().millis) + val date = LocalDate.of(2011, 9, 8) + val dateTime = LocalDateTime.of(2012, 5, 27, 15, 29, 55, 1000) + val timestamp = Timestamp.valueOf(dateTime) val time = Duration.ofHours(3) + Duration.ofMinutes(5) + Duration.ofSeconds(10) val year = 2012.toShort() @@ -235,7 +235,7 @@ class PreparedStatementsSpec : ConnectionHelper() { val row = assertNotNull(rows[0]) assertEquals(date, row["created_at_date"]) - assertEquals(LocalDateTime(timestamp.time), row["created_at_timestamp"]) + assertEquals(timestamp.toLocalDateTime(), row["created_at_timestamp"]) assertEquals(time, row["created_at_time"]) assertEquals(year, row["created_at_year"]) assertEquals(dateTime, row["created_at_datetime"]) @@ -262,7 +262,7 @@ class PreparedStatementsSpec : ConnectionHelper() { Duration.ofSeconds(7) + Duration.ofMillis(19) - val timestamp = LocalDateTime(2013, 1, 19, 3, 14, 7, 19) + val timestamp = LocalDateTime.of(2013, 1, 19, 3, 14, 7, 19 * 1000_000) val select = "SELECT * FROM posts" withConnection { connection -> @@ -349,7 +349,7 @@ class PreparedStatementsSpec : ConnectionHelper() { connection, "CREATE TEMPORARY TABLE timestamps ( id INT NOT NULL, moment TIMESTAMP NULL, primary key (id))" ) - val moment = LocalDateTime.now().withMillisOfDay(0) // cut off millis to match timestamp + val moment = LocalDateTime.now().withNano(0) // cut off millis to match timestamp executePreparedStatement( connection, "INSERT INTO timestamps (moment, id) VALUES (?, ?)", diff --git a/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/QuerySpec.kt b/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/QuerySpec.kt index 4c64fd929..56209739e 100644 --- a/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/QuerySpec.kt +++ b/mysql-async/src/test/java/com/github/jasync/sql/db/mysql/QuerySpec.kt @@ -12,11 +12,11 @@ import com.github.jasync.sql.db.util.map import io.netty.util.CharsetUtil import java.math.BigDecimal import java.time.Duration +import java.time.LocalDate +import java.time.LocalDateTime import java.util.concurrent.ExecutionException import java.util.function.Supplier import org.assertj.core.api.Assertions.assertThat -import org.joda.time.LocalDate -import org.joda.time.LocalDateTime import org.junit.Test import org.slf4j.MDC @@ -103,25 +103,25 @@ class QuerySpec : ConnectionHelper() { val date = result("created_at_date") as LocalDate - assertThat(date.getYear()).isEqualTo(2038) - assertThat(date.getMonthOfYear()).isEqualTo(1) - assertThat(date.getDayOfMonth()).isEqualTo(19) + assertThat(date.year).isEqualTo(2038) + assertThat(date.monthValue).isEqualTo(1) + assertThat(date.dayOfMonth).isEqualTo(19) val dateTime = result("created_at_datetime") as LocalDateTime - assertThat(dateTime.getYear()).isEqualTo(2013) - assertThat(dateTime.getMonthOfYear()).isEqualTo(1) - assertThat(dateTime.getDayOfMonth()).isEqualTo(19) - assertThat(dateTime.getHourOfDay()).isEqualTo(3) - assertThat(dateTime.getMinuteOfHour()).isEqualTo(14) - assertThat(dateTime.getSecondOfMinute()).isEqualTo(7) + assertThat(dateTime.year).isEqualTo(2013) + assertThat(dateTime.monthValue).isEqualTo(1) + assertThat(dateTime.dayOfMonth).isEqualTo(19) + assertThat(dateTime.hour).isEqualTo(3) + assertThat(dateTime.minute).isEqualTo(14) + assertThat(dateTime.second).isEqualTo(7) val timestamp = result("created_at_timestamp") as LocalDateTime - assertThat(timestamp.getYear()).isEqualTo(2020) - assertThat(timestamp.getMonthOfYear()).isEqualTo(1) - assertThat(timestamp.getDayOfMonth()).isEqualTo(19) - assertThat(timestamp.getHourOfDay()).isEqualTo(3) - assertThat(timestamp.getMinuteOfHour()).isEqualTo(14) - assertThat(timestamp.getSecondOfMinute()).isEqualTo(7) + assertThat(timestamp.year).isEqualTo(2020) + assertThat(timestamp.monthValue).isEqualTo(1) + assertThat(timestamp.dayOfMonth).isEqualTo(19) + assertThat(timestamp.hour).isEqualTo(3) + assertThat(timestamp.minute).isEqualTo(14) + assertThat(timestamp.second).isEqualTo(7) assertThat(result("created_at_time")).isEqualTo( Duration.ofHours(3).plus( diff --git a/postgresql-async/README.md b/postgresql-async/README.md index 9f4423311..57b42d896 100644 --- a/postgresql-async/README.md +++ b/postgresql-async/README.md @@ -18,7 +18,8 @@ This driver contains Java code from the [JDBC PostgreSQL](http://jdbc.postgresql - execute direct queries (without portals/prepared statements) - portals/prepared statements - parses most of the basic PostgreSQL types, other types are parsed as string -- date, time and timestamp types are handled as JodaTime objects and **not** as **java.util.Date** objects +- date, time and timestamp types are handled as JSR 310 objects and **not** as **java.util.Date** objects +- Interval are handled as JSR 310 (Period, Duration) and PeriodDuration(https://www.threeten.org/threeten-extra/index.html) - all work is done using the new `scala.concurrent.Future` and `scala.concurrent.Promise` objects - support for Byte arrays if using PostgreSQL >= 9.0 - support for LISTEN/NOTIFY operations (check [ListenNotifySpec](https://github.com/mauricio/postgresql-async/blob/master/postgresql-async/src/test/scala/com/github/mauricio/async/db/postgresql/ListenNotifySpec.scala) for an example on how to use it ); diff --git a/postgresql-async/build.gradle.kts b/postgresql-async/build.gradle.kts index 832a401bb..674115783 100644 --- a/postgresql-async/build.gradle.kts +++ b/postgresql-async/build.gradle.kts @@ -6,6 +6,7 @@ val JODA_CONVERT_VERSION: String by project val NETTY_VERSION: String by project val KOTLIN_LOGGING_VERSION: String by project val SCRAM_CLIENT_VERSION: String by project +val THREETEN_EXTRA: String by project val JUNIT_VERSION: String by project val ASSERTJ_VERSION: String by project @@ -26,6 +27,7 @@ dependencies { compile("io.netty:netty-handler:$NETTY_VERSION") compile("io.github.microutils:kotlin-logging:$KOTLIN_LOGGING_VERSION") compile("com.ongres.scram:client:$SCRAM_CLIENT_VERSION") + compile("org.threeten:threeten-extra:$THREETEN_EXTRA") testImplementation("junit:junit:$JUNIT_VERSION") testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION") testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION") diff --git a/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLColumnEncoderRegistry.kt b/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLColumnEncoderRegistry.kt index 7175a5b26..2c332497a 100644 --- a/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLColumnEncoderRegistry.kt +++ b/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLColumnEncoderRegistry.kt @@ -18,15 +18,15 @@ import com.github.jasync.sql.db.column.UUIDEncoderDecoder import io.netty.buffer.ByteBuf import java.math.BigDecimal import java.nio.ByteBuffer -import org.joda.time.DateTime -import org.joda.time.LocalDate -import org.joda.time.LocalDateTime -import org.joda.time.LocalTime -import org.joda.time.ReadableDateTime -import org.joda.time.ReadableDuration -import org.joda.time.ReadableInstant -import org.joda.time.ReadablePartial -import org.joda.time.ReadablePeriod +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.Period +import java.time.temporal.TemporalAccessor +import org.threeten.extra.PeriodDuration class PostgreSQLColumnEncoderRegistry : ColumnEncoderRegistry { @@ -62,12 +62,13 @@ class PostgreSQLColumnEncoderRegistry : ColumnEncoderRegistry { LocalDate::class.java to (DateEncoderDecoder to ColumnTypes.Date), LocalDateTime::class.java to (TimestampEncoderDecoder.Instance to ColumnTypes.Timestamp), - DateTime::class.java to (TimestampWithTimezoneEncoderDecoder to ColumnTypes.TimestampWithTimezone), - ReadableDateTime::class.java to (TimestampWithTimezoneEncoderDecoder to ColumnTypes.TimestampWithTimezone), - ReadableInstant::class.java to (DateEncoderDecoder to ColumnTypes.Date), + OffsetDateTime::class.java to (TimestampWithTimezoneEncoderDecoder to ColumnTypes.TimestampWithTimezone), + OffsetDateTime::class.java to (TimestampWithTimezoneEncoderDecoder to ColumnTypes.TimestampWithTimezone), + Instant::class.java to (DateEncoderDecoder to ColumnTypes.Date), - ReadablePeriod::class.java to (PostgreSQLIntervalEncoderDecoder to ColumnTypes.Interval), - ReadableDuration::class.java to (PostgreSQLIntervalEncoderDecoder to ColumnTypes.Interval), + PeriodDuration::class.java to (PostgreSQLIntervalEncoderDecoder to ColumnTypes.Interval), + Period::class.java to (PostgreSQLIntervalEncoderDecoder to ColumnTypes.Interval), + Duration::class.java to (PostgreSQLIntervalEncoderDecoder to ColumnTypes.Interval), java.util.Date::class.java to (TimestampWithTimezoneEncoderDecoder to ColumnTypes.TimestampWithTimezone), java.sql.Date::class.java to (DateEncoderDecoder to ColumnTypes.Date), @@ -82,7 +83,7 @@ class PostgreSQLColumnEncoderRegistry : ColumnEncoderRegistry { private val classesSequence = listOf( LocalTime::class.java to (TimeEncoderDecoder.Instance to ColumnTypes.Time), - ReadablePartial::class.java to (TimeEncoderDecoder.Instance to ColumnTypes.Time) + TemporalAccessor::class.java to (TimeEncoderDecoder.Instance to ColumnTypes.Time) ) + classesSequenceInternal private val classes = classesSequence.toMap() diff --git a/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLIntervalEncoderDecoder.kt b/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLIntervalEncoderDecoder.kt index bda20e0a1..adbce682a 100644 --- a/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLIntervalEncoderDecoder.kt +++ b/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLIntervalEncoderDecoder.kt @@ -2,21 +2,18 @@ package com.github.jasync.sql.db.postgresql.column import com.github.jasync.sql.db.column.ColumnEncoderDecoder import com.github.jasync.sql.db.exceptions.DateEncoderNotAvailableException -import org.joda.time.Period -import org.joda.time.ReadableDuration -import org.joda.time.ReadablePeriod -import org.joda.time.format.ISOPeriodFormat +import java.time.Duration +import java.time.Period import org.joda.time.format.PeriodFormatterBuilder +import org.threeten.extra.PeriodDuration object PostgreSQLIntervalEncoderDecoder : ColumnEncoderDecoder { - /* Postgres accepts all ISO8601 formats. */ - private val formatter = ISOPeriodFormat.standard() - override fun encode(value: Any): String { return when (value) { - is ReadablePeriod -> formatter.print(value) - is ReadableDuration -> value.toString() // funaults to ISO8601 + is PeriodDuration -> value.toString() + is Period -> PeriodDuration.of(value).toString() + is Duration -> PeriodDuration.of(value).toString() // funaults to ISO8601 else -> throw DateEncoderNotAvailableException(value) } } @@ -75,34 +72,39 @@ object PostgreSQLIntervalEncoderDecoder : ColumnEncoderDecoder { /* This supports all positive intervals, and intervalstyle of postgres_verbose, and iso_8601 perfectly. * If intervalstyle is set to postgres or sql_standard, some negative intervals may be rejected. */ - override fun decode(value: String): Period { + override fun decode(value: String): PeriodDuration { return if (value.isEmpty()) { /* huh? */ - Period.ZERO + PeriodDuration.ZERO + } else if (value.startsWith('P')) { + PeriodDuration.parse(value) } else { val format = ( - if (value.startsWith('P')) /* iso_8601 */ - formatter - else if (value.startsWith("@ ")) - postgres_verboseParser - else { - /* try to guess based on what comes after the first number */ - val i = value.indexOfFirst { !it.isDigit() }.let { if ("-+".contains(value[0])) 1 else 0 } - if (i < 0 || ":.".contains(value[i])) /* simple HMS (to support group negation) */ - hmsParser - else if (value[i] == '-') /* sql_standard: Y-M */ - sqlParser - else if (value[i] == ' ' && i + 1 < value.length && value[i + 1].isDigit()) /* sql_standard: D H:M:S */ - sqlDTParser - else - postgresParser - } - ) - if ((format == hmsParser) && value.startsWith('-')) + if (value.startsWith("@ ")) + postgres_verboseParser + else { + /* try to guess based on what comes after the first number */ + val i = value.indexOfFirst { !it.isDigit() }.let { if ("-+".contains(value[0])) 1 else 0 } + if (i < 0 || ":.".contains(value[i])) /* simple HMS (to support group negation) */ + hmsParser + else if (value[i] == '-') /* sql_standard: Y-M */ + sqlParser + else if (value[i] == ' ' && i + 1 < value.length && value[i + 1].isDigit()) /* sql_standard: D H:M:S */ + sqlDTParser + else + postgresParser + } + ) + val jodaPeriod = if ((format == hmsParser) && value.startsWith('-')) format.parsePeriod(value.substring(1)).negated() else if (value.endsWith(" ago")) /* only really applies to postgres_verbose, but shouldn't hurt */ format.parsePeriod(value.removeSuffix(" ago")).negated() else format.parsePeriod(value) + PeriodDuration.of( + Period.of(jodaPeriod.years, jodaPeriod.months, jodaPeriod.days), + Duration.ofHours(jodaPeriod.hours.toLong()).plusMinutes(jodaPeriod.minutes.toLong()) + .plusSeconds(jodaPeriod.seconds.toLong()).plusMillis(jodaPeriod.millis.toLong()) + ) } } } diff --git a/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLTimestampEncoderDecoder.kt b/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLTimestampEncoderDecoder.kt index 1a7baaaa3..34c35c39c 100644 --- a/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLTimestampEncoderDecoder.kt +++ b/postgresql-async/src/main/java/com/github/jasync/sql/db/postgresql/column/PostgreSQLTimestampEncoderDecoder.kt @@ -5,36 +5,36 @@ import com.github.jasync.sql.db.exceptions.DateEncoderNotAvailableException import com.github.jasync.sql.db.general.ColumnData import com.github.jasync.sql.db.postgresql.messages.backend.PostgreSQLColumnData import com.github.jasync.sql.db.util.XXX +import com.github.jasync.sql.db.util.microsecondsFormatter import io.netty.buffer.ByteBuf import java.nio.charset.Charset import java.sql.Timestamp +import java.time.LocalDateTime +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder +import java.time.temporal.TemporalAccessor import java.util.Calendar import java.util.Date -import org.joda.time.DateTime -import org.joda.time.LocalDateTime -import org.joda.time.ReadableDateTime -import org.joda.time.format.DateTimeFormatter -import org.joda.time.format.DateTimeFormatterBuilder object PostgreSQLTimestampEncoderDecoder : ColumnEncoderDecoder { private val optionalTimeZone = DateTimeFormatterBuilder() - .appendPattern("Z").toParser() + .appendPattern("X").toFormatter() - private val internalFormatters: List = (1..6).map { index -> - DateTimeFormatterBuilder() - .appendPattern("yyyy-MM-dd HH:mm:ss") - .appendPattern("." + ("S".repeat(index))) - .appendOptional(optionalTimeZone) - .toFormatter() - } + private val internalFormatter = DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd HH:mm:ss") + .appendOptional(microsecondsFormatter) + .appendOptional(optionalTimeZone) + .toFormatter() private val internalFormatterWithoutSeconds = DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd HH:mm:ss") .appendOptional(optionalTimeZone) .toFormatter() - fun formatter() = internalFormatters[5] + fun formatter() = internalFormatter override fun decode(kind: ColumnData, value: ByteBuf, charset: Charset): Any { val bytes = ByteArray(value.readableBytes()) @@ -46,16 +46,16 @@ object PostgreSQLTimestampEncoderDecoder : ColumnEncoderDecoder { return when (columnType.dataType) { ColumnTypes.Timestamp, ColumnTypes.TimestampArray -> { - selectFormatter(text).parseLocalDateTime(text) + LocalDateTime.parse(text, selectFormatter(text)) } ColumnTypes.TimestampWithTimezoneArray -> { - selectFormatter(text).parseDateTime(text) + OffsetDateTime.parse(text, selectFormatter(text)) } ColumnTypes.TimestampWithTimezone -> { if (columnType.dataTypeModifier > 0) { - internalFormatters[columnType.dataTypeModifier - 1].parseDateTime(text) + OffsetDateTime.parse(text, internalFormatter) } else { - selectFormatter(text).parseDateTime(text) + OffsetDateTime.parse(text, selectFormatter(text)) } } else -> XXX("should treat ${columnType.dataType}") @@ -64,7 +64,7 @@ object PostgreSQLTimestampEncoderDecoder : ColumnEncoderDecoder { private fun selectFormatter(text: String): DateTimeFormatter { return if (text.contains(".")) { - internalFormatters[5] + internalFormatter } else { internalFormatterWithoutSeconds } @@ -75,11 +75,11 @@ object PostgreSQLTimestampEncoderDecoder : ColumnEncoderDecoder { override fun encode(value: Any): String { return when (value) { - is Timestamp -> this.formatter().print(DateTime(value)) - is Date -> this.formatter().print(DateTime(value)) - is Calendar -> this.formatter().print(DateTime(value)) - is LocalDateTime -> this.formatter().print(value) - is ReadableDateTime -> this.formatter().print(value) + is Timestamp -> value.toInstant().atOffset(ZoneOffset.UTC).format(this.formatter()) + is Date -> value.toInstant().atOffset(ZoneOffset.UTC).format(this.formatter()) + is Calendar -> value.toInstant().atOffset(ZoneOffset.UTC).format(this.formatter()) + is LocalDateTime -> this.formatter().format(value) + is TemporalAccessor -> this.formatter().format(value) else -> throw DateEncoderNotAvailableException(value) } } diff --git a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/ArrayTypesSpec.kt b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/ArrayTypesSpec.kt index ef6e4feea..51cd58830 100644 --- a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/ArrayTypesSpec.kt +++ b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/ArrayTypesSpec.kt @@ -3,6 +3,7 @@ package com.github.aysnc.sql.db.integration import com.github.jasync.sql.db.column.InetAddressEncoderDecoder import com.github.jasync.sql.db.column.TimestampWithTimezoneEncoderDecoder import com.github.jasync.sql.db.invoke +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -61,10 +62,16 @@ class ArrayTypesSpec : DatabaseTestHelper() { null ) ) - assertThat(result[0]("timestamp_column")).isEqualTo( - listOf( - TimestampWithTimezoneEncoderDecoder.decode("2013-04-06 01:15:10.528-03"), - TimestampWithTimezoneEncoderDecoder.decode("2013-04-06 01:15:08.528-03") + val times = result[0]("timestamp_column") as List<*> + + assertThat( + (times[0] as OffsetDateTime).isEqual( + TimestampWithTimezoneEncoderDecoder.decode("2013-04-06 01:15:10.528-03") as OffsetDateTime + ) + ) + assertThat( + (times[1] as OffsetDateTime).isEqual( + TimestampWithTimezoneEncoderDecoder.decode("2013-04-06 01:15:08.528-03") as OffsetDateTime ) ) } finally { @@ -109,7 +116,10 @@ class ArrayTypesSpec : DatabaseTestHelper() { assertThat(result[0]("inet_column")).isEqualTo(inets) assertThat(result[0]("direction_column")).isEqualTo("{in,out}") // user type decoding not supported) assertThat(result[0]("endpoint_column")).isEqualTo("""{"(127.0.0.1,80)","(2002:15::1,443)"}""") // user type decoding not supported) - assertThat(result[0]("timestamp_column")).isEqualTo(timestamps) + + val times = result[0]("timestamp_column") as List<*> + assertThat((times[0] as OffsetDateTime).isEqual(timestamps[0] as OffsetDateTime)) + assertThat((times[1] as OffsetDateTime).isEqual(timestamps[1] as OffsetDateTime)) } finally { executeDdl(handler, simpleDrop("csaups")) } diff --git a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PostgreSQLConnectionSpec.kt b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PostgreSQLConnectionSpec.kt index f2ffe484b..00267966f 100644 --- a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PostgreSQLConnectionSpec.kt +++ b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PostgreSQLConnectionSpec.kt @@ -21,12 +21,12 @@ import com.github.jasync.sql.db.util.map import com.github.jasync.sql.db.util.mapAsync import io.netty.buffer.Unpooled import java.nio.ByteBuffer +import java.time.LocalDateTime import java.util.concurrent.CompletableFuture import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit import java.util.function.Supplier import org.assertj.core.api.Assertions.assertThat -import org.joda.time.LocalDateTime import org.junit.Test import org.slf4j.MDC @@ -356,7 +356,7 @@ class PostgreSQLConnectionSpec : DatabaseTestHelper() { withHandler { handler -> executePreparedStatement(handler, "CREATE TEMP TABLE test(t TIMESTAMP)") - val date1 = LocalDateTime() + val date1 = LocalDateTime.now() executePreparedStatement(handler, "INSERT INTO test(t) VALUES(?)", listOf(date1)) val result = executePreparedStatement(handler, "SELECT t FROM test") val date2 = (result.rows.head)(0) diff --git a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PreparedStatementSpec.kt b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PreparedStatementSpec.kt index f3addaa37..44243ab45 100644 --- a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PreparedStatementSpec.kt +++ b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/PreparedStatementSpec.kt @@ -10,12 +10,12 @@ import com.github.jasync.sql.db.invoke import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException import com.github.jasync.sql.db.util.length import com.github.jasync.sql.db.util.map +import java.time.LocalDate import java.util.UUID import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit import java.util.function.Supplier import org.assertj.core.api.Assertions.assertThat -import org.joda.time.LocalDate import org.junit.Test import org.slf4j.MDC diff --git a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/TimeAndDateSpec.kt b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/TimeAndDateSpec.kt index e491d6dea..2da10915b 100644 --- a/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/TimeAndDateSpec.kt +++ b/postgresql-async/src/test/java/com/github/aysnc/sql/db/integration/TimeAndDateSpec.kt @@ -3,16 +3,18 @@ package com.github.aysnc.sql.db.integration import com.github.jasync.sql.db.invoke import com.github.jasync.sql.db.util.head import com.github.jasync.sql.db.util.length +import java.time.Duration +import java.time.Instant +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.Period +import java.time.ZoneOffset import org.assertj.core.api.Assertions.assertThat import org.assertj.core.data.Offset -import org.joda.time.DateTime -import org.joda.time.DateTimeZone -import org.joda.time.LocalDate -import org.joda.time.LocalDateTime -import org.joda.time.LocalTime -import org.joda.time.Period import org.junit.Ignore import org.junit.Test +import org.threeten.extra.PeriodDuration class TimeAndDateSpec : DatabaseTestHelper() { @@ -28,15 +30,15 @@ class TimeAndDateSpec : DatabaseTestHelper() { )""" executeDdl(handler, create) - executePreparedStatement(handler, "INSERT INTO messages (moment) VALUES (?)", listOf(LocalTime(4, 5, 6))) + executePreparedStatement(handler, "INSERT INTO messages (moment) VALUES (?)", listOf(LocalTime.of(4, 5, 6))) val rows = executePreparedStatement(handler, "select * from messages").rows val time = rows[0]("moment") as LocalTime - assertThat(time.hourOfDay).isEqualTo(4) - assertThat(time.minuteOfHour).isEqualTo(5) - assertThat(time.secondOfMinute).isEqualTo(6) + assertThat(time.hour).isEqualTo(4) + assertThat(time.minute).isEqualTo(5) + assertThat(time.second).isEqualTo(6) } } @@ -55,17 +57,17 @@ class TimeAndDateSpec : DatabaseTestHelper() { executePreparedStatement( handler, "INSERT INTO messages (moment) VALUES (?)", - listOf(LocalTime(4, 5, 6, 134)) + listOf(LocalTime.of(4, 5, 6, 134 * 1000_000)) ) val rows = executePreparedStatement(handler, "select * from messages").rows val time = rows(0)("moment") as LocalTime - assertThat(time.hourOfDay).isEqualTo(4) - assertThat(time.minuteOfHour).isEqualTo(5) - assertThat(time.secondOfMinute).isEqualTo(6) - assertThat(time.millisOfSecond).isEqualTo(134) + assertThat(time.hour).isEqualTo(4) + assertThat(time.minute).isEqualTo(5) + assertThat(time.second).isEqualTo(6) + assertThat(time.nano).isEqualTo(134 * 1000_000) } } @@ -88,9 +90,9 @@ class TimeAndDateSpec : DatabaseTestHelper() { val time = rows(0)("moment") as LocalTime - assertThat(time.hourOfDay).isEqualTo(4) - assertThat(time.minuteOfHour).isEqualTo(5) - assertThat(time.secondOfMinute).isEqualTo(6) + assertThat(time.hour).isEqualTo(4) + assertThat(time.minute).isEqualTo(5) + assertThat(time.second).isEqualTo(6) } } @@ -111,11 +113,11 @@ class TimeAndDateSpec : DatabaseTestHelper() { assertThat(rows.length).isEqualTo(1) - val dateTime = rows(0)("moment") as DateTime + val dateTime = rows(0)("moment") as OffsetDateTime // Note: Since this assertion depends on Brazil locale, I think epoch time assertion is preferred // assertThat( // dateTime.getZone.toTimeZone.getRawOffset).isEqualTo(-10800000) - assertThat(dateTime.millis).isEqualTo(915779106000L) + assertThat(dateTime.toInstant().toEpochMilli()).isEqualTo(915779106000L) } } @@ -144,12 +146,12 @@ class TimeAndDateSpec : DatabaseTestHelper() { assertThat(rows.length).isEqualTo(1) - val dateTime = rows(0)("moment") as DateTime + val dateTime = rows(0)("moment") as OffsetDateTime // Note: Since this assertion depends on Brazil locale, I think epoch time assertion is preferred // dateTime.getZone.toTimeZone.getRawOffset).isEqualTo(-10800000) - assertThat(dateTime.millis).isGreaterThan(915779106000L) - assertThat(dateTime.millis).isLessThan(915779107000L) + assertThat(dateTime.toInstant().toEpochMilli()).isGreaterThan(915779106000L) + assertThat(dateTime.toInstant().toEpochMilli()).isLessThan(915779107000L) } } } @@ -173,9 +175,9 @@ class TimeAndDateSpec : DatabaseTestHelper() { assertThat(rows.length).isEqualTo(1) - val dateTime = rows(0)("moment") as DateTime + val dateTime = rows(0)("moment") as OffsetDateTime - assertThat(dateTime.millis).isCloseTo(millis, Offset.offset(500L)) + assertThat(dateTime.toInstant().toEpochMilli()).isCloseTo(millis, Offset.offset(1000L)) } } @@ -183,13 +185,13 @@ class TimeAndDateSpec : DatabaseTestHelper() { fun `when processing times and dates should handle sending a time with timezone and return a LocalDateTime for a timestamp without timezone column`() { withHandler { conn -> - val date = DateTime(2190319) + val date = OffsetDateTime.ofInstant(Instant.ofEpochMilli(2190319), ZoneOffset.UTC) executePreparedStatement(conn, "CREATE TEMP TABLE TEST(T TIMESTAMP)") executePreparedStatement(conn, "INSERT INTO TEST(T) VALUES(?)", listOf(date)) val result = executePreparedStatement(conn, "SELECT T FROM TEST") val date2 = (result.rows.head)(0) - assertThat(date2).isEqualTo(date.toDateTime(DateTimeZone.UTC).toLocalDateTime()) + assertThat(date2).isEqualTo(date.toLocalDateTime()) } } @@ -197,14 +199,14 @@ class TimeAndDateSpec : DatabaseTestHelper() { fun `when processing times and dates should supports sending a local date and later a date time object for the same field`() { withHandler { conn -> - val date = LocalDate(2016, 3, 5) + val date = OffsetDateTime.of(2016, 3, 5, 0, 0, 0, 0, ZoneOffset.UTC) executePreparedStatement(conn, "CREATE TEMP TABLE TEST(T TIMESTAMP)") executePreparedStatement(conn, "INSERT INTO TEST(T) VALUES(?)", listOf(date)) val result = executePreparedStatement(conn, "SELECT T FROM TEST WHERE T = ?", listOf(date)) assertThat(result.rows.size).isEqualTo(1) - val dateTime = LocalDateTime(2016, 3, 5, 0, 0, 0, 0) + val dateTime = LocalDateTime.of(2016, 3, 5, 0, 0, 0, 0) val dateTimeResult = executePreparedStatement(conn, "SELECT T FROM TEST WHERE T = ?", listOf(dateTime)) assertThat(dateTimeResult.rows.size).isEqualTo(1) } @@ -214,7 +216,7 @@ class TimeAndDateSpec : DatabaseTestHelper() { fun `when processing times and dates should handle sending a LocalDateTime and return a LocalDateTime for a timestamp without timezone column`() { withHandler { conn -> - val date1 = LocalDateTime(2190319) + val date1 = LocalDateTime.ofInstant(Instant.ofEpochMilli(2190319), ZoneOffset.UTC) awaitFuture(conn.sendPreparedStatement("CREATE TEMP TABLE TEST(T TIMESTAMP)")) awaitFuture(conn.sendPreparedStatement("INSERT INTO TEST(T) VALUES(?)", listOf(date1))) @@ -229,7 +231,7 @@ class TimeAndDateSpec : DatabaseTestHelper() { fun `when processing times and dates should handle sending a date with timezone and retrieving the date , the same time zone`() { withHandler { conn -> - val date1 = DateTime(2190319) + val date1 = OffsetDateTime.ofInstant(Instant.ofEpochMilli(2190319), ZoneOffset.UTC) awaitFuture(conn.sendPreparedStatement("CREATE TEMP TABLE TEST(T TIMESTAMP WITH TIME ZONE)")) awaitFuture(conn.sendPreparedStatement("INSERT INTO TEST(T) VALUES(?)", listOf(date1))) @@ -246,7 +248,11 @@ class TimeAndDateSpec : DatabaseTestHelper() { executeDdl(handler, "CREATE TEMP TABLE intervals (duration interval NOT NULL)") - val p = Period(1, 2, 0, 4, 5, 6, 7, 8) /* postgres normalizes weeks */ + val p = PeriodDuration.of( + Period.of(1, 2, 4), + Duration.ofHours(5).plusMinutes(6).plusSeconds(7).plusMillis(8) + ) /* postgres normalizes weeks */ + executePreparedStatement(handler, "INSERT INTO intervals (duration) VALUES (?)", listOf(p)) val rows = executeQuery(handler, "SELECT duration FROM intervals").rows From 7c702c8f2e100d1e3b070cd4985c7613d15da5c4 Mon Sep 17 00:00:00 2001 From: dragneelfps Date: Mon, 14 Jun 2021 02:09:44 +0530 Subject: [PATCH 2/2] Fix formatter --- .../github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt b/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt index 18830df0b..fa6f54e71 100644 --- a/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt +++ b/db-async-common/src/main/java/com/github/jasync/sql/db/column/LocalDateTimeEncoderDecoder.kt @@ -10,7 +10,7 @@ object LocalDateTimeEncoderDecoder : ColumnEncoderDecoder { private val format = DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd HH:mm:ss") - .append(microsecondsFormatter) + .appendOptional(microsecondsFormatter) .toFormatter() override fun encode(value: Any): String =