fix(timezone): resolve the abbreviation via java.util, not java.time
The abbreviation came from java.time's "zzz" formatter, which on Android has no short specific-zone names and silently degrades every zone to a "GMT-4" form — so on device New York showed "GMT-04:00" instead of "EDT". Desktop couldn't catch it: there "zzz" and java.util.TimeZone agree, and that agreement is the whole trap. Resolve through java.util.TimeZone.getDisplayName(inDst, SHORT, locale) instead — ICU-backed, so it returns the real abbreviation on both Android and the JVM. Zones with no named abbreviation still fall back to a GMT form, which zoneDescriptor already collapses to a single token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,6 @@ package de.jeanlucmakiola.calendula.domain
|
|||||||
import java.text.Normalizer
|
import java.text.Normalizer
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZonedDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import java.time.format.TextStyle
|
import java.time.format.TextStyle
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@@ -35,16 +33,24 @@ data class TimeZoneOption(
|
|||||||
val region: String get() = id.substringBeforeLast('/', missingDelimiterValue = "")
|
val region: String get() = id.substringBeforeLast('/', missingDelimiterValue = "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optionFor(zone: ZoneId, locale: Locale, at: Instant) = TimeZoneOption(
|
private fun optionFor(zone: ZoneId, locale: Locale, at: Instant): TimeZoneOption {
|
||||||
id = zone.id,
|
val offsetSeconds = zone.rules.getOffset(at).totalSeconds
|
||||||
displayName = zone.getDisplayName(TextStyle.FULL, locale),
|
return TimeZoneOption(
|
||||||
// "zzz" resolves the DST-correct abbreviation at [at] (CET vs CEST); zones
|
id = zone.id,
|
||||||
// with no named abbreviation fall back to a "GMT+05:30" form, which is
|
displayName = zone.getDisplayName(TextStyle.FULL, locale),
|
||||||
// exactly what we'd want to show them as anyway.
|
// The abbreviation ("EDT"/"CEST", DST-correct at [at]). Resolved through
|
||||||
shortName = ZonedDateTime.ofInstant(at, zone)
|
// java.util.TimeZone, NOT java.time's "zzz" formatter: on Android the
|
||||||
.format(DateTimeFormatter.ofPattern("zzz", locale)),
|
// latter has no short specific-zone names and silently degrades every
|
||||||
offsetMinutes = zone.rules.getOffset(at).totalSeconds / 60,
|
// zone to a "GMT-4" form (it agrees with java.util only on desktop), so
|
||||||
)
|
// "zzz" cost us the real abbreviation on the one platform that ships.
|
||||||
|
// java.util is ICU-backed and returns the abbreviation on both. Zones
|
||||||
|
// with genuinely no named abbreviation still fall back to a GMT form,
|
||||||
|
// which is what we'd show anyway.
|
||||||
|
shortName = java.util.TimeZone.getTimeZone(zone.id)
|
||||||
|
.getDisplayName(zone.rules.isDaylightSavings(at), java.util.TimeZone.SHORT, locale),
|
||||||
|
offsetMinutes = offsetSeconds / 60,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Every zone the JVM knows, resolved at [at]. This is ~600 entries, so build it
|
* Every zone the JVM knows, resolved at [at]. This is ~600 entries, so build it
|
||||||
|
|||||||
Reference in New Issue
Block a user