From a3e1c338d262cf63ec210b0bd526b349f0af9409 Mon Sep 17 00:00:00 2001 From: Trey t Date: Sat, 13 Dec 2025 00:04:44 -0600 Subject: [PATCH] Add X-Timezone header to all API requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Send the device's IANA timezone identifier (e.g., "America/Los_Angeles") with every API request to enable timezone-aware overdue task detection. Platform implementations: - Android/JVM: TimeZone.getDefault().id - iOS: NSTimeZone.localTimeZone.name - JS/WASM: Intl.DateTimeFormat().resolvedOptions().timeZone 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../kotlin/com/example/casera/network/ApiClient.android.kt | 6 ++++++ .../kotlin/com/example/casera/network/ApiClient.kt | 7 +++++++ .../kotlin/com/example/casera/network/ApiClient.ios.kt | 6 ++++++ .../kotlin/com/example/casera/network/ApiClient.js.kt | 6 ++++++ .../kotlin/com/example/casera/network/ApiClient.jvm.kt | 6 ++++++ .../kotlin/com/example/casera/network/ApiClient.wasmJs.kt | 6 ++++++ 6 files changed, 37 insertions(+) diff --git a/composeApp/src/androidMain/kotlin/com/example/casera/network/ApiClient.android.kt b/composeApp/src/androidMain/kotlin/com/example/casera/network/ApiClient.android.kt index b09b039..afecc27 100644 --- a/composeApp/src/androidMain/kotlin/com/example/casera/network/ApiClient.android.kt +++ b/composeApp/src/androidMain/kotlin/com/example/casera/network/ApiClient.android.kt @@ -8,6 +8,7 @@ import io.ktor.client.plugins.logging.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json import java.util.Locale +import java.util.TimeZone actual fun getLocalhostAddress(): String = "10.0.2.2" @@ -15,6 +16,10 @@ actual fun getDeviceLanguage(): String { return Locale.getDefault().language } +actual fun getDeviceTimezone(): String { + return TimeZone.getDefault().id +} + actual fun createHttpClient(): HttpClient { return HttpClient(OkHttp) { install(ContentNegotiation) { @@ -32,6 +37,7 @@ actual fun createHttpClient(): HttpClient { install(DefaultRequest) { headers.append("Accept-Language", getDeviceLanguage()) + headers.append("X-Timezone", getDeviceTimezone()) } } } diff --git a/composeApp/src/commonMain/kotlin/com/example/casera/network/ApiClient.kt b/composeApp/src/commonMain/kotlin/com/example/casera/network/ApiClient.kt index 71282a1..27b1d21 100644 --- a/composeApp/src/commonMain/kotlin/com/example/casera/network/ApiClient.kt +++ b/composeApp/src/commonMain/kotlin/com/example/casera/network/ApiClient.kt @@ -16,6 +16,13 @@ expect fun createHttpClient(): HttpClient */ expect fun getDeviceLanguage(): String +/** + * Get the device's timezone identifier (e.g., "America/Los_Angeles", "Europe/London"). + * This is used to set the X-Timezone header for API requests + * so the server can calculate overdue tasks correctly based on the user's local time. + */ +expect fun getDeviceTimezone(): String + object ApiClient { val httpClient = createHttpClient() diff --git a/composeApp/src/iosMain/kotlin/com/example/casera/network/ApiClient.ios.kt b/composeApp/src/iosMain/kotlin/com/example/casera/network/ApiClient.ios.kt index 30a1e19..17cd496 100644 --- a/composeApp/src/iosMain/kotlin/com/example/casera/network/ApiClient.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/example/casera/network/ApiClient.ios.kt @@ -8,6 +8,7 @@ import io.ktor.client.plugins.logging.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json import platform.Foundation.NSLocale +import platform.Foundation.NSTimeZone import platform.Foundation.preferredLanguages actual fun getLocalhostAddress(): String = "127.0.0.1" @@ -19,6 +20,10 @@ actual fun getDeviceLanguage(): String { return firstLanguage?.split("-")?.firstOrNull() ?: "en" } +actual fun getDeviceTimezone(): String { + return NSTimeZone.localTimeZone.name +} + actual fun createHttpClient(): HttpClient { return HttpClient(Darwin) { install(ContentNegotiation) { @@ -36,6 +41,7 @@ actual fun createHttpClient(): HttpClient { install(DefaultRequest) { headers.append("Accept-Language", getDeviceLanguage()) + headers.append("X-Timezone", getDeviceTimezone()) } engine { diff --git a/composeApp/src/jsMain/kotlin/com/example/casera/network/ApiClient.js.kt b/composeApp/src/jsMain/kotlin/com/example/casera/network/ApiClient.js.kt index 95f7aad..1e1504a 100644 --- a/composeApp/src/jsMain/kotlin/com/example/casera/network/ApiClient.js.kt +++ b/composeApp/src/jsMain/kotlin/com/example/casera/network/ApiClient.js.kt @@ -15,6 +15,11 @@ actual fun getDeviceLanguage(): String { return window.navigator.language.split("-").firstOrNull() ?: "en" } +actual fun getDeviceTimezone(): String { + // Use Intl API to get IANA timezone name (e.g., "America/Los_Angeles") + return js("Intl.DateTimeFormat().resolvedOptions().timeZone") as String +} + actual fun createHttpClient(): HttpClient { return HttpClient(Js) { install(ContentNegotiation) { @@ -32,6 +37,7 @@ actual fun createHttpClient(): HttpClient { install(DefaultRequest) { headers.append("Accept-Language", getDeviceLanguage()) + headers.append("X-Timezone", getDeviceTimezone()) } } } diff --git a/composeApp/src/jvmMain/kotlin/com/example/casera/network/ApiClient.jvm.kt b/composeApp/src/jvmMain/kotlin/com/example/casera/network/ApiClient.jvm.kt index f2fc711..eb5513c 100644 --- a/composeApp/src/jvmMain/kotlin/com/example/casera/network/ApiClient.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/com/example/casera/network/ApiClient.jvm.kt @@ -8,6 +8,7 @@ import io.ktor.client.plugins.logging.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json import java.util.Locale +import java.util.TimeZone actual fun getLocalhostAddress(): String = "127.0.0.1" @@ -15,6 +16,10 @@ actual fun getDeviceLanguage(): String { return Locale.getDefault().language } +actual fun getDeviceTimezone(): String { + return TimeZone.getDefault().id +} + actual fun createHttpClient(): HttpClient { return HttpClient(CIO) { install(ContentNegotiation) { @@ -32,6 +37,7 @@ actual fun createHttpClient(): HttpClient { install(DefaultRequest) { headers.append("Accept-Language", getDeviceLanguage()) + headers.append("X-Timezone", getDeviceTimezone()) } } } diff --git a/composeApp/src/wasmJsMain/kotlin/com/example/casera/network/ApiClient.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/com/example/casera/network/ApiClient.wasmJs.kt index 95f7aad..1e1504a 100644 --- a/composeApp/src/wasmJsMain/kotlin/com/example/casera/network/ApiClient.wasmJs.kt +++ b/composeApp/src/wasmJsMain/kotlin/com/example/casera/network/ApiClient.wasmJs.kt @@ -15,6 +15,11 @@ actual fun getDeviceLanguage(): String { return window.navigator.language.split("-").firstOrNull() ?: "en" } +actual fun getDeviceTimezone(): String { + // Use Intl API to get IANA timezone name (e.g., "America/Los_Angeles") + return js("Intl.DateTimeFormat().resolvedOptions().timeZone") as String +} + actual fun createHttpClient(): HttpClient { return HttpClient(Js) { install(ContentNegotiation) { @@ -32,6 +37,7 @@ actual fun createHttpClient(): HttpClient { install(DefaultRequest) { headers.append("Accept-Language", getDeviceLanguage()) + headers.append("X-Timezone", getDeviceTimezone()) } } }