Duration Style
Print out a nice text version of your newDuration
.
The Duration
type, introduced in at WWDC 2022, has its own set of styles available.
There are two format styles available for the Duration
type:
Time Style Xcode 14+
Time format style allows you to output your Duration
as a combination of hours, minutes, and seconds, which is set by the pattern
parameter on the style.
You can either initialize a new instance of Duration.TimeFormatStyle
, or use the .time(pattern:)
extension on FormatStyle.
Duration.seconds(1_000).formatted() // "0:16:40"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute)) // "0:17"
Duration.TimeFormatStyle(pattern: .hourMinute).format(Duration.seconds(1_000)) // "0:17"
You’ll notice that this is the default style when calling.formatted()
on anyDuration
without specifying a style.
There are six total patterns available, broken out into two types: properties and methods.
The properties are the “defaults” for each type of pattern and will choose the configuration for you.
Pattern | Description |
---|---|
.hourMinute |
Displays the hour and minute values for the Duration |
.hourMinuteSecond |
Displays the hour, minute, and second values for the Duration |
.minuteSecond |
Displays the minute and second values for the Duration |
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute)) // "0:17"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond)) // "0:16:40"
Duration.seconds(1_000).formatted(.time(pattern: .minuteSecond)) // "16:40"
The method variants allow you to fully customize the behaviour of each of the patterns:
Pattern | Description |
---|---|
.hourMinute() |
Displays the hour and minute values for the Duration |
.hourMinuteSecond() |
Displays the hour, minute, and second values for the Duration |
.minuteSecond() |
Displays the minute and second values for the Duration |
The following are the parameter options for hourMinute
:
Parameter | Description |
---|---|
padHourToLength |
Pads the output to include that number of digits. |
roundSeconds |
The rounding rule to use on the seconds value (See Rounding for all options). Defaults to .toNearestOrEven |
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 3, roundSeconds: .awayFromZero))) // "000:17"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .down))) // "000:16"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .toNearestOrAwayFromZero))) // "0:17"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .toNearestOrEven))) // "0:17"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .towardZero))) // "0:16"
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute(padHourToLength: 1, roundSeconds: .up))) // "0:17"
The following are the parameter options are for ‘minuteSecond`:
Parameter | Description |
---|---|
padMinuteToLength |
Pads the output to include that number of digits. |
fractionalSecondsLength |
The number of digits to include when displaying fractional seconds. |
roundFractionalSeconds |
The rounding rule to use on the seconds value (See Rounding for all options). Defaults to .toNearestOrEven |
Duration.seconds(1_000).formatted(
.time(
pattern: .hourMinuteSecond(
padHourToLength: 3,
fractionalSecondsLength: 3,
roundFractionalSeconds: .awayFromZero
)
)
) // "000:16:40.000"
Duration.seconds(1_000).formatted(
.time(
pattern: .minuteSecond(
padMinuteToLength: 3,
fractionalSecondsLength: 3,
roundFractionalSeconds: .awayFromZero
)
)
) // "016:40.000"
You can set the Locale
of the style by adding the .locale()
method to your .time(pattern:)
method, or including the locale parameter in the Duration.TimeFormatStyle
initializer.
Duration.seconds(1_000).formatted(.time(pattern: .hourMinute).locale(Locale(identifier: "fr_FR"))) // "0:17"
let frenchTimeFormatStyle = Duration.TimeFormatStyle(pattern: .minuteSecond, locale: Locale(identifier: "fr_FR"))
frenchTimeFormatStyle.format(Duration.seconds(1_000)) // "16:40"
You can output AttributedString
values by adding the .attributed
method.
Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond).attributed)
Unit Style Xcode 14+
The units style allows you to declare and customize the specific units to display for your duration.
You can either initialize a new instance of Duration.UnitsFormatStyle
, or use the .units()
extension on FormatStyle.
Duration.seconds(100).formatted(.units()) // "1 min, 40 sec"
Duration.UnitsFormatStyle(allowedUnits: [.hours, .minutes, .seconds], width: .abbreviated).format(.seconds(100)) // "1 min, 40 sec"
In both cases, there are two variants the initializer or style method.
Parameter | Description |
---|---|
.allowed |
A Set stating the possible units to display. |
width |
The “width” of the output string. |
maximumUnitCount |
The maximum number of time units to display. |
zeroValueUnits |
How to handle units with zero values. |
valueLength |
How to pad or truncate values for display. |
fractionalPart |
How to display fractional values if the unit can’t be shown |
Parameter | Description |
---|---|
.allowed |
A Set stating the possible units to display. |
width |
The “width” of the output string. |
maximumUnitCount |
The maximum number of time units to display. |
zeroValueUnits |
How to handle units with zero values. |
valueLengthLimits |
How to pad or truncate values for display. |
fractionalPart |
How to display fractional values if the unit can’t be shown |
If you can’t spot it at a glance, the difference is the valueLength
/valueLengthLimits
parameter which configures how each unit is padded or truncated. valueLength
accepts an optional Integer
, while valueLengthLimits
takes an optional ValueRange
value.
This parameter, passed in as a Set< Duration.UnitsFormatStyle.Unit>
, declares which units to use in the final display. By default, if a unit has a value of “0”, then it will be omitted from the final string. You can override this functionality with the zeroValueUnits
parameter.
The following units are available (from smallest to largest):
.nanoseconds
.microseconds
.miliseconds
.seconds
.minutes
.hours
.days
.weeks
Duration.milliseconds(500).formatted(.units(allowed: [.nanoseconds])) // "500,000,000 ns"
Duration.milliseconds(500).formatted(.units(allowed: [.microseconds])) // "500,000 μs"
Duration.milliseconds(500).formatted(.units(allowed: [.milliseconds])) // "500 ms"
Duration.milliseconds(500).formatted(.units(allowed: [.seconds])) // "0 sec"
Duration.milliseconds(500).formatted(.units(allowed: [.minutes])) // "0 min"
Duration.milliseconds(500).formatted(.units(allowed: [.hours])) // "0 hr"
Duration.milliseconds(500).formatted(.units(allowed: [.days])) // "0 days"
Duration.milliseconds(500).formatted(.units(allowed: [.weeks])) // "0 wks"
Duration.seconds(1_000_000.00123).formatted(
.units(
allowed: [
.nanoseconds,
.milliseconds,
.milliseconds,
.seconds,
.minutes,
.hours,
.days,
.weeks
]
)
) // "1 wk, 4 days, 13 hr, 46 min, 40 sec, 1 ms, 230,000 ns"
Duration.seconds(1).formatted(
.units(
allowed: [
.nanoseconds,
.milliseconds,
.milliseconds,
.seconds,
.minutes,
.hours,
.days,
.weeks
]
)
) // "1 sec"
This parameter controls how verbose/wordy and condensed the string output is.
Parameter | Description |
---|---|
.wide |
Shows the full unit name, eg. “3 hours” |
.abbreviated |
Shows the shortened version of the unit “3 hrs” |
.condensedAbbreviated |
Shows the shortened version, without a space between the value and unit “3hr” |
.narrow |
Shows the shortest possible unit name |
Duration.seconds(100).formatted(.units(width: .abbreviated)) // "1 min, 40 sec"
Duration.seconds(100).formatted(.units(width: .condensedAbbreviated)) // "1 min,40 sec"
Duration.seconds(100).formatted(.units(width: .narrow)) // "1m 40s"
Duration.seconds(100).formatted(.units(width: .wide)) // "1 minute, 40 seconds"
Controls the number of units to display in the final string. This works in tandem with the .units
property.
Duration.seconds(10000).formatted(.units(maximumUnitCount: 1)) // "3 hr"
Duration.seconds(10000).formatted(.units(maximumUnitCount: 2)) // "2 hr, 47 min"
Duration.seconds(10000).formatted(.units(maximumUnitCount: 3)) // "2 hr, 46 min, 40 sec"
Controls how units with a value of zero are shown or not, and if set to .show
, how many zeros to use in their display.
Duration.seconds(100).formatted(.units(zeroValueUnits: .hide)) // "1 min, 40 sec"
Duration.seconds(100).formatted(.units(zeroValueUnits: .show(length: 1))) // "0 hr, 1 min, 40 sec"
Duration.seconds(100).formatted(.units(zeroValueUnits: .show(length: 3))) // "000 hr, 001 min, 040 sec"
Controls how many digits of each unit to display. valueLength
accepts a fixed integer, while valueLengthLimits
accepts a range.
Duration.seconds(1_000).formatted(.units(valueLength: 1)) // "16 min, 40 sec"
Duration.seconds(1_000).formatted(.units(valueLength: 3)) // "016 min, 040 sec"
Duration.seconds(10_000).formatted(.units(valueLengthLimits: 1...)) // This is a bug (Feedback FB10607619)
Duration.seconds(10_000).formatted(.units(valueLengthLimits: ...3)) // "2 hr, 46 min, 40 sec"
Duration.seconds(100).formatted(.units(valueLengthLimits: 2 ... 3)) // "01 min, 40 sec"
As of Xcode 14.0 beta 3 (14A5270f), there’s a bug when you use aValueRange
of1...
. In the above example, the output is “16.0 measure-unit/duration-minute, 40.0 measure-unit/duration-second” instead of “16 min, 40 sec”. Feedback FB10607619 has been submitted.
Controls how fractional values are handled for display.
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide)) // "10 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .up))) // "11 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .towardZero))) // "10 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .toNearestOrEven))) // "10 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .toNearestOrAwayFromZero))) // "10 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .down))) // "10 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .hide(rounded: .awayFromZero))) // "11 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 0))) // "10 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 5))) // "10.00230 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .up))) // "10.003 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .towardZero))) // "10.002 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .toNearestOrEven))) // "10.002 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .toNearestOrAwayFromZero))) // "10.002 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .down))) // "10.002 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, rounded: .awayFromZero))) // "10.003 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, increment: 1))) // "10.000 sec"
Duration.seconds(10.0_023).formatted(.units(fractionalPart: .show(length: 3, increment: 0.001))) // "10.002 sec"