feat: per-event time zones (#31) #82

Merged
makiolaj merged 11 commits from feat/timezone-support into release/v2.16.0 2026-07-19 09:49:13 +00:00
Showing only changes of commit 629896b74d - Show all commits

View File

@@ -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