Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Iso8601DateFormatter(
locale: Locale) extends DateFormatter with DateTimeFormatterHelper {

@transient
private lazy val formatter = buildFormatter(pattern, locale)
private lazy val formatter = DateTimeFormatterHelper.getFormatter(pattern, locale)
private val UTC = ZoneId.of("UTC")

private def toInstant(s: String): Instant = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,34 @@ import java.time.chrono.IsoChronology
import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder, ResolverStyle}
import java.time.temporal.{ChronoField, TemporalAccessor, TemporalQueries}
import java.util.Locale
import java.util.concurrent.{Callable, TimeUnit}

import com.google.common.cache.CacheBuilder
import com.google.common.util.concurrent.{ExecutionError, UncheckedExecutionException}

trait DateTimeFormatterHelper {
protected def toInstantWithZoneId(temporalAccessor: TemporalAccessor, zoneId: ZoneId): Instant = {
val localTime = if (temporalAccessor.query(TemporalQueries.localTime) == null) {
LocalTime.ofNanoOfDay(0)
} else {
LocalTime.from(temporalAccessor)
}
val localDate = LocalDate.from(temporalAccessor)
val localDateTime = LocalDateTime.of(localDate, localTime)
val zonedDateTime = ZonedDateTime.of(localDateTime, zoneId)
Instant.from(zonedDateTime)
}
}

object DateTimeFormatterHelper {
private val cache = CacheBuilder.newBuilder()
.initialCapacity(8)
.maximumSize(128)
.expireAfterAccess(1, TimeUnit.HOURS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need the expire policy?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I think it has to make a thread to deal with it and it's not worth it. Min size doesn't really matter either.

.build[(String, Locale), DateTimeFormatter]()


protected def buildFormatter(pattern: String, locale: Locale): DateTimeFormatter = {
private def buildFormatter(pattern: String, locale: Locale): DateTimeFormatter = {
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern(pattern)
Expand All @@ -39,15 +63,17 @@ trait DateTimeFormatterHelper {
.withResolverStyle(ResolverStyle.STRICT)
}

protected def toInstantWithZoneId(temporalAccessor: TemporalAccessor, zoneId: ZoneId): Instant = {
val localTime = if (temporalAccessor.query(TemporalQueries.localTime) == null) {
LocalTime.ofNanoOfDay(0)
} else {
LocalTime.from(temporalAccessor)
def getFormatter(pattern: String, locale: Locale): DateTimeFormatter = {
try {
cache.get(
(pattern, locale),
new Callable[DateTimeFormatter]() {
override def call = buildFormatter(pattern, locale)
})
} catch {
// Cache.get() may wrap the original exception.
case e @ (_: UncheckedExecutionException | _: ExecutionError) =>
throw e.getCause
}
val localDate = LocalDate.from(temporalAccessor)
val localDateTime = LocalDateTime.of(localDate, localTime)
val zonedDateTime = ZonedDateTime.of(localDateTime, zoneId)
Instant.from(zonedDateTime)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Iso8601TimestampFormatter(
timeZone: TimeZone,
locale: Locale) extends TimestampFormatter with DateTimeFormatterHelper {
@transient
private lazy val formatter = buildFormatter(pattern, locale)
private lazy val formatter = DateTimeFormatterHelper.getFormatter(pattern, locale)

private def toInstant(s: String): Instant = {
val temporalAccessor = formatter.parse(s)
Expand Down