Skip to content

Date.formatted(date:time:) ignores app locale on Android (uses JVM default) #128

Description

@sebsto

Summary

Date.formatted(date: .omitted, time: .shortened) produces AM/PM output on an Android device configured in French (fr), where it should produce 24-hour format (e.g. "14:30" not "2:30 PM").

Root Cause

In Sources/SkipFoundation/Date.swift (line ~235), the formatted(date:time:) implementation creates a bare DateFormatter() and sets dateStyle/timeStyle, but never assigns a .locale:

public func formatted(date: Date.FormatStyle.DateStyle, time: Date.FormatStyle.TimeStyle) -> String {
    let df = DateFormatter()
    // ... sets dateStyle / timeStyle ...
    return df.string(from: self)
}

When _locale is nil, DateFormatter.createDateFormat() calls java.text.DateFormat.getTimeInstance(SHORT) without a locale argument, which falls back to java.util.Locale.getDefault().

In a SkipFuse native app, java.util.Locale.getDefault() in the Swift runtime process may not match the Android app locale (it can be stuck on en_US), so French users get 12-hour AM/PM formatting.

Expected Behavior

Date.formatted(date:time:) should respect the app locale, matching Apple's Foundation behavior where .shortened produces locale-appropriate time (24h for French, 12h for English).

Suggested Fix

Set Locale.current on the DateFormatter before formatting:

public func formatted(date: Date.FormatStyle.DateStyle, time: Date.FormatStyle.TimeStyle) -> String {
    let df = DateFormatter()
    df.locale = Locale.current  // <-- add this
    // ... rest unchanged ...
    return df.string(from: self)
}

This ensures the formatter picks up the correct locale even when the JVM default diverges.

A deeper fix might also ensure Locale.current itself is synced to the Android app locale at process startup (if it isn't already), but propagating it explicitly in formatted would fix the immediate issue.

Environment

  • Skip native fuse app (mode: native)
  • Device language: French (fr)
  • SkipFoundation via SPM (latest)
  • Observed on Android emulator and physical device; iOS is unaffected (uses Apple Foundation)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions