Skip to content
This repository has been archived by the owner on Jul 8, 2022. It is now read-only.

Commit

Permalink
Fixes #131 - Support 9 S in format
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Jul 23, 2020
1 parent d89f134 commit 86dc7e1
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 62 deletions.
59 changes: 30 additions & 29 deletions klock/src/commonMain/kotlin/com/soywiz/klock/PatternDateFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ data class PatternDateFormat @JvmOverloads constructor(
}

fun withLocale(locale: KlockLocale?) = this.copy(locale = locale)
fun withTimezoneNames(tzNames: TimezoneNames) = this.copy(tzNames = this.tzNames + tzNames)
fun withTimezoneNames(tzNames: TimezoneNames) = this.copy(tzNames = this.tzNames + tzNames)
fun withOptions(options: Options) = this.copy(options = options)
fun withOptional() = this.copy(options = options.copy(optionalSupport = true))
fun withNonOptional() = this.copy(options = options.copy(optionalSupport = false))

private val openOffsets = LinkedHashMap<Int, Int>()
private val closeOffsets = LinkedHashMap<Int, Int>()

internal val chunks = arrayListOf<String>().also { chunks ->
internal val chunks = arrayListOf<String>().also { chunks ->
val s = MicroStrReader(format)
while (s.hasMore) {
if (s.peekChar() == '\'') {
Expand Down Expand Up @@ -66,7 +66,7 @@ data class PatternDateFormat @JvmOverloads constructor(
}
}.toList()

internal val regexChunks = chunks.map {
internal val regexChunks = chunks.map {
when (it) {
"E", "EE", "EEE", "EEEE", "EEEEE", "EEEEEE" -> """(\w+)"""
"z", "zzz" -> """([\w\s\-\+\:]+)"""
Expand Down Expand Up @@ -96,6 +96,7 @@ data class PatternDateFormat @JvmOverloads constructor(
"SSSSSS" -> """(\d{6})"""
"SSSSSSS" -> """(\d{7})"""
"SSSSSSSS" -> """(\d{8})"""
"SSSSSSSSS" -> """(\d{9})"""
"X", "XX", "XXX", "x", "xx", "xxx" -> """([\w:\+\-]+)"""
"a" -> """(\w+)"""
" " -> """(\s+)"""
Expand All @@ -106,8 +107,8 @@ data class PatternDateFormat @JvmOverloads constructor(
}
}

//val escapedFormat = Regex.escape(format)
internal val rx2: Regex = Regex("^" + regexChunks.mapIndexed { index, it ->
//val escapedFormat = Regex.escape(format)
internal val rx2: Regex = Regex("^" + regexChunks.mapIndexed { index, it ->
if (options.optionalSupport) {
val opens = openOffsets.getOrElse(index) { 0 }
val closes = closeOffsets.getOrElse(index) { 0 }
Expand All @@ -122,7 +123,7 @@ data class PatternDateFormat @JvmOverloads constructor(
}.joinToString("") + "$")


// EEE, dd MMM yyyy HH:mm:ss z -- > Sun, 06 Nov 1994 08:49:37 GMT
// EEE, dd MMM yyyy HH:mm:ss z -- > Sun, 06 Nov 1994 08:49:37 GMT
// YYYY-MM-dd HH:mm:ss

override fun format(dd: DateTimeTz): String {
Expand Down Expand Up @@ -154,13 +155,13 @@ data class PatternDateFormat @JvmOverloads constructor(
"m", "mm" -> utc.minutes.padded(nlen)
"s", "ss" -> utc.seconds.padded(nlen)

"S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS", "SSSSSSS", "SSSSSSSS" -> {
"S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS", "SSSSSSS", "SSSSSSSS", "SSSSSSSSS" -> {
val milli = utc.milliseconds
val base10length = log10(utc.milliseconds.toDouble()).toInt() + 1
if (base10length > name.length) {
(milli.toDouble() * 10.0.pow(-1 * (base10length - name.length))).toInt()
} else {
"${milli.padded(3)}00000".substr(0, name.length)
"${milli.padded(3)}000000".substr(0, name.length)
}
}
"X", "XX", "XXX", "x", "xx", "xxx" -> {
Expand All @@ -181,9 +182,9 @@ data class PatternDateFormat @JvmOverloads constructor(
}
"a" -> if (utc.hours < 12) "am" else "pm"
else -> when {
name.startsWith('\'') -> name.substring(1, name.length - 1)
else -> name
}
name.startsWith('\'') -> name.substring(1, name.length - 1)
else -> name
}
}
}
return out
Expand All @@ -207,23 +208,23 @@ data class PatternDateFormat @JvmOverloads constructor(
when (name) {
"E", "EE", "EEE", "EEEE", "EEEEE", "EEEEEE" -> Unit // day of week (Sun | Sunday)
"z", "zzz" -> { // timezone (GMT)
val tzOffset = tzNames.namesToOffsets[value.toUpperCase()]
if (tzOffset != null) {
offset = tzOffset
} else {
var sign = +1
val reader = MicroStrReader(value)
reader.tryRead("GMT")
reader.tryRead("UTC")
if (reader.tryRead("+")) sign = +1
if (reader.tryRead("-")) sign = -1
val part = reader.readRemaining().replace(":", "")
val hours = part.substr(0, 2).padStart(2, '0').toIntOrNull() ?: 0
val minutes = part.substr(2, 2).padStart(2, '0').toIntOrNull() ?: 0
val roffset = hours.hours + minutes.minutes
offset = if (sign > 0) +roffset else -roffset
}
}
val tzOffset = tzNames.namesToOffsets[value.toUpperCase()]
if (tzOffset != null) {
offset = tzOffset
} else {
var sign = +1
val reader = MicroStrReader(value)
reader.tryRead("GMT")
reader.tryRead("UTC")
if (reader.tryRead("+")) sign = +1
if (reader.tryRead("-")) sign = -1
val part = reader.readRemaining().replace(":", "")
val hours = part.substr(0, 2).padStart(2, '0').toIntOrNull() ?: 0
val minutes = part.substr(2, 2).padStart(2, '0').toIntOrNull() ?: 0
val roffset = hours.hours + minutes.minutes
offset = if (sign > 0) +roffset else -roffset
}
}
"d", "dd" -> day = value.toInt()
"M", "MM" -> month = value.toInt()
"MMM" -> month = realLocale.monthsShort.indexOf(value.toLowerCase()) + 1
Expand All @@ -237,7 +238,7 @@ data class PatternDateFormat @JvmOverloads constructor(
}
"m", "mm" -> minute = value.toInt()
"s", "ss" -> second = value.toInt()
"S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS" -> {
"S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS", "SSSSSSS", "SSSSSSSS", "SSSSSSSSS" -> {
val base10length = log10(value.toDouble()).toInt() + 1
millisecond = if (base10length > 3) {
// only precision to millisecond supported, ignore the rest. ex: 9999999 => 999"
Expand Down
74 changes: 41 additions & 33 deletions klock/src/commonTest/kotlin/com/soywiz/klock/DateTimeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,10 @@ class DateTimeTest {
)
}

@Test
fun testNewParserBug1() {
DateFormat("EEE, dd MMMM yyyy HH:mm:ss z").parseLong("Sat, 08 September 2018 04:08:09 UTC")
}
@Test
fun testNewParserBug1() {
DateFormat("EEE, dd MMMM yyyy HH:mm:ss z").parseLong("Sat, 08 September 2018 04:08:09 UTC")
}

@Test
fun testParsingDateTimesInCustomStringFormatsWithAmPm() {
Expand Down Expand Up @@ -333,35 +333,35 @@ class DateTimeTest {
assertEquals(c, c.clamp(a, c))
}

@Test
fun testStartEndDay() {
val date = DateTime(1568803601377)
val start = date.dateDayStart
val end = date.dateDayEnd
assertEquals("2019-09-18T00:00:00", ISO8601.DATETIME_COMPLETE.extended.format(start))
assertEquals("2019-09-18T23:59:59", ISO8601.DATETIME_COMPLETE.extended.format(end))
assertEquals(1568764800000L, start.unixMillisLong)
assertEquals(1568851199999L, end.unixMillisLong)
}

@Test
fun testTimeZones() {
"Tue, 19 Sep 2017 00:58:45 GMT-0800".let { STR -> assertEquals(STR, HttpDate.parse(STR).toString()) }
"Tue, 19 Sep 2017 00:58:45 GMT+0800".let { STR -> assertEquals(STR, HttpDate.parse(STR).toString()) }
}

@Test
fun testBug37() {
val format = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
format.parse("2019-04-15T17:28:46.862+0900")
}

@Test
fun testBug33() {
assertEquals("20190412", DateTime(2019, 4, 12).localUnadjusted.format("yyyyMMdd"))
assertEquals("2019年04月12日", Date(2019, 4, 12).format("yyyy年MM月dd日"))
assertEquals("2019年04月12日", Date(2019, 4, 12).format("yyyy'年'MM'月'dd'日'"))
}
@Test
fun testStartEndDay() {
val date = DateTime(1568803601377)
val start = date.dateDayStart
val end = date.dateDayEnd
assertEquals("2019-09-18T00:00:00", ISO8601.DATETIME_COMPLETE.extended.format(start))
assertEquals("2019-09-18T23:59:59", ISO8601.DATETIME_COMPLETE.extended.format(end))
assertEquals(1568764800000L, start.unixMillisLong)
assertEquals(1568851199999L, end.unixMillisLong)
}

@Test
fun testTimeZones() {
"Tue, 19 Sep 2017 00:58:45 GMT-0800".let { STR -> assertEquals(STR, HttpDate.parse(STR).toString()) }
"Tue, 19 Sep 2017 00:58:45 GMT+0800".let { STR -> assertEquals(STR, HttpDate.parse(STR).toString()) }
}

@Test
fun testBug37() {
val format = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
format.parse("2019-04-15T17:28:46.862+0900")
}

@Test
fun testBug33() {
assertEquals("20190412", DateTime(2019, 4, 12).localUnadjusted.format("yyyyMMdd"))
assertEquals("2019年04月12日", Date(2019, 4, 12).format("yyyy年MM月dd日"))
assertEquals("2019年04月12日", Date(2019, 4, 12).format("yyyy'年'MM'月'dd'日'"))
}

@Test
fun testBug93() {
Expand Down Expand Up @@ -418,4 +418,12 @@ class DateTimeTest {
assertEquals(str1, DateTime.parse(str1).format(DateFormat.FORMAT1))
assertEquals(str2, DateTime.parse(str2).format(DateFormat.FORMAT2))
}

@Test
fun testIssue131() {
assertEquals(
"2020-07-23T12:30:52.999000000Z",
DateTime(1595507452999L).format("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZ")
)
}
}

0 comments on commit 86dc7e1

Please sign in to comment.