cashapp / paraphrase

A Gradle plugin that generates type-safe formatters for Android string resources in the ICU message format.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Time zones are not handled correctly

theisenp opened this issue · comments

Currently we require Instant for both date and time arguments, which we then convert to a Long before passing it on to the ICU library.

These types don't have an associated time zone, so when the ICU library formats them it uses the default system time zone. Probably a reasonable assumption for most use cases, but not all. For instance - the Android tests in our :tests module fail if you don't run them on the east coast 🫠

Ah yeah I meant to fix this as a follow-up. The ICU code tragically calls Locale.getDefault() internally rather than accepting superior types that contain timezone information.

The fix is a test rule that sets the location to a fixed locale and then resets it at the end of the test.

I can do it right now.

The underlying ICU library accepts three types for date and time arguments: Long, Date, Calendar.

Calendar includes time zone information, so we can potentially use that in cases where developers don't want to use the system default. And to have a consistent java.time API, we could require ZonedDateTime and map it to Calendar internally.

Maybe we generate two methods? Something like:

fun release_description(release_date: Instant, release_time: Instant) {
  // ...
}

fun release_description(release_date: ZonedDateTime, release_time: ZonedDateTime) {
  // ...
}

Or I should say that ICU4J accepts com.ibm.icu.util.Calendar - I haven't confirmed yet that the Android fork accepts android.icu.util.Calendar

Oof Calendar is a brutally terrible type, but yeah if we can encapsulate it entirely as implementation detail then that's an excellent idea.

I think we can and should be more opinionated about what's generated based on which is used and what will be printed.

Dates never print time or time zone information, so for dates we should accept only LocalDate:

fun some_resource(argName: LocalDate)

Times never print date information. short, medium, and long times don't print time zone information; full times do. So for all times we should accept a LocalTime, and for full times only we should accept a ZoneId:

fun resource_with_long_time(argName: LocalTime)
fun resource_with_full_time(argName: LocalTime, argNameZone: ZoneId)

Skeletons can have an arbitrary combination of date, time, and time zone elements. But which symbols correspond to which type of elements is well-defined, so we can check skeleton strings and decide the correct arg type based on the combination of symbols: LocalDate, LocalTime, ZoneId, LocalDateTime if both date and time are present but no zone, or ZonedDateTime if all three are present.

I wouldn't offer Instant args for any of them; as a formatter with explicit date/time type differentiation I think it makes sense to force the consumer to be explicit about what they want.

Nice, I like the idea of requesting specifically what we need to resolve the argument!

Some of the the details regarding time formatting were off in my previous comment; see #91 for details.