Home > Mobile >  How to store and format a quantity of days/weeks/months in Swift?
How to store and format a quantity of days/weeks/months in Swift?

Time:09-29

I would like to create not Date but simple quantities of time (i.e. durations) whose units are either days, weeks or months, then format them nicely as "1 day", "7 days", "3 weeks", "1 month", etc.

However both Duration and Measurement<UnitDuration> initializers do not accept these units.

// Type 'Duration' has no member 'days'
let duration: Duration = .days(7)
// Type 'UnitDuration' has no member 'days'
let measurement: Measurement<UnitDuration> = .init(value: 7, unit: .days)

I tried to extend UnitDuration but it does not allow to automatically format units to plural.

extension UnitDuration {
    static let days = UnitDuration(
        symbol: "days",
        converter: UnitConverterLinear(coefficient: 60 * 60 * 24)
    )
}

let measurement: Measurement<UnitDuration> = .init(value: 1, unit: .days)
measurement.formatted(.measurement(width: .wide, usage: .asProvided)) // "1 days" :(

The aim is to get something like this :

let onDay: Duration = .days(1)
let eightWeeks: Duration = .weeks(8)
oneDay.formatted() // "1 day"
eightWeeks.formatted() // "8 weeks"

CodePudding user response:

As Fabio says, you could use a DateComponentsFormatter:

formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.allowedUnits = [.weekOfMonth]

let components = DateComponents(weekOfMonth: 13)

if let string = formatter.string(from: components) {
    print(string)
} else {
    print("Unable to convert components to a String")
}

That code outputs "13 weeks" as expected

CodePudding user response:

This sounds like a scenario for an enum with associated values.

enum MyDuration{
    case day(Int), week(Int), month(Int)
    
    var formatted: String{
        switch self{
            
        case .day(let day):
            return day == 1 ? "\(day) day" : "\(day) days"
        case .week(let week):
            return week == 1 ? "\(week) week" : "\(week) weeks"
        case .month(let month):
            return month == 1 ? "\(month) month" : "\(month) months"
        }
    }
}

let myDay: MyDuration = .day(3)
let myWeek: MyDuration = .week(1)

print(myDay.formatted)
print(myWeek.formatted)

Output:

3 days 
1 week
  • Related