Date.RelativeFormatStyle

This is part of the FormatStyle Deep Dive series

Displaying relative dates in a localized fashion is fiendishly complex when you sit and think about it. Apple providing it to us developers in such a simple package is a lifeline.

Note: This style cannot output AttributedString values by appending .attributed.


Download the Xcode Playground with all examples

See the examples as a gist


let thePast = Calendar(identifier: .gregorian).date(byAdding: .day, value: -14, to: Date())!

// MARK: - Without Units
thePast.formatted(.relative(presentation: .numeric)) // "2 weeks ago"
thePast.formatted(.relative(presentation: .named))   // "2 weeks ago"

// MARK: - Including Units
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .abbreviated)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .narrow))      // "2 wk. ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .spellOut))    // "two weeks ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .wide))        // "2 weeks ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .abbreviated))   // "2 wk. ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .narrow))        // "2 wk. ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut))      // "two weeks ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .wide))          // "2 weeks ago"

You can set the locale in-line by using the .locale() call:

let franceLocale = Locale(identifier: "fr_FR")
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut).locale(franceLocale)) // "il y a deux semaines"

By creating an instance of Date.RelativeFormatStyle, you can additionally customize the locale, calendar and capitalization context of the style.

// MARK: - Custom RelativeFormatStyle
let relativeInFrench = Date.RelativeFormatStyle(
    presentation: .named,
    unitsStyle: .spellOut,
    locale: Locale(identifier: "fr_FR"),
    calendar: Calendar(identifier: .gregorian),
    capitalizationContext: .beginningOfSentence
)

thePast.formatted(relativeInFrench) // "Il y a deux semaines"
relativeInFrench.format(thePast) // "Il y a deux semaines"

And finally, you can wrap the custom RelativeFormatStyle in a FormatStyle extension for easier access:

struct InFrench: FormatStyle {
    typealias FormatInput = Date
    typealias FormatOutput = String

    static let relativeInFrench = Date.RelativeFormatStyle(
        presentation: .named,
        unitsStyle: .spellOut,
        locale: Locale(identifier: "fr_FR"),
        calendar: Calendar(identifier: .gregorian),
        capitalizationContext: .beginningOfSentence
    )

    func format(_ value: Date) -> String {
        InFrench.relativeInFrench.format(value)
    }
}

extension FormatStyle where Self == InFrench {
    static var inFrench: InFrench { .init() }
}

thePast.formatted(.inFrench) // "Il y a deux semaines"

Download the Xcode Playground with all examples

See the examples as a gist


Tags: