Skip to content

Commit

Permalink
Fix timetz offset (#114)
Browse files Browse the repository at this point in the history
* Fix timetz offset

* Fix time range
  • Loading branch information
koxudaxi committed Mar 29, 2021
1 parent 1316be6 commit 1a634c2
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.koxudaxi.localDataApi

import java.sql.*
import java.time.OffsetDateTime
import java.time.OffsetTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.*
Expand All @@ -14,17 +15,35 @@ val BOOLEAN = listOf(Types.BOOLEAN, Types.BIT)
val BLOB = listOf(Types.BLOB, Types.BINARY, Types.LONGVARBINARY, Types.VARBINARY)
val DATETIME = listOf(Types.TIMESTAMP)
val DATETIME_TZ = listOf(Types.TIMESTAMP_WITH_TIMEZONE)
val TIME = listOf(Types.TIME)
val TIME_TZ = listOf(Types.TIME_WITH_TIMEZONE)

fun stripNanoSecs(time: String): String {
val splitTime = time.split(".")
return splitTime[0] + ".${splitTime[1]}".dropLastWhile { char -> char == '0' || char == '.' }
}


fun convertOffsetDatetimeToUTC(input: String): String {
val splitFormatUtc = OffsetDateTime.parse(input,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSSSSS][.SSSSS][.SSSS][.SSS][.SS][.S]x")
)
.atZoneSameInstant(ZoneOffset.UTC)
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))
.split(".")
return splitFormatUtc[0] + ".${splitFormatUtc[1]}".dropLastWhile { char -> char == '0' || char == '.' }

return stripNanoSecs(splitFormatUtc)
}

fun convertOffsetTimeToUTC(input: String): String {
val splitFormatUtc = OffsetTime.parse(input,
DateTimeFormatter.ofPattern("HH:mm:ss[.SSSSSS][.SSSSS][.SSSS][.SSS][.SS][.S]x")
)
.withOffsetSameInstant(ZoneOffset.UTC)
.format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"))
return stripNanoSecs(splitFormatUtc)
}


fun createField(resultSet: ResultSet, index: Int): Field {
if (resultSet.getObject(index) == null) {
return Field(isNull = true)
Expand All @@ -37,7 +56,12 @@ fun createField(resultSet: ResultSet, index: Int): Field {
value in BLOB -> Field(blobValue = Base64.getEncoder().encodeToString(resultSet.getBytes(index)))
value in DATETIME_TZ || (value in DATETIME && resultSet.metaData.getColumnTypeName(index) == "timestamptz")
-> Field(stringValue = convertOffsetDatetimeToUTC(resultSet.getString(index)))
value in DATETIME -> Field(stringValue = Regex("^[^.]+\\.\\d{3}|^[^.]+").find(resultSet.getString(index))!!.value)
value in DATETIME -> Field(stringValue = Regex("^[^.]+\\.\\d{1,6}|^[^.]+").find(resultSet.getString(
index))!!.value)
value in TIME_TZ || (value in TIME && resultSet.metaData.getColumnTypeName(index) == "timetz")
-> Field(stringValue = convertOffsetTimeToUTC(resultSet.getString(index)))
value in TIME -> Field(stringValue = Regex("^[^.]+\\.\\d{1,3}|^[^.]+").find(resultSet.getString(
index))!!.value)
else -> Field(stringValue = resultSet.getString(index))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,33 @@ class ApplicationTest {
}
}

@Test
fun testExecuteSelectTIME() {
withTestApplication({ module(testing = true) }) {

handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
"SELECT CAST('22:41:04.968123' AS TIME) AS value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"22:41:05\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}
handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
"SELECT CAST('22:41:04' AS TIME) AS value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"22:41:04\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}
}
}

@Test
fun testExecuteSelectTIMESTAMP() {
withTestApplication({ module(testing = true) }) {
Expand All @@ -205,11 +232,12 @@ class ApplicationTest {
"SELECT CAST('2021-03-10 22:41:04.968123' AS TIMESTAMP) AS value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"2021-03-10 22:41:04.968\"}]],\"columnMetadata\":null}",
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"2021-03-10 22:41:04.968123\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}


handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
Expand All @@ -223,6 +251,35 @@ class ApplicationTest {
}
}

@Test
fun testExecuteSelectTIME_WITH_TZ() {
ResourceManager.INSTANCE.setResource("h2:./test;MODE=PostgreSQL", dummyResourceArn, null, null, emptyMap())

withTestApplication({ module(testing = true) }) {
handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
"SELECT CAST('22:41:04.968123+02' AS TIME(6) WITH TIME ZONE) as value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"20:41:04.968\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}

handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
"SELECT CAST('22:41:04+02' AS TIME(6) WITH TIME ZONE) AS value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"20:41:04\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}
}
}

@Test
fun testExecuteSelectTIMESTAMP_WITH_TZ() {
ResourceManager.INSTANCE.setResource("h2:./test;MODE=PostgreSQL", dummyResourceArn, null, null, emptyMap())
Expand All @@ -231,21 +288,21 @@ class ApplicationTest {
handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
"SELECT CAST('2021-03-10 22:41:04.968123' AS TIMESTAMP(6) WITH TIME ZONE) as value")))
"SELECT CAST('2021-03-10 22:41:04.968123+02' AS TIMESTAMP(6) WITH TIME ZONE) as value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"2021-03-10 22:41:04.968123\"}]],\"columnMetadata\":null}",
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"2021-03-10 20:41:04.968123\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}

handleRequest(HttpMethod.Post, "/Execute") {
addHeader(HttpHeaders.ContentType, "*/*")
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
"SELECT CAST('2021-03-10 22:41:04' AS TIMESTAMP(6) WITH TIME ZONE) AS value")))
"SELECT CAST('2021-03-10 22:41:04+02' AS TIMESTAMP(6) WITH TIME ZONE) AS value")))
}.apply {
assertEquals(
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"2021-03-10 22:41:04\"}]],\"columnMetadata\":null}",
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"2021-03-10 20:41:04\"}]],\"columnMetadata\":null}",
response.content)
assertEquals(HttpStatusCode.OK, response.status())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class LocalDataApiTest {
assertEquals(listOf(Types.BLOB, Types.BINARY, Types.LONGVARBINARY, Types.VARBINARY), BLOB)
assertEquals(listOf(Types.TIMESTAMP), DATETIME)
assertEquals(listOf(Types.TIMESTAMP_WITH_TIMEZONE), DATETIME_TZ)
assertEquals(listOf(Types.TIME), TIME)
assertEquals(listOf(Types.TIME_WITH_TIMEZONE), TIME_TZ)
}

@Test
Expand All @@ -47,14 +49,44 @@ class LocalDataApiTest {
assertEquals("2021-03-10 20:41:04", convertOffsetDatetimeToUTC("2021-03-10 22:41:04.000000+02"))
}

@Test
fun testConvertOffsetTimeToUTC() {
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.123456+02"))
assertEquals("22:41:04.123", convertOffsetTimeToUTC("22:41:04.123456+00"))
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.123450+02"))
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.123400+02"))
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.123000+02"))
assertEquals("20:41:04.12", convertOffsetTimeToUTC("22:41:04.120000+02"))
assertEquals("20:41:04.1", convertOffsetTimeToUTC("22:41:04.100000+02"))
assertEquals("20:41:04", convertOffsetTimeToUTC("22:41:04.000000+02"))
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.12345+02"))
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.1234+02"))
assertEquals("20:41:04.123", convertOffsetTimeToUTC("22:41:04.123+02"))
assertEquals("20:41:04.12", convertOffsetTimeToUTC("22:41:04.12+02"))
assertEquals("20:41:04.1", convertOffsetTimeToUTC("22:41:04.1+02"))
assertEquals("20:41:04", convertOffsetTimeToUTC("22:41:04+02"))
assertEquals("20:41:04", convertOffsetTimeToUTC("22:41:04.000050+02"))
assertEquals("20:41:04", convertOffsetTimeToUTC("22:41:04.000400+02"))
assertEquals("20:41:04.003", convertOffsetTimeToUTC("22:41:04.003000+02"))
assertEquals("20:41:04.02", convertOffsetTimeToUTC("22:41:04.020000+02"))
assertEquals("20:41:04", convertOffsetTimeToUTC("22:41:04.000000+02"))
}

@Test
fun testCreateField() {
// For PostgreSQL
val mock = mockk<ResultSet>(relaxed = true)
every {mock.metaData.getColumnType(1) } returns Types.TIMESTAMP
every {mock.metaData.getColumnTypeName(1) } returns "timestamptz"
every {mock.getString(1) } returns "2021-03-10 22:41:04.123456+02"
assertEquals(createField(mock, 1).stringValue, "2021-03-10 20:41:04.123456")
val timestamptzMock = mockk<ResultSet>(relaxed = true)
every {timestamptzMock.metaData.getColumnType(1) } returns Types.TIMESTAMP
every {timestamptzMock.metaData.getColumnTypeName(1) } returns "timestamptz"
every {timestamptzMock.getString(1) } returns "2021-03-10 22:41:04.123456+02"
assertEquals(createField(timestamptzMock, 1).stringValue, "2021-03-10 20:41:04.123456")

val timetzMock = mockk<ResultSet>(relaxed = true)
every {timetzMock.metaData.getColumnType(1) } returns Types.TIME
every {timetzMock.metaData.getColumnTypeName(1) } returns "timetz"
every {timetzMock.getString(1) } returns "22:41:04.123+02"
assertEquals(createField(timetzMock, 1).stringValue, "20:41:04.123")

}

@Test
Expand Down

0 comments on commit 1a634c2

Please sign in to comment.