Skip to content

Instantly share code, notes, and snippets.

@brettohland
Last active July 23, 2024 12:12
Show Gist options
  • Save brettohland/ac2fbd1446bc7bb64da491587b010e3c to your computer and use it in GitHub Desktop.
Save brettohland/ac2fbd1446bc7bb64da491587b010e3c to your computer and use it in GitHub Desktop.
FormatStyle in Excruciating Detail
import Foundation
// Dates
Date(timeIntervalSinceReferenceDate: 0).formatted() // "12/31/2000, 5:00 PM"
// Measurements
Measurement(value: 20, unit: UnitDuration.minutes).formatted() // "20 min"
Measurement(value: 300, unit: UnitLength.miles).formatted() // "300 mi"
Measurement(value: 10, unit: UnitMass.kilograms).formatted() // "22 lb"
Measurement(value: 100, unit: UnitTemperature.celsius).formatted() // "212°F"
// Numbers
32.formatted() // "32"
Decimal(20.0).formatted() // "20"
Float(10.0).formatted() // "10"
Int(2).formatted() // "2"
Double(100.0003).formatted() // "100.0003"
// Names
PersonNameComponents(givenName: "Johnny", familyName: "Appleseed").formatted() // "Johnny Appleseed"
// Lists
["Alba", "Bruce", "Carol", "Billson"].formatted() // "Alba, Bruce, Carol, and Billson"
// TimeInterval
let referenceDay = Date(timeIntervalSinceReferenceDate: 0)
(referenceDay ..< referenceDay.addingTimeInterval(200)).formatted() // "12/31/00, 5:00 – 5:03 PM"
// Calling format on a style
let byteCountStyle = ByteCountFormatStyle(
style: .file,
allowedUnits: .all,
spellsOutZero: true,
includesActualByteCount: true,
locale: Locale.current
)
byteCountStyle.format(1_000_000_000) // "1 GB (1,000,000,000 bytes)"
import PlaygroundSupport
import SwiftUI
// Not all FormatStyle instances provided by Apple will output AttributedString values.
// Check the blog for all of the details.
struct ContentView: View {
var percentAttributed: AttributedString {
var percentAttributedString = 0.8890.formatted(.percent.attributed)
percentAttributedString.swiftUI.font = .title
percentAttributedString.runs.forEach { run in
if let numberRun = run.numberPart {
switch numberRun {
case .integer:
percentAttributedString[run.range].foregroundColor = .orange
case .fraction:
percentAttributedString[run.range].foregroundColor = .blue
}
}
if let symbolRun = run.numberSymbol {
switch symbolRun {
case .percent:
percentAttributedString[run.range].foregroundColor = .green
case .decimalSeparator:
percentAttributedString[run.range].foregroundColor = .red
default:
break
}
}
}
return percentAttributedString
}
var body: some View {
VStack {
Text(percentAttributed)
}
.padding()
}
}
PlaygroundPage.current.setLiveView(ContentView())
import Foundation
// MARK: - Duration.TimeFormatStyle
Duration.seconds(1_000).formatted() // "0:16:40"
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"
Duration.TimeFormatStyle(pattern: .hourMinute).format(Duration.seconds(1_000)) // "0:17"
Duration.TimeFormatStyle(pattern: .hourMinuteSecond).format(Duration.seconds(1_000)) // "0:16:40"
Duration.TimeFormatStyle(pattern: .minuteSecond).format(Duration.seconds(1_000)) // "16:40"
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"
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"
// MARK: Setting the Locale
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"
// MARK: Attributed String Output
Duration.seconds(1_000).formatted(.time(pattern: .hourMinuteSecond).attributed)
// MARK: - Duration.UnitsFormatStyle
Duration.seconds(100).formatted(.units()) // "1 min, 40 sec"
Duration.UnitsFormatStyle(allowedUnits: [.hours, .minutes, .seconds], width: .abbreviated)
.format(.seconds(100)) // "1 min, 40 sec"
// MARK: allowed
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.00_123).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"
// MARK: width
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"
// MARK: maximumUnitCount
Duration.seconds(10_000).formatted(.units(maximumUnitCount: 1)) // "3 hr"
Duration.seconds(10_000).formatted(.units(maximumUnitCount: 2)) // "2 hr, 47 min"
Duration.seconds(10_000).formatted(.units(maximumUnitCount: 3)) // "2 hr, 46 min, 40 sec"
// MARK: zeroValueUnits
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"
// MARK: valueLength
Duration.seconds(1_000).formatted(.units(valueLength: 1)) // "16 min, 40 sec"
Duration.seconds(1_000).formatted(.units(valueLength: 3)) // "016 min, 040 sec"
// MARK: valueLengthLimits
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"
// MARK: fractionalPart
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"
// MARK: Locale
Duration.seconds(100).formatted(.units().locale(Locale(identifier: "fr_FR"))) // "1 min et 40 s"
// MARK: Attributed String Output
Duration.seconds(100).formatted(.units().attributed)
import Foundation
let appleURL = URL(string: "https://apple.com")!
appleURL.formatted() // "https://apple.com"
appleURL.formatted(.url) // "https://apple.com"
appleURL.formatted(.url.locale(Locale(identifier: "fr_FR"))) // "https://apple.com"
var httpComponents = URLComponents(url: appleURL, resolvingAgainstBaseURL: false)!
httpComponents.scheme = "https"
httpComponents.user = "jAppleseed"
httpComponents.password = "Test1234"
httpComponents.host = "apple.com"
httpComponents.port = 80
httpComponents.path = "/macbook-pro"
httpComponents.query = "get-free"
httpComponents.fragment = "someFragmentOfSomething"
let complexURL = httpComponents.url!
let everythingStyle = URL.FormatStyle(
scheme: .always,
user: .always,
password: .always,
host: .always,
port: .always,
path: .always,
query: .always,
fragment: .always
)
everythingStyle
.format(complexURL) // "https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething"
let omitStyle = URL.FormatStyle(
scheme: .omitIfHTTPFamily,
user: .omitIfHTTPFamily,
password: .omitIfHTTPFamily,
host: .omitIfHTTPFamily,
port: .omitIfHTTPFamily,
path: .omitIfHTTPFamily,
query: .omitIfHTTPFamily,
fragment: .omitIfHTTPFamily
)
var httpsComponent = httpComponents
httpsComponent.scheme = "https"
let httpsURL = httpsComponent.url!
var ftpComponents = httpComponents
ftpComponents.scheme = "ftp"
let ftpURL = ftpComponents.url!
omitStyle.format(complexURL) // ""
omitStyle.format(httpsURL) // ""
omitStyle.format(ftpURL) // "ftp://jAppleseed@apple.com:80/macbook-pro?get-free#someFragmentOfSomething"
let localhostURL = URL(string: "https://localhost:80/macbook-pro")!
let displayWhen = URL.FormatStyle(
scheme: .always,
user: .never,
password: .never,
host: .displayWhen(.host, matches: ["localhost"]),
port: .always,
path: .always,
query: .never,
fragment: .never
)
displayWhen.format(complexURL) // "https://:80/macbook-pro"
displayWhen.format(localhostURL) // "https://localhost:80/macbook-pro"
let omitWhen = URL.FormatStyle(
scheme: .always,
user: .never,
password: .never,
host: .omitWhen(.host, matches: ["localhost"]),
port: .always,
path: .always,
query: .never,
fragment: .never
)
omitWhen.format(complexURL) // "https://apple.com:80/macbook-pro"
omitWhen.format(localhostURL) // "https://:80/macbook-pro"
let omitSpecificWhen = URL.FormatStyle(
scheme: .always,
user: .never,
password: .never,
host: .omitSpecificSubdomains(["secret"], includeMultiLevelSubdomains: false),
port: .always,
path: .always,
query: .never,
fragment: .never
)
var secretAppleURL = URL(string: "https://secret.apple.com/macbook-pro")!
omitSpecificWhen.format(complexURL) // "https://apple.com:80/macbook-pro"
omitSpecificWhen.format(secretAppleURL) // "https://apple.com/macbook-pro"
let omitSpecificWhenWhere = URL.FormatStyle(
scheme: .always,
user: .never,
password: .never,
host: .omitSpecificSubdomains(["secret"], includeMultiLevelSubdomains: false, when: .user, matches: ["jAppleseed"]),
port: .always,
path: .always,
query: .never,
fragment: .never
)
let complexSecretURL =
URL(string: "https://jAppleseed:Test1234@secret.apple.com:80/macbook-pro?get-free#someFragmentOfSomething")!
omitSpecificWhenWhere.format(complexSecretURL) // "https://apple.com:80/macbook-pro"
omitSpecificWhenWhere.format(secretAppleURL) // "https://secret.apple.com/macbook-pro"
let parsedURL = URL(string: "https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething")
do {
try URL.FormatStyle.Strategy(port: .defaultValue(80)).parse("http://www.apple.com") // http://www.apple.com:80
try URL.FormatStyle.Strategy(port: .optional).parse("http://www.apple.com") // http://www.apple.com
try? URL.FormatStyle.Strategy(port: .required).parse("http://www.apple.com") // nil
// This returns a valid URL
try URL.FormatStyle.Strategy()
.scheme(.required)
.user(.required)
.password(.required)
.host(.required)
.port(.required)
.path(.required)
.query(.required)
.fragment(.required)
.parse("https://jAppleseed:Test1234@apple.com:80/macbook-pro?get-free#someFragmentOfSomething")
// This throws an error
try URL.FormatStyle.Strategy()
.scheme(.required)
.user(.required)
.password(.required)
.host(.required)
.port(.required)
.path(.required)
.query(.required)
.fragment(.required)
.parse("https://jAppleseed:Test1234@apple.com/macbook-pro?get-free#someFragmentOfSomething")
} catch {
print(error)
}
import Foundation
10.formatted(.number) // "10"
FloatingPointFormatStyle<Double>().rounded(rule: .up, increment: 1).format(10.9) // "11"
IntegerFormatStyle<Int>().notation(.compactName).format(1_000) // "1K"
Decimal.FormatStyle().scale(10).format(1) // "10"
// MARK: - Rounded
Double(1.9999999).formatted(.number.rounded()) // "2"
Decimal(1.9999999).formatted(.number.rounded()) // "2"
Float(1.9999999).formatted(.number.rounded()) // "2"
Int(1.9999999).formatted(.number.rounded()) // "1"
Float(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Float(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Float(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Float(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Float(0.01).formatted(.number.rounded(rule: .down)) // "0.009999"
Float(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Float(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.009999"
Float(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Float(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Float(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Double(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Double(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Double(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Double(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Double(0.01).formatted(.number.rounded(rule: .down)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.01"
Double(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Double(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Double(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
Decimal(0.26575467567788).formatted(.number.rounded(rule: .awayFromZero)) // "0.265755"
Decimal(0.00900999876871).formatted(.number.rounded(rule: .awayFromZero)) // "0.00901"
Decimal(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 1)) // "6"
Decimal(5.01).formatted(.number.rounded(rule: .awayFromZero, increment: 10)) // "10"
Decimal(0.01).formatted(.number.rounded(rule: .down)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .towardZero)) // "0.01"
Decimal(0.01).formatted(.number.rounded(rule: .up)) // "0.01"
Decimal(5.01).formatted(.number.rounded(rule: .down, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .towardZero, increment: 1)) // "5"
Decimal(5.01).formatted(.number.rounded(rule: .up, increment: 1)) // "5"
// MARK: - Sign
Float(1.90).formatted(.number.sign(strategy: .never)) // "1.9"
Float(-1.90).formatted(.number.sign(strategy: .never)) // "1.9"
Float(1.90).formatted(.number.sign(strategy: .automatic)) // "1.9"
Float(1.90).formatted(.number.sign(strategy: .always())) // "+1.9"
Float(0).formatted(.number.sign(strategy: .always(includingZero: true))) // "+0"
Float(0).formatted(.number.sign(strategy: .always(includingZero: false))) // "0"
// MARK: - Decimal Separator
Float(10).formatted(.number.decimalSeparator(strategy: .automatic)) // "10"
Float(10).formatted(.number.decimalSeparator(strategy: .always)) // "10."
// MARK: - Grouping
Float(1_000).formatted(.number.grouping(.automatic)) // "1,000"
Float(1_000).formatted(.number.grouping(.never)) // "1000"
// MARK: - Precision
Decimal(10.1).formatted(.number.precision(.significantDigits(1))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(2))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(3))) // "10.1"
Decimal(10.1).formatted(.number.precision(.significantDigits(4))) // "10.10"
Decimal(10.1).formatted(.number.precision(.significantDigits(5))) // "10.100"
Decimal(1_000_000.1).formatted(.number.precision(.significantDigits(5))) // "10.100"
Decimal(1).formatted(.number.precision(.significantDigits(1...3))) // "1"
Decimal(10).formatted(.number.precision(.significantDigits(1...3))) // "10"
Decimal(10.1).formatted(.number.precision(.significantDigits(1...3))) // "10.1"
Decimal(10.01).formatted(.number.precision(.significantDigits(1...3))) // "10"
Decimal(10.01).formatted(.number.precision(.fractionLength(1))) // 10.0
Decimal(10.01).formatted(.number.precision(.fractionLength(2))) // 10.01
Decimal(10.01).formatted(.number.precision(.fractionLength(3))) // 10.010
Decimal(10).formatted(.number.precision(.fractionLength(0...2))) // 10
Decimal(10.1).formatted(.number.precision(.fractionLength(0...2))) // 10.1
Decimal(10.11).formatted(.number.precision(.fractionLength(0...2))) // 10.11
Decimal(10.111).formatted(.number.precision(.fractionLength(0...2))) // 10.11
Decimal(10.111).formatted(.number.precision(.integerLength(1))) // 0.111
Decimal(10.111).formatted(.number.precision(.integerLength(2))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerLength(0...1))) // .111
Decimal(10.111).formatted(.number.precision(.integerLength(0...2))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerLength(0...3))) // 10.111
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 1, fraction: 1))) // 0.1
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 1))) // 10.1
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 2))) // 10.11
Decimal(10.111).formatted(.number.precision(.integerAndFractionLength(integer: 2, fraction: 3))) // 10.111
// MARK: - Notation
Float(1_000).formatted(.number.notation(.automatic)) // "1,000"
1_000.formatted(.number.notation(.compactName)) // "1K"
1_000_000_000.formatted(.number.notation(.compactName)) // 1B
1_500_000.formatted(.number.notation(.compactName)) // 1.5M
Float(1_000).formatted(.number.notation(.scientific)) // "1E3"
// MARK: - Scale
Float(10).formatted(.number.scale(1.0)) // "10"
Float(10).formatted(.number.scale(1.5)) // "15"
Float(10).formatted(.number.scale(2.0)) // "20"
Float(10).formatted(.number.scale(-2.0)) // "-20"
// MARK: - Compositing
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K"
// MARK: - Locale
Float(1_000).formatted(.number.notation(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1_000).formatted(.number.notation(.compactName).locale(Locale(identifier: "fr_FR"))) // "1 k"
Float(1_000).formatted(.number.notation(.scientific).locale(Locale(identifier: "fr_FR"))) // "1E3"
Float(1_000).formatted(.number.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "1 000"
Float(1_000).formatted(.number.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "1000"
// MARK: - Attributed String Output
Float(10).formatted(.number.scale(200.0).notation(.compactName).grouping(.automatic).attributed)
// MARK: - Initializing
let frenchStyle = IntegerFormatStyle<Int>()
.notation(.compactName)
.locale(Locale(identifier: "fr_FR"))
.scale(20)
frenchStyle.format(1_000) // "20 k"
1_000.formatted(frenchStyle) // "20 k"
// MARK: - Parsing Decimals
let decimal = Decimal(1_000.01)
try? Decimal.FormatStyle().parseStrategy.parse("-100.1002") // -100.1002
try? Decimal("-100.1002", strategy: Decimal.FormatStyle().scale(2).parseStrategy) // -50.0501
import Foundation
0.1.formatted(.percent) // "10%"
FloatingPointFormatStyle<Double>.Percent().rounded(rule: .up, increment: 1).format(0.109) // "11%"
IntegerFormatStyle<Int>.Percent().notation(.compactName).format(1_000) // "1K%"
Decimal.FormatStyle.Percent().scale(12).format(0.1) // "1.2%"
// MARK: - Rounded
Double(1.9999999).formatted(.percent.rounded()) // "199.99999%"
Decimal(1.9999999).formatted(.percent.rounded()) // "199.99999%"
Float(1.9999999).formatted(.percent.rounded()) // "199.999998%"
Int(1.9999999).formatted(.percent.rounded()) // 1%
Float(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575467%"
Float(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Float(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "502%"
Float(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Float(0.01).formatted(.percent.rounded(rule: .down)) // "0.999999%"
Float(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Float(0.01).formatted(.percent.rounded(rule: .towardZero)) // "0.999999%"
Float(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Float(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "501%"
Float(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "502%"
Double(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575468%"
Double(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Double(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Double(0.01).formatted(.percent.rounded(rule: .down)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .towardZero)) // "1%"
Double(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Double(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "501%"
Double(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "501%"
Decimal(0.26575467567788).formatted(.percent.rounded(rule: .awayFromZero)) // "26.575468%"
Decimal(0.00900999876871).formatted(.percent.rounded(rule: .awayFromZero)) // "0.901%"
Decimal(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 1)) // "501%"
Decimal(5.01).formatted(.percent.rounded(rule: .awayFromZero, increment: 10)) // "510%"
Decimal(0.01).formatted(.percent.rounded(rule: .down)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .towardZero)) // "1%"
Decimal(0.01).formatted(.percent.rounded(rule: .up)) // "1%"
Decimal(5.01).formatted(.percent.rounded(rule: .down, increment: 1)) // "500%"
Decimal(5.01).formatted(.percent.rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "501%"
Decimal(5.01).formatted(.percent.rounded(rule: .towardZero, increment: 1)) // "500%"
Decimal(5.01).formatted(.percent.rounded(rule: .up, increment: 1)) // "501%"
// MARK: - Sign
Float(1.90).formatted(.percent.sign(strategy: .never)) // "189.999998%"
Float(-1.90).formatted(.percent.sign(strategy: .never)) // "189.999998%"
Float(1.90).formatted(.percent.sign(strategy: .automatic)) // "189.999998%"
Float(1.90).formatted(.percent.sign(strategy: .always())) // "+189.999998%"
Float(0).formatted(.percent.sign(strategy: .always(includingZero: true))) // "+0%"
Float(0).formatted(.percent.sign(strategy: .always(includingZero: false))) // "0%"
// MARK: - Decimal Separator
Float(10).formatted(.percent.decimalSeparator(strategy: .automatic)) // "1,000%"
Float(10).formatted(.percent.decimalSeparator(strategy: .always)) // "1,000.%"
// MARK: - Grouping
Float(1_000).formatted(.percent.grouping(.automatic)) // "100,000%"
Float(1_000).formatted(.percent.grouping(.never)) // "100000%"
// MARK: - Precision
Decimal(10.1).formatted(.percent.precision(.significantDigits(1))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(2))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(3))) // "1,010%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(4))) // "1,010%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(5))) // "1,010.0%"
Decimal(1).formatted(.percent.precision(.significantDigits(1...3))) // "100%"
Decimal(10).formatted(.percent.precision(.significantDigits(1...3))) // "1,000%"
Decimal(10.1).formatted(.percent.precision(.significantDigits(1...3))) // "1,010%"
Decimal(10.01).formatted(.percent.precision(.significantDigits(1...3))) // "1,000%"
Decimal(0.0001).formatted(.percent.precision(.fractionLength(1))) // 0.0%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(2))) // 0.001%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(3))) // 0.010%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...1))) // 0%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...2))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...3))) // 0.01%
Decimal(0.0001).formatted(.percent.precision(.fractionLength(0...4))) // 0.01%
Decimal(10.111).formatted(.percent.precision(.integerLength(1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(2))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...2))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerLength(0...3))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 1, fraction: 1))) // 1.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 1))) // 11.1%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 2))) // 11.10%
Decimal(10.111).formatted(.percent.precision(.integerAndFractionLength(integer: 2, fraction: 3))) // 11.100%
// MARK: - Notation
Float(1_000).formatted(.percent.notation(.automatic)) // "100,000%"
Float(1_000).formatted(.percent.notation(.compactName)) // "100K%"
Float(1_000).formatted(.percent.notation(.scientific)) // "1E5%"
// MARK: - Scale
Float(10).formatted(.percent.scale(1.0)) // "10%"
Float(10).formatted(.percent.scale(1.5)) // "15%"
Float(10).formatted(.percent.scale(2.0)) // "20%"
Float(10).formatted(.percent.scale(-2.0)) // "-20%"
// MARK: Compositing
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic)) // "2K%"
// MARK: - Locale
Float(1_000).formatted(.percent.grouping(.automatic).locale(Locale(identifier: "fr_FR"))) // "100 000 %"
Float(1_000).formatted(.percent.grouping(.never).locale(Locale(identifier: "fr_FR"))) // "100000 %"
Float(1_000).formatted(.percent.notation(.automatic).locale(Locale(identifier: "fr_FR"))) // "100 000 %"
Float(1_000).formatted(.percent.notation(.compactName).locale(Locale(identifier: "fr_FR"))) // "100 k %"
Float(1_000).formatted(.percent.notation(.scientific).locale(Locale(identifier: "fr_FR"))) // "1E5 %"
// MARK: - Attributed String Output
Float(10).formatted(.percent.scale(200.0).notation(.compactName).grouping(.automatic).attributed)
// MARK: - Initializing
let frenchStyle = FloatingPointFormatStyle<Double>.Percent()
.notation(.compactName)
.locale(Locale(identifier: "fr_FR"))
frenchStyle.format(0.1) // "10 %"
0.1.formatted(frenchStyle) // "10 %"
import Foundation
10.formatted(.currency(code: "JPY")) // "10%"
FloatingPointFormatStyle<Double>.Currency(code: "JPY").rounded(rule: .up, increment: 1).format(10.9) // ¥11"
IntegerFormatStyle<Int>.Currency(code: "GBP").presentation(.fullName).format(42) // "42.00 British pounds"
Decimal.FormatStyle.Currency(code: "USD").scale(12).format(0.1) // "$1.20"
// MARK: - Rounded
Decimal(0.59).formatted(.currency(code: "GBP").rounded()) // "£0.59"
Decimal(0.599).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(0.5999).formatted(.currency(code: "GBP").rounded()) // "£0.60"
Decimal(5.001).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero, increment: 1)) // "£6"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .awayFromZero, increment: 10)) // "£10"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .down)) // "£5.00"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .toNearestOrAwayFromZero)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .towardZero)) // "£5.00"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .up)) // "£5.01"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .down, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .toNearestOrAwayFromZero, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .towardZero, increment: 1)) // "£5"
Decimal(5.01).formatted(.currency(code: "GBP").rounded(rule: .up, increment: 1)) // "£5"
// MARK: - Grouping
Int(3_000).formatted(.currency(code: "GBP").grouping(.never)) // "£3000.00"
Int(3_000).formatted(.currency(code: "GBP").grouping(.automatic)) // "£3,000.00"
// MARK: - Precision
// Please don't use Floating point numbers to store currency. Please.
Float(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(4))) // "£3,000.0029" <- This is why
Float(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(1 ... 4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.fractionLength(1 ... 4))) // "£3,000.0029"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(3))) // "£000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(4))) // "£3,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(5))) // "£03,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0 ... 3))) // "£.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0 ... 4))) // "£3,000.00"
Decimal(3_000.003).formatted(.currency(code: "GBP").precision(.integerLength(0 ... 5))) // "£03,000.00"
Decimal(3).formatted(.currency(code: "GBP").precision(.integerAndFractionLength(integer: 4, fraction: 4))) // "£0,003.0000"
Decimal(3).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£3.0"
Decimal(3.00004).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£3.00004"
Decimal(3.000000004).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
)
Decimal(30_000.01).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£30,000.01"
Decimal(3_000_000.000001).formatted(
.currency(code: "GBP")
.precision(.integerAndFractionLength(integerLimits: 1 ... 5, fractionLimits: 1 ... 5))
) // "£0.0"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(1))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(2))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(3))) // "£10.1"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(4))) // "£10.10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(5))) // "£10.100"
Decimal(1).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£1"
Decimal(10).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10"
Decimal(10.1).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10.1"
Decimal(10.01).formatted(.currency(code: "GBP").precision(.significantDigits(1 ... 3))) // "£10"
// MARK: - Decimal Separator
Decimal(3_000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .automatic)) // "£3,000.00"
Decimal(3_000).formatted(.currency(code: "GBP").decimalSeparator(strategy: .always)) // "£3,000.00"
// MARK: - Presentation
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName)) // "10.00 British pounds"
Decimal(10).formatted(.currency(code: "GBP").presentation(.isoCode)) // "GBP 10.00"
Decimal(10).formatted(.currency(code: "GBP").presentation(.narrow)) // "£10.00"
Decimal(10).formatted(.currency(code: "GBP").presentation(.standard)) // "£10.00"
// MARK: Scale
Decimal(10).formatted(.currency(code: "GBP").scale(1)) // "£10.00"
Decimal(10).formatted(.currency(code: "GBP").scale(1.5)) // "£15.00"
Decimal(10).formatted(.currency(code: "GBP").scale(-1.5)) // "-£15.00"
Decimal(10).formatted(.currency(code: "GBP").scale(10)) // "£100.00"
// MARK: - Locale
Decimal(10).formatted(.currency(code: "GBP").presentation(.fullName).locale(Locale(identifier: "fr_FR"))) // "10,00 livres sterling"
// MARK: - Sign
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .automatic)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .never)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accounting)) // "£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways())) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways(showZero: true))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .accountingAlways(showZero: false))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always())) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always(showZero: true))) // "+£7.00"
Decimal(7).formatted(.currency(code: "GBP").sign(strategy: .always(showZero: false))) // "+£7.00"
// MARK: Compositing
Decimal(10).formatted(.currency(code: "GBP").scale(200.0).sign(strategy: .always()).presentation(.fullName)) // "+2,000.00 British pounds"
// MARK: - Attributed String
Decimal(10).formatted(.currency(code: "GBP").scale(200.0).sign(strategy: .always()).presentation(.fullName).attributed)
// MARK: - Initializing
let frenchStyle = IntegerFormatStyle<Int>.Currency(code: "GBP")
.sign(strategy: .always())
.presentation(.fullName)
.locale(Locale(identifier: "fr_FR"))
frenchStyle.format(1_000) // "+1 000,00 livres sterling"
1_000.formatted(frenchStyle) // "+1 000,00 livres sterling"
// MARK: - Parsing
let parsingStyle = Decimal.FormatStyle.Currency(code: "GBP").presentation(.fullName)
try? parsingStyle.parseStrategy.parse("10.00 British pounds") // 10
try? Decimal("10.00 British pounds", strategy: parsingStyle.parseStrategy) // 10
import Foundation
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
// Order when method chaining doesn't matter to final display
twosday.formatted(
Date.FormatStyle().year().month().day().hour().minute().second()
) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(
Date.FormatStyle().second().minute().hour().day().month().year()
) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(
.dateTime.year().month().day().hour().minute().second()
) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(
.dateTime.second().minute().hour().day().month().year()
) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(.dateTime.day()) // "22"
twosday.formatted(.dateTime.dayOfYear()) // "53"
twosday.formatted(.dateTime.era()) // "AD"
twosday.formatted(.dateTime.hour()) // "2 AM"
twosday.formatted(.dateTime.minute()) // "22"
twosday.formatted(.dateTime.month()) // "Feb"
twosday.formatted(.dateTime.quarter()) // "Q1"
twosday.formatted(.dateTime.second()) // "22"
twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00"
twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(.dateTime.timeZone()) // "MST"
twosday.formatted(.dateTime.week()) // "9"
twosday.formatted(.dateTime.weekday()) // "Tue"
twosday.formatted(.dateTime.year()) // "2022"
twosday.formatted(Date.FormatStyle().day()) // "22"
twosday.formatted(Date.FormatStyle().dayOfYear()) // "53"
twosday.formatted(Date.FormatStyle().era()) // "AD"
twosday.formatted(Date.FormatStyle().hour()) // "2 AM"
twosday.formatted(Date.FormatStyle().minute()) // "22"
twosday.formatted(Date.FormatStyle().month()) // "Feb"
twosday.formatted(Date.FormatStyle().quarter()) // "Q1"
twosday.formatted(Date.FormatStyle().second()) // "22"
twosday.formatted(Date.FormatStyle().secondFraction(.fractional(2))) // "00"
twosday.formatted(Date.FormatStyle().secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(Date.FormatStyle().timeZone()) // "MST"
twosday.formatted(Date.FormatStyle().week()) // "9"
twosday.formatted(Date.FormatStyle().weekday()) // "Tue"
twosday.formatted(Date.FormatStyle().year()) // "2022"
// Era
twosday.formatted(.dateTime.era(.abbreviated)) // "AD"
twosday.formatted(.dateTime.era(.narrow)) // "A"
twosday.formatted(.dateTime.era(.wide)) // "Anno Domini"
twosday.formatted(Date.FormatStyle().era(.abbreviated)) // "AD"
twosday.formatted(Date.FormatStyle().era(.narrow)) // "A"
twosday.formatted(Date.FormatStyle().era(.wide)) // "Anno Domini"
// Year
twosday.formatted(.dateTime.year(.twoDigits)) // "22"
twosday.formatted(.dateTime.year(.defaultDigits)) // "2022"
twosday.formatted(.dateTime.year(.extended())) // "22"
twosday.formatted(.dateTime.year(.extended(minimumLength: 2))) // "2022"
twosday.formatted(.dateTime.year(.padded(10))) // "0000002022"
twosday.formatted(.dateTime.year(.relatedGregorian())) // "2022"
twosday.formatted(.dateTime.year(.relatedGregorian(minimumLength: 2))) // "22"
twosday.formatted(Date.FormatStyle().year(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().year(.defaultDigits)) // "2022"
twosday.formatted(Date.FormatStyle().year(.extended())) // "22"
twosday.formatted(Date.FormatStyle().year(.extended(minimumLength: 2))) // "2022"
twosday.formatted(Date.FormatStyle().year(.padded(10))) // "0000002022"
twosday.formatted(Date.FormatStyle().year(.relatedGregorian())) // "2022"
twosday.formatted(Date.FormatStyle().year(.relatedGregorian(minimumLength: 2))) // "22"
// Quarter
twosday.formatted(.dateTime.quarter(.narrow)) // "1"
twosday.formatted(.dateTime.quarter(.abbreviated)) // "Q1"
twosday.formatted(.dateTime.quarter(.wide)) // "1st quarter"
twosday.formatted(.dateTime.quarter(.twoDigits)) // "01"
twosday.formatted(.dateTime.quarter(.oneDigit)) // "1"
twosday.formatted(Date.FormatStyle().quarter(.narrow)) // "1"
twosday.formatted(Date.FormatStyle().quarter(.abbreviated)) // "Q1"
twosday.formatted(Date.FormatStyle().quarter(.wide)) // "1st quarter"
twosday.formatted(Date.FormatStyle().quarter(.twoDigits)) // "01"
twosday.formatted(Date.FormatStyle().quarter(.oneDigit)) // "1"
// Month
twosday.formatted(.dateTime.month(.defaultDigits)) // "2"
twosday.formatted(.dateTime.month(.twoDigits)) // "02"
twosday.formatted(.dateTime.month(.wide)) // "February"
twosday.formatted(.dateTime.month(.abbreviated)) // "Feb"
twosday.formatted(.dateTime.month(.narrow)) // "F"
twosday.formatted(Date.FormatStyle().month(.defaultDigits)) // "2"
twosday.formatted(Date.FormatStyle().month(.twoDigits)) // "02"
twosday.formatted(Date.FormatStyle().month(.wide)) // "February"
twosday.formatted(Date.FormatStyle().month(.abbreviated)) // "Feb"
twosday.formatted(Date.FormatStyle().month(.narrow)) // "F"
// Week
twosday.formatted(.dateTime.week(.defaultDigits)) // "9"
twosday.formatted(.dateTime.week(.twoDigits)) // "09"
twosday.formatted(.dateTime.week(.weekOfMonth)) // "9"
twosday.formatted(Date.FormatStyle().week(.defaultDigits)) // "9"
twosday.formatted(Date.FormatStyle().week(.twoDigits)) // "09"
twosday.formatted(Date.FormatStyle().week(.weekOfMonth)) // "9"
// Day
twosday.formatted(.dateTime.day(.twoDigits)) // "22"
twosday.formatted(.dateTime.day(.ordinalOfDayInMonth)) // "4"
twosday.formatted(.dateTime.day(.defaultDigits)) // "22"
twosday.formatted(.dateTime.day(.julianModified())) // "2459633"
twosday.formatted(.dateTime.day(.julianModified(minimumLength: 8))) // "02459633"
twosday.formatted(Date.FormatStyle().day(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().day(.ordinalOfDayInMonth)) // "4"
twosday.formatted(Date.FormatStyle().day(.defaultDigits)) // "22"
twosday.formatted(Date.FormatStyle().day(.julianModified())) // "2459633"
twosday.formatted(Date.FormatStyle().day(.julianModified(minimumLength: 8))) // "02459633"
// Day of Year
twosday.formatted(.dateTime.dayOfYear(.defaultDigits)) // "53"
twosday.formatted(.dateTime.dayOfYear(.threeDigits)) // "053"
twosday.formatted(.dateTime.dayOfYear(.twoDigits)) // "53"
twosday.formatted(Date.FormatStyle().dayOfYear(.defaultDigits)) // "53"
twosday.formatted(Date.FormatStyle().dayOfYear(.threeDigits)) // "053"
twosday.formatted(Date.FormatStyle().dayOfYear(.twoDigits)) // "53"
// Weekday
twosday.formatted(.dateTime.weekday(.abbreviated)) // "Tue"
twosday.formatted(.dateTime.weekday(.twoDigits)) // "3"
twosday.formatted(.dateTime.weekday(.short)) // "Tu"
twosday.formatted(.dateTime.weekday(.oneDigit)) // "3"
twosday.formatted(.dateTime.weekday(.wide)) // "Tuesday"
twosday.formatted(.dateTime.weekday(.narrow)) // "T"
twosday.formatted(Date.FormatStyle().weekday(.abbreviated)) // "Tue"
twosday.formatted(Date.FormatStyle().weekday(.twoDigits)) // "3"
twosday.formatted(Date.FormatStyle().weekday(.short)) // "Tu"
twosday.formatted(Date.FormatStyle().weekday(.oneDigit)) // "3"
twosday.formatted(Date.FormatStyle().weekday(.wide)) // "Tuesday"
twosday.formatted(Date.FormatStyle().weekday(.narrow)) // "T"
// Hour
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .omitted))) // "02"
// Minute
twosday.formatted(.dateTime.minute(.twoDigits)) // "22"
twosday.formatted(.dateTime.minute(.defaultDigits)) // "22"
twosday.formatted(Date.FormatStyle().minute(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().minute(.defaultDigits)) // "22"
// Second
twosday.formatted(.dateTime.second(.twoDigits)) // "22"
twosday.formatted(.dateTime.second(.defaultDigits)) // "22"
twosday.formatted(Date.FormatStyle().second(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().second(.defaultDigits)) // "22"
// Fractional Second
twosday.formatted(Date.FormatStyle().secondFraction(.fractional(2))) // "00"
twosday.formatted(Date.FormatStyle().secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00"
twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"
// Time Zone
twosday.formatted(.dateTime.timeZone(.exemplarLocation)) // "Edmonton"
twosday.formatted(.dateTime.timeZone(.genericLocation)) // "Edmonton Time"
twosday.formatted(.dateTime.timeZone(.genericName(.long))) // "Mountain Time"
twosday.formatted(.dateTime.timeZone(.genericName(.short))) // "MT"
twosday.formatted(.dateTime.timeZone(.identifier(.short))) // "caedm"
twosday.formatted(.dateTime.timeZone(.identifier(.long))) // "America/Edmonton"
twosday.formatted(.dateTime.timeZone(.iso8601(.long))) // "-07:00"
twosday.formatted(.dateTime.timeZone(.iso8601(.short))) // "-07:00"
twosday.formatted(.dateTime.timeZone(.specificName(.short))) // "MST"
twosday.formatted(.dateTime.timeZone(.specificName(.long))) // "Mountain Standard Time"
twosday.formatted(.dateTime.timeZone(.localizedGMT(.short))) // "GMT-7"
twosday.formatted(.dateTime.timeZone(.localizedGMT(.long))) // "GMT-07:00"
twosday.formatted(Date.FormatStyle().timeZone(.exemplarLocation)) // "Edmonton"
twosday.formatted(Date.FormatStyle().timeZone(.genericLocation)) // "Edmonton Time"
twosday.formatted(Date.FormatStyle().timeZone(.genericName(.long))) // "Mountain Time"
twosday.formatted(Date.FormatStyle().timeZone(.genericName(.short))) // "MT"
twosday.formatted(Date.FormatStyle().timeZone(.identifier(.short))) // "caedm"
twosday.formatted(Date.FormatStyle().timeZone(.identifier(.long))) // "America/Edmonton"
twosday.formatted(Date.FormatStyle().timeZone(.iso8601(.long))) // "-07:00"
twosday.formatted(Date.FormatStyle().timeZone(.iso8601(.short))) // "-07:00"
twosday.formatted(Date.FormatStyle().timeZone(.specificName(.short))) // "MST"
twosday.formatted(Date.FormatStyle().timeZone(.specificName(.long))) // "Mountain Standard Time"
twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.short))) // "GMT-7"
twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.long))) // "GMT-07:00"
// Locale
twosday.formatted(.dateTime.locale(Locale(identifier: "fr_FR"))) // "22/02/2022 à 2:22"
twosday.formatted(Date.FormatStyle().locale(Locale(identifier: "fr_FR"))) // "22/02/2022 à 2:22"
// Attributed String Output
twosday.formatted(.dateTime.attributed)
twosday.formatted(Date.FormatStyle().attributed)
// MARK: - Parsing
let dateString = "Feb 22, 2022, 2:22:22 AM"
let dateStyle = Date.FormatStyle()
.day()
.month()
.year()
.hour()
.minute()
.second()
try? dateStyle.parse(dateString) // Feb 22, 2022 at 2:22 AM
try? Date(dateString, strategy: dateStyle.parseStrategy) // Feb 22, 2022 at 2:22 AM
import Foundation
// MARK: - Setup
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
// MARK: - The Basics
twosday.formatted() // "2/22/2022, 2:22 AM"
// MARK: - DateStyle
twosday.formatted(date: .abbreviated, time: .omitted) // "Feb 22, 2022"
twosday.formatted(date: .complete, time: .omitted) // "Tuesday, February 22, 2022"
twosday.formatted(date: .long, time: .omitted) // "February 22, 2022"
twosday.formatted(date: .numeric, time: .omitted) // "2/22/2022"
// MARK: - TimeStyle
twosday.formatted(date: .omitted, time: .complete) // "2:22:22 AM MST"
twosday.formatted(date: .omitted, time: .shortened) // "2:22 AM"
twosday.formatted(date: .omitted, time: .standard) // "2:22:22 AM"
// MARK: - DateStyle & TimeStyle
twosday.formatted(date: .abbreviated, time: .complete) // "Feb 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .abbreviated, time: .shortened) // "Feb 22, 2022, 2:22 AM"
twosday.formatted(date: .abbreviated, time: .standard) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(date: .complete, time: .complete) // "Tuesday, February 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .complete, time: .shortened) // "Tuesday, February 22, 2022, 2:22 AM"
twosday.formatted(date: .complete, time: .standard) // "Tuesday, February 22, 2022, 2:22:22 AM"
twosday.formatted(date: .long, time: .complete) // "February 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .long, time: .shortened) // "February 22, 2022, 2:22 AM"
twosday.formatted(date: .long, time: .standard) // "February 22, 2022, 2:22:22 AM"
twosday.formatted(date: .numeric, time: .complete) // "2/22/2022, 2:22:22 AM MST"
twosday.formatted(date: .numeric, time: .shortened) // "2/22/2022, 2:22 AM"
twosday.formatted(date: .numeric, time: .standard) // "2/22/2022, 2:22:22 AM"
// MARK: - Custom Date.FormatStyle
let frenchHebrew = Date.FormatStyle(
date: .complete,
time: .complete,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .hebrew),
timeZone: TimeZone(secondsFromGMT: 0)!,
capitalizationContext: .standalone
)
twosday.formatted(frenchHebrew) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
frenchHebrew.format(twosday) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
// MARK: - Custom Date.FormatStyle wrapped in custom FormatStyle extension
struct FrenchHebrewStyle: FormatStyle {
typealias FormatInput = Date
typealias FormatOutput = String
static let frenchHebrew = Date.FormatStyle(
date: .complete,
time: .complete,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .hebrew),
timeZone: TimeZone(secondsFromGMT: 0)!,
capitalizationContext: .standalone
)
func format(_ value: Date) -> String {
FrenchHebrewStyle.frenchHebrew.format(value)
}
}
extension FormatStyle where Self == FrenchHebrewStyle {
static var frenchHebrew: FrenchHebrewStyle { .init() }
}
twosday.formatted(.frenchHebrew) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
import Foundation
// MARK: - Date.ISO8601FormatStyle
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
twosday.formatted(.iso8601) // "2022-02-22T09:22:22Z"
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
twosday.formatted(.iso8601.locale(franceLocale)) // "2022-02-22T09:22:22Z"
// MARK: - Customization Options
let isoFormat = Date.ISO8601FormatStyle(
dateSeparator: .dash,
dateTimeSeparator: .standard,
timeSeparator: .colon,
timeZoneSeparator: .colon,
includingFractionalSeconds: true,
timeZone: TimeZone(secondsFromGMT: 0)!
)
isoFormat.format(twosday) // "2022-02-22T09:22:22.000Z"
twosday.formatted(isoFormat) // "2022-02-22T09:22:22.000Z"
let isoStyle = Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
.year()
.day()
.month()
.dateSeparator(.dash)
.dateTimeSeparator(.standard)
.timeSeparator(.colon)
.timeZoneSeparator(.colon)
.time(includingFractionalSeconds: true)
.locale(Locale(identifier: "fr_FR"))
isoStyle.format(twosday) // "2022-02-22T09:22:22.000"
twosday.formatted(isoStyle) // "2022-02-22T09:22:22.000"
// MARK: - Parsing
let parsingStyle = Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
.year()
.day()
.month()
.dateSeparator(.dash)
.dateTimeSeparator(.standard)
.timeSeparator(.colon)
.timeZoneSeparator(.colon)
.time(includingFractionalSeconds: true)
try? parsingStyle.parseStrategy.parse("2022-02-22T09:22:22.000")
try? parsingStyle.parse("2022-02-22T09:22:22.000")
try? Date("2022-02-22T09:22:22.000", strategy: parsingStyle.parseStrategy)
import Foundation
let thePast = Calendar(identifier: .gregorian).date(byAdding: .day, value: -14, to: Date())!
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"
// MARK: - Set Locale
let franceLocale = Locale(identifier: "fr_FR")
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut).locale(franceLocale)) // "il y a deux semaines"
// 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"
//: [Previous](@previous)
import Foundation
var greeting = "Hello, playground"
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
twosday.formatted(
.verbatim(
"\(hour: .defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .defaultDigits):\(minute: .defaultDigits) \(dayPeriod: .standard(.wide))",
locale: Locale(identifier: "zh_CN"),
timeZone: .current,
calendar: .current
)
) // "2:22:22 上午"
import Foundation
// MARK: - Date.VerbatimFormatStyle
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
let verbatim = Date.VerbatimFormatStyle(
format: "\(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .twoDigits)",
timeZone: TimeZone.current,
calendar: .current
)
verbatim.format(twosday) // "02:22"
twosday.formatted(verbatim.attributed)
// Note: These are grouped due to the fact that outputting everything in one string causes the output to be an empty string
let groupOne = Date.FormatString("""
Cyclic Year
.abbreviated: \(cyclicYear: .abbreviated)
.narrow: \(cyclicYear: .narrow)
.wide: \(cyclicYear: .wide)
Day
.defaultDigits: \(day: .defaultDigits)
.julianModified: \(day: .julianModified())x
.julianModified(minimumLength: 2): \(day: .julianModified(minimumLength: 2))
.ordinalOfDayInMonth: \(day: .ordinalOfDayInMonth)
.twoDigits: \(day: .twoDigits)
Day of Year
.defaultDigits: \(dayOfYear: .defaultDigits)
.threeDigits: \(dayOfYear: .threeDigits)
.twoDigits: \(dayOfYear: .twoDigits)
Day Period
.conversational(.abbreviated)): \(dayPeriod: .conversational(.abbreviated))
.conversational(.narrow)): \(dayPeriod: .conversational(.narrow))
.conversational(.wide)): \(dayPeriod: .conversational(.wide))
.standard(.abbreviated)): \(dayPeriod: .standard(.abbreviated))
.standard(.narrow)): \(dayPeriod: .standard(.narrow))
.standard(.wide)): \(dayPeriod: .standard(.wide))
.with12s(.abbreviated)): \(dayPeriod: .with12s(.abbreviated))
.with12s(.narrow)): \(dayPeriod: .with12s(.narrow))
.with12s(.wide)): \(dayPeriod: .with12s(.wide))
Era
.abbreviated: \(era: .abbreviated)
.narrow: \(era: .narrow)
.wide: \(era: .wide)
Hour
.defaultDigits(clock: .twelveHour, hourCycle: .oneBased)): \(hour: .defaultDigits(clock: .twelveHour, hourCycle: .oneBased))
.defaultDigits(clock: .twelveHour, hourCycle: .zeroBased)): \(hour: .defaultDigits(clock: .twelveHour, hourCycle: .zeroBased))
.defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased)): \(hour: .defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased))
.defaultDigits(clock: .twentyFourHour, hourCycle: .zeroBased)): \(hour: .defaultDigits(clock: .twentyFourHour, hourCycle: .zeroBased))
Minute
.defaultDigits: \(minute: .defaultDigits)
.twoDigits: \(minute: .twoDigits)
Month
.abbreviated: \(month: .abbreviated)
.defaultDigits: \(month: .defaultDigits)
.narrow: \(month: .narrow)
.twoDigits: \(month: .twoDigits)
.wide: \(month: .wide)
""")
let groupTwo = Date.FormatString("""
Second
.defaultDigits: \(second: .defaultDigits)
.twoDigits: \(second: .twoDigits)
Second Fraction
.fractional(10)): \(secondFraction: .fractional(10))
.milliseconds(10)): \(secondFraction: .milliseconds(10))
Standalone Month
.abbreviated: \(standaloneMonth: .abbreviated)
.defaultDigits: \(standaloneMonth: .defaultDigits)
.narrow: \(standaloneMonth: .narrow)
.twoDigits: \(standaloneMonth: .twoDigits)
.wide: \(standaloneMonth: .wide)
""")
let groupThree = Date.FormatString("""
Standalone Quarter
.abbreviated: \(standaloneQuarter: .abbreviated)
.narrow: \(standaloneQuarter: .narrow)
.oneDigit: \(standaloneQuarter: .oneDigit)
.twoDigits: \(standaloneQuarter: .twoDigits)
.wide: \(standaloneQuarter: .wide)
Standalone Weekday
.abbreviated: \(standaloneWeekday: .abbreviated)
.narrow: \(standaloneWeekday: .narrow)
.oneDigit: \(standaloneWeekday: .oneDigit)
.short: \(standaloneWeekday: .short)
.wide: \(standaloneWeekday: .wide)
Time Zone
.exemplarLocation: \(timeZone: .exemplarLocation)
.genericLocation: \(timeZone: .genericLocation)
.identifier(.long)): \(timeZone: .identifier(.long))
.identifier(.short)): \(timeZone: .identifier(.short))
.iso8601(.long)): \(timeZone: .iso8601(.long))
.iso8601(.short)): \(timeZone: .iso8601(.short))
.localizedGMT(.long)): \(timeZone: .localizedGMT(.long))
.localizedGMT(.short)): \(timeZone: .localizedGMT(.short))
.specificName(.long)): \(timeZone: .specificName(.long))
.specificName(.short)): \(timeZone: .specificName(.short))
Week
.defaultDigits: \(week: .defaultDigits)
.twoDigits: \(week: .twoDigits)
.weekOfMonth: \(week: .weekOfMonth)
Weekday
.abbreviated: \(weekday: .abbreviated)
.narrow: \(weekday: .narrow)
.oneDigit: \(weekday: .oneDigit)
.short: \(weekday: .short)
.twoDigits: \(weekday: .twoDigits)
.wide: \(weekday: .wide)
Year
.defaultDigits: \(year: .defaultDigits)
.extended()): \(year: .extended())
.extended(minimumLength: 4)): \(year: .extended(minimumLength: 4))
.padded(10)): \(year: .padded(10))
.relatedGregorian()): \(year: .relatedGregorian())
.relatedGregorian(minimumLength: 4)): \(year: .relatedGregorian(minimumLength: 4))
.twoDigits: \(year: .twoDigits)
Year For Week of Year
.defaultDigits: \(yearForWeekOfYear: .defaultDigits)
.padded(4)): \(yearForWeekOfYear: .padded(4))
.twoDigits): \(yearForWeekOfYear: .twoDigits)
""")
let verbatimGroupOne = Date.VerbatimFormatStyle(
format: groupOne,
timeZone: TimeZone.current,
calendar: .current
)
let verbatimGroupTwo = Date.VerbatimFormatStyle(
format: groupTwo,
timeZone: TimeZone.current,
calendar: .current
)
let verbatimGroupThree = Date.VerbatimFormatStyle(
format: groupThree,
timeZone: TimeZone.current,
calendar: .current
)
print(verbatimGroupOne.format(twosday))
print(verbatimGroupTwo.format(twosday))
print(verbatimGroupThree.format(twosday))
/* OUTPUT
Cyclic Year
.abbreviated: 2022
.narrow: 02022
.wide: 2022
Day
.defaultDigits: 22
.julianModified: 2459633x
.julianModified(minimumLength: 2): 2459633
.ordinalOfDayInMonth: 4
.twoDigits: 22
Day of Year
.defaultDigits: 53
.threeDigits: 053
.twoDigits: 53
Day Period
.conversational(.abbreviated)): AM
.conversational(.narrow)): AM
.conversational(.wide)): AM
.standard(.abbreviated)): AM
.standard(.narrow)): AM
.standard(.wide)): AM
.with12s(.abbreviated)): AM
.with12s(.narrow)): AM
.with12s(.wide)): AM
Era
.abbreviated: CE
.narrow: CE
.wide: CE
Hour
.defaultDigits(clock: .twelveHour, hourCycle: .oneBased)): 2
.defaultDigits(clock: .twelveHour, hourCycle: .zeroBased)): 2
.defaultDigits(clock: .twentyFourHour, hourCycle: .oneBased)): 2
.defaultDigits(clock: .twentyFourHour, hourCycle: .zeroBased)): 2
Minute
.defaultDigits: 22
.twoDigits: 22
Month
.abbreviated: M02
.defaultDigits: 2
.narrow: 2
.twoDigits: 02
.wide: M02
Second
.defaultDigits: 22
.twoDigits: 22
Second Fraction
.fractional(10)): 000000000
.milliseconds(10)): 008542000
Standalone Month
.abbreviated: M02
.defaultDigits: 2
.narrow: 2
.twoDigits: 02
.wide: M02
Standalone Quarter
.abbreviated: Q1
.narrow: 1
.oneDigit: 1
.twoDigits: 01
.wide: Q1
Standalone Weekday
.abbreviated: Tue
.narrow: T
.oneDigit: 3
.short: Tue
.wide: Tue
Time Zone
.exemplarLocation: Edmonton
.genericLocation: Edmonton
.identifier(.long)): America/Edmonton
.identifier(.short)): caedm
.iso8601(.long)): -07:00
.iso8601(.short)): -0700
.localizedGMT(.long)): GMT-07:00
.localizedGMT(.short)): GMT-7
.specificName(.long)): GMT-07:00
.specificName(.short)): GMT-7
Week
.defaultDigits: 9
.twoDigits: 09
.weekOfMonth: 4
Weekday
.abbreviated: Tue
.narrow: T
.oneDigit: 3
.short: Tue
.twoDigits: 03
.wide: Tue
Year
.defaultDigits: 2022
.extended()): 2022
.extended(minimumLength: 4)): 2022
.padded(10)): 0000002022
.relatedGregorian()): 2022
.relatedGregorian(minimumLength: 4)): 2022
.twoDigits: 22
Year For Week of Year
.defaultDigits: 2022
.padded(4)): 2022
.twoDigits): 22
*/
import Foundation
// MARK: - Date.IntervalFormatStyle
let range = Date(timeIntervalSince1970: 0) ..< Date(timeIntervalSinceReferenceDate: 2837)
range.formatted(.interval) // "12/31/69, 5:00 PM – 12/31/00, 5:47 PM"
range.formatted(.interval.day()) // "12/31/1969 – 12/31/2000"
range.formatted(.interval.hour()) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
range.formatted(.interval.hour(.conversationalDefaultDigits(amPM: .abbreviated))) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
range.formatted(.interval.hour(.conversationalDefaultDigits(amPM: .narrow))) // "12/31/1969, 5 p – 12/31/2000, 5 p"
range.formatted(.interval.hour(.conversationalDefaultDigits(amPM: .omitted))) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
range.formatted(.interval.hour(.conversationalDefaultDigits(amPM: .wide))) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
range.formatted(.interval.minute()) // "12/31/1969, 0 – 12/31/2000, 47"
range.formatted(.interval.month(.defaultDigits)) // "12/1969 – 12/2000"
range.formatted(.interval.month(.twoDigits)) // "12/1969 – 12/2000"
range.formatted(.interval.month(.wide)) // "December 1969 – December 2000"
range.formatted(.interval.month(.narrow)) // "D 1969 – D 2000"
range.formatted(.interval.month(.abbreviated)) // "Dec 1969 – Dec 2000"
range.formatted(.interval.second()) // "12/31/1969, 0 – 12/31/2000, 17"
range.formatted(.interval.timeZone()) // "12/31/1969, MST – 12/31/2000, MT"
range.formatted(.interval.timeZone(.exemplarLocation)) // "12/31/1969, Edmonton – 12/31/2000, Edmonton"
range.formatted(.interval.timeZone(.genericLocation)) // "12/31/1969, Edmonton Time – 12/31/2000, Edmonton Time"
range.formatted(.interval.timeZone(.genericName(.long))) // "12/31/1969, Mountain Standard Time – 12/31/2000, Mountain Time"
range.formatted(.interval.timeZone(.genericName(.short))) // "12/31/1969, MST – 12/31/2000, MT"
range.formatted(.interval.timeZone(.identifier(.short))) // "12/31/1969, caedm – 12/31/2000, caedm"
range.formatted(.interval.timeZone(.identifier(.long))) // "12/31/1969, America/Edmonton – 12/31/2000, America/Edmonton"
range.formatted(.interval.timeZone(.iso8601(.short))) // "12/31/1969, -0700 – 12/31/2000, -0700"
range.formatted(.interval.timeZone(.iso8601(.long))) // "12/31/1969, -07:00 – 12/31/2000, -07:00"
range.formatted(.interval.timeZone(.localizedGMT(.short))) // "GMT-7"
range.formatted(.interval.timeZone(.localizedGMT(.long))) // "12/31/1969, GMT-07:00 – 12/31/2000, GMT-07:00"
range.formatted(.interval.timeZone(.specificName(.long))) // "12/31/1969, Mountain Standard Time – 12/31/2000, Mountain Standard Time"
range.formatted(.interval.timeZone(.specificName(.short))) // "12/31/1969, MST – 12/31/2000, MST"
range.formatted(.interval.year()) //"1969 – 2000"
Date.IntervalFormatStyle().day().format(range) // "12/31/1969 – 12/31/2000"
Date.IntervalFormatStyle().hour().format(range) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
Date.IntervalFormatStyle().hour(.conversationalDefaultDigits(amPM: .abbreviated)).format(range) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
Date.IntervalFormatStyle().hour(.conversationalDefaultDigits(amPM: .narrow)).format(range) // "12/31/1969, 5 p – 12/31/2000, 5 p"
Date.IntervalFormatStyle().hour(.conversationalDefaultDigits(amPM: .omitted)).format(range) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
Date.IntervalFormatStyle().hour(.conversationalDefaultDigits(amPM: .wide)).format(range) // "12/31/1969, 5 PM – 12/31/2000, 5 PM"
Date.IntervalFormatStyle().minute().format(range) // "12/31/1969, 0 – 12/31/2000, 47"
Date.IntervalFormatStyle().month(.defaultDigits).format(range) // "12/1969 – 12/2000"
Date.IntervalFormatStyle().month(.twoDigits).format(range) // "12/1969 – 12/2000"
Date.IntervalFormatStyle().month(.wide).format(range) // "December 1969 – December 2000"
Date.IntervalFormatStyle().month(.narrow).format(range) // "D 1969 – D 2000"
Date.IntervalFormatStyle().month(.abbreviated).format(range) // "Dec 1969 – Dec 2000"
Date.IntervalFormatStyle().second().format(range) // "12/31/1969, 0 – 12/31/2000, 17"
Date.IntervalFormatStyle().timeZone().format(range) // "12/31/1969, MST – 12/31/2000, MT"
Date.IntervalFormatStyle().timeZone(.exemplarLocation).format(range) // "12/31/1969, Edmonton – 12/31/2000, Edmonton"
Date.IntervalFormatStyle().timeZone(.genericLocation).format(range) // "12/31/1969, Edmonton Time – 12/31/2000, Edmonton Time"
Date.IntervalFormatStyle().timeZone(.genericName(.long)).format(range) // "12/31/1969, Mountain Standard Time – 12/31/2000, Mountain Time"
Date.IntervalFormatStyle().timeZone(.genericName(.short)).format(range) // "12/31/1969, MST – 12/31/2000, MT"
Date.IntervalFormatStyle().timeZone(.identifier(.short)).format(range) // "12/31/1969, caedm – 12/31/2000, caedm"
Date.IntervalFormatStyle().timeZone(.identifier(.long)).format(range) // "12/31/1969, America/Edmonton – 12/31/2000, America/Edmonton"
Date.IntervalFormatStyle().timeZone(.iso8601(.short)).format(range) // "12/31/1969, -0700 – 12/31/2000, -0700"
Date.IntervalFormatStyle().timeZone(.iso8601(.long)).format(range) // "12/31/1969, -07:00 – 12/31/2000, -07:00"
Date.IntervalFormatStyle().timeZone(.localizedGMT(.short)).format(range) // "GMT-7"
Date.IntervalFormatStyle().timeZone(.localizedGMT(.long)).format(range) // "12/31/1969, GMT-07:00 – 12/31/2000, GMT-07:00"
Date.IntervalFormatStyle().timeZone(.specificName(.long)).format(range) // "12/31/1969, Mountain Standard Time – 12/31/2000, Mountain Standard Time"
Date.IntervalFormatStyle().timeZone(.specificName(.short)).format(range) // "12/31/1969, MST – 12/31/2000, MST"
Date.IntervalFormatStyle().year().format(range) //"1969 – 2000"
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
range.formatted(.interval.locale(franceLocale)) // "31/12/1969 à 17:00 – 31/12/2000 à 17:47"
range.formatted(.interval.timeZone(.genericName(.long)).locale(franceLocale)) // "31/12/1969 à heure normale des Rocheuses – 31/12/2000 à heure des Rocheuses"
Date.IntervalFormatStyle().locale(franceLocale).format(range) // "31/12/1969 à 17:00 – 31/12/2000 à 17:47"
Date.IntervalFormatStyle().timeZone(.genericName(.long)).locale(franceLocale).format(range) // "31/12/1969 à heure normale des Rocheuses – 31/12/2000 à heure des Rocheuses"
// MARK: - Initializing
let interval = Date.IntervalFormatStyle(
date: .abbreviated,
time: .shortened,
locale: Locale(identifier: "en_US"),
calendar: Calendar(identifier: .gregorian),
timeZone: TimeZone(secondsFromGMT: 0)!
)
interval.format(range) // "Jan 1, 1970, 12:00 AM – Jan 1, 2001, 12:47 AM"
range.formatted(interval) // "Jan 1, 1970, 12:00 AM – Jan 1, 2001, 12:47 AM"
import Foundation
// MARK: - Date.ComponentsFormatStyle
let testRange = Date(timeIntervalSince1970: 0) ..< Date(timeIntervalSinceReferenceDate: 0)
testRange.formatted(.components(style: .abbreviated, fields: [.day])) // "11,323 days"
testRange.formatted(.components(style: .narrow, fields: [.day])) // "11,323days"
testRange.formatted(.components(style: .wide, fields: [.day])) // "11,323 days"
testRange.formatted(.components(style: .spellOut, fields: [.day])) // "eleven thousand three hundred twenty-three days"
testRange.formatted(.components(style: .condensedAbbreviated, fields: [.day])) // "11,323d"
testRange.formatted(.components(style: .condensedAbbreviated, fields: [.day, .month, .year, .hour, .second, .week])) // "31y"
testRange.formatted(.components(style: .abbreviated, fields: [.year])
.calendar(Calendar(identifier: .coptic))) // "31 yrs"
let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
let appleReferenceDay = Date(timeIntervalSinceReferenceDate: 0)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!
let secondRange = appleReferenceDay ..< twosday
// "21 yrs, 1 mth, 3 wks, 9 hr, 1,342 sec"
secondRange.formatted(.components(style: .abbreviated, fields: [.day, .month, .year, .hour, .second, .week]))
// "21yrs 1mth 3wks 9hr 1,342sec"
secondRange.formatted(.components(style: .narrow, fields: [.day, .month, .year, .hour, .second, .week]))
// "21 years, 1 month, 3 weeks, 9 hours, 1,342 seconds"
secondRange.formatted(.components(style: .wide, fields: [.day, .month, .year, .hour, .second, .week]))
// "twenty-one years, one month, three weeks, nine hours, one thousand three hundred forty-two seconds"
secondRange.formatted(.components(style: .spellOut, fields: [.day, .month, .year, .hour, .second, .week]))
// "21y 1mo 3w 9h 1,342s"
secondRange.formatted(.components(style: .condensedAbbreviated, fields: [.day, .month, .year, .hour, .second, .week]))
// MARK: - Setting Locale
let franceLocale = Locale(identifier: "fr_FR")
// "vingt-et-un ans, un mois, trois semaines, neuf heures et mille trois cent quarante-deux secondes"
secondRange.formatted(.components(style: .spellOut, fields: [.day, .month, .year, .hour, .second, .week]).locale(franceLocale))
// MARK: - Custom Date.ComponentsFormatStyle
let componentsFormat = Date.ComponentsFormatStyle(
style: .wide,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .gregorian),
fields: [
.day,
.month,
.year,
.hour,
.second,
.week,
]
)
componentsFormat.format(secondRange) // "21 ans, 1 mois, 3 semaines, 9 heures et 1 342 secondes"
secondRange.formatted(componentsFormat) // "21 ans, 1 mois, 3 semaines, 9 heures et 1 342 secondes"
struct InFrench: FormatStyle {
typealias FormatInput = Range<Date>
typealias FormatOutput = String
static let componentsFormat = Date.ComponentsFormatStyle(
style: .wide,
locale: Locale(identifier: "fr_FR"),
calendar: Calendar(identifier: .gregorian),
fields: [
.day,
.month,
.year,
.hour,
.second,
.week,
]
)
func format(_ value: Range<Date>) -> String {
InFrench.componentsFormat.format(value)
}
}
extension FormatStyle where Self == InFrench {
static var inFrench: InFrench { .init() }
}
secondRange.formatted(.inFrench) // "21 ans, 1 mois, 3 semaines, 9 heures et 1 342 secondes"
import Foundation
let speedLimit = Measurement(value: 100, unit: UnitSpeed.kilometersPerHour)
let drivingDistance = Measurement(value: 200, unit: UnitLength.kilometers)
let averageBaseballThrow = Measurement(value: 70, unit: UnitLength.feet)
let bodyTemperature = Measurement(value: 98.5, unit: UnitTemperature.fahrenheit)
// MARK: FormatStyle String Output
// .formatted uses the current locale for all string output by default.
// This means that different devices will output different values depending
// on the Locale value set on.
speedLimit.formatted() // "62 mph"
drivingDistance.formatted() // "124 mi"
averageBaseballThrow.formatted() // "70 ft"
bodyTemperature.formatted() // "98°F"
// MARK: Width Property
speedLimit.formatted(.measurement(width: .wide)) // "62 miles per hour"
speedLimit.formatted(.measurement(width: .abbreviated)) // "62 mph"
speedLimit.formatted(.measurement(width: .narrow)) // "62mph"
drivingDistance.formatted(.measurement(width: .wide)) // "124 miles"
drivingDistance.formatted(.measurement(width: .abbreviated)) // "124 mi"
drivingDistance.formatted(.measurement(width: .narrow)) // "124mi"
averageBaseballThrow.formatted(.measurement(width: .wide)) // "70 feet"
averageBaseballThrow.formatted(.measurement(width: .abbreviated)) // "70 ft"
averageBaseballThrow.formatted(.measurement(width: .narrow)) // "70'"
bodyTemperature.formatted(.measurement(width: .wide)) // "98 degrees Fahrenheit"
bodyTemperature.formatted(.measurement(width: .abbreviated)) // "98°F"
bodyTemperature.formatted(.measurement(width: .narrow)) // "98°"
// MARK: - Usage
let usa = Locale(identifier: "en-US")
let canada = Locale(identifier: "en-CA")
let sweden = Locale(identifier: "sv-SE")
// MARK: Custom for UnitEnergy
let recommendedCalories = Measurement(value: 2.0, unit: UnitEnergy.kilowattHours)
recommendedCalories.formatted(.measurement(width: .wide, usage: .general).locale(usa)) // "2 kilowatt-hours"
recommendedCalories.formatted(.measurement(width: .wide, usage: .asProvided).locale(usa)) // "2 kilowatt-hours"
recommendedCalories.formatted(.measurement(width: .wide, usage: .food).locale(usa)) // "1,721 Calories"
recommendedCalories.formatted(.measurement(width: .wide, usage: .workout).locale(usa)) // "1,721 Calories"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .general).locale(usa)) // "2 kWh"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(usa)) // "2 kWh"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .food).locale(usa)) // "1,721 Cal"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .workout).locale(usa)) // "1,721 Cal"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .general).locale(usa)) // "2kWh"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .asProvided).locale(usa)) // "2kWh"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .food).locale(usa)) // "1,721Cal"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .workout).locale(usa)) // "1,721Cal"
recommendedCalories.formatted(.measurement(width: .wide, usage: .general).locale(canada)) // "2 kilowatt-hours"
recommendedCalories.formatted(.measurement(width: .wide, usage: .asProvided).locale(canada)) // "2 kilowatt-hours"
recommendedCalories.formatted(.measurement(width: .wide, usage: .food).locale(canada)) // "1,721 Calories"
recommendedCalories.formatted(.measurement(width: .wide, usage: .workout).locale(canada)) // "1,721 Calories"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .general).locale(canada)) // "2 kWh"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(canada)) // "2 kWh"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .food).locale(canada)) // "1,721 Cal"
recommendedCalories.formatted(.measurement(width: .abbreviated, usage: .workout).locale(canada)) // "1,721 Cal"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .general).locale(canada)) // "2kWh"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .asProvided).locale(canada)) // "2kWh"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .food).locale(canada)) // "1,721Cal"
recommendedCalories.formatted(.measurement(width: .narrow, usage: .workout).locale(canada)) // "1,721Cal"
// MARK: Custom for UnitLength
let myHeight = Measurement(value: 190, unit: UnitLength.centimeters)
myHeight.formatted(.measurement(width: .wide, usage: .general).locale(usa)) // "6.2 feet"
myHeight.formatted(.measurement(width: .wide, usage: .asProvided).locale(usa)) // "190 centimeters"
myHeight.formatted(.measurement(width: .wide, usage: .focalLength).locale(usa)) // "1,900 millimeters"
myHeight.formatted(.measurement(width: .wide, usage: .person).locale(usa)) // "75 inches"
myHeight.formatted(.measurement(width: .wide, usage: .snowfall).locale(usa)) // "75 inches"
myHeight.formatted(.measurement(width: .wide, usage: .road).locale(usa)) // "6 feet"
myHeight.formatted(.measurement(width: .wide, usage: .rainfall).locale(usa)) // "75 inches"
myHeight.formatted(.measurement(width: .wide, usage: .personHeight).locale(usa)) // "6 feet, 2.8 inches"
myHeight.formatted(.measurement(width: .abbreviated, usage: .general).locale(usa)) // "6.2 ft"
myHeight.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(usa)) // "190 cm"
myHeight.formatted(.measurement(width: .abbreviated, usage: .focalLength).locale(usa)) // "1,900 mm"
myHeight.formatted(.measurement(width: .abbreviated, usage: .person).locale(usa)) // "75 in"
myHeight.formatted(.measurement(width: .abbreviated, usage: .snowfall).locale(usa)) // "75 in"
myHeight.formatted(.measurement(width: .abbreviated, usage: .road).locale(usa)) // "6 ft"
myHeight.formatted(.measurement(width: .abbreviated, usage: .rainfall).locale(usa)) // "75 in"
myHeight.formatted(.measurement(width: .abbreviated, usage: .personHeight).locale(usa)) // "6 ft, 2.8 in"
myHeight.formatted(.measurement(width: .narrow, usage: .general).locale(usa)) // "6.2'"
myHeight.formatted(.measurement(width: .narrow, usage: .asProvided).locale(usa)) // "190cm"
myHeight.formatted(.measurement(width: .narrow, usage: .focalLength).locale(usa)) // "1,900mm"
myHeight.formatted(.measurement(width: .narrow, usage: .person).locale(usa)) // "75""
myHeight.formatted(.measurement(width: .narrow, usage: .snowfall).locale(usa)) // "75""
myHeight.formatted(.measurement(width: .narrow, usage: .road).locale(usa)) // "6'"
myHeight.formatted(.measurement(width: .narrow, usage: .rainfall).locale(usa)) // "75""
myHeight.formatted(.measurement(width: .narrow, usage: .personHeight).locale(usa)) // "6'2.8""
myHeight.formatted(.measurement(width: .wide, usage: .general).locale(canada)) // "1.9 metres"
myHeight.formatted(.measurement(width: .wide, usage: .asProvided).locale(canada)) // "190 centimetres"
myHeight.formatted(.measurement(width: .wide, usage: .focalLength).locale(canada)) // "1,900 millimetres"
myHeight.formatted(.measurement(width: .wide, usage: .person).locale(canada)) // "75 inches"
myHeight.formatted(.measurement(width: .wide, usage: .snowfall).locale(canada)) // "190 centimetres"
myHeight.formatted(.measurement(width: .wide, usage: .road).locale(canada)) // "2 metres"
myHeight.formatted(.measurement(width: .wide, usage: .rainfall).locale(canada)) // "1,900 millimetres"
myHeight.formatted(.measurement(width: .wide, usage: .personHeight).locale(canada)) // "6 feet, 2.8 inches"
myHeight.formatted(.measurement(width: .abbreviated, usage: .general).locale(canada)) // "1.9m"
myHeight.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(canada)) // "190 cm"
myHeight.formatted(.measurement(width: .abbreviated, usage: .focalLength).locale(canada)) // "1,900 mm"
myHeight.formatted(.measurement(width: .abbreviated, usage: .person).locale(canada)) // "75 in"
myHeight.formatted(.measurement(width: .abbreviated, usage: .snowfall).locale(canada)) // "190 in"
myHeight.formatted(.measurement(width: .abbreviated, usage: .road).locale(canada)) // "2 m"
myHeight.formatted(.measurement(width: .abbreviated, usage: .rainfall).locale(canada)) // "1,900 mm"
myHeight.formatted(.measurement(width: .abbreviated, usage: .personHeight).locale(canada)) // "6 ft, 2.8 in"
myHeight.formatted(.measurement(width: .narrow, usage: .general).locale(canada)) // "1.9m"
myHeight.formatted(.measurement(width: .narrow, usage: .asProvided).locale(canada)) // "190cm"
myHeight.formatted(.measurement(width: .narrow, usage: .focalLength).locale(canada)) // "1,900mm"
myHeight.formatted(.measurement(width: .narrow, usage: .person).locale(canada)) // "75""
myHeight.formatted(.measurement(width: .narrow, usage: .snowfall).locale(canada)) // "190cm"
myHeight.formatted(.measurement(width: .narrow, usage: .road).locale(canada)) // "2m"
myHeight.formatted(.measurement(width: .narrow, usage: .rainfall).locale(canada)) // "1,900mm"
myHeight.formatted(.measurement(width: .narrow, usage: .personHeight).locale(canada)) // "6'2.8""
// MARK: Custom for UnitMass
let averageWeight = Measurement(value: 197.9, unit: UnitMass.pounds)
averageWeight.formatted(.measurement(width: .wide, usage: .general).locale(usa)) // "198 pounds"
averageWeight.formatted(.measurement(width: .wide, usage: .asProvided).locale(usa)) // "197.9 pounds"
averageWeight.formatted(.measurement(width: .wide, usage: .personWeight).locale(usa)) // "198 pounds"
averageWeight.formatted(.measurement(width: .abbreviated, usage: .general).locale(usa)) // "198 lb"
averageWeight.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(usa)) // "197.9 lb"
averageWeight.formatted(.measurement(width: .abbreviated, usage: .personWeight).locale(usa)) // "198 lb"
averageWeight.formatted(.measurement(width: .narrow, usage: .general).locale(usa)) // "198#"
averageWeight.formatted(.measurement(width: .narrow, usage: .asProvided).locale(usa)) // "197.9#"
averageWeight.formatted(.measurement(width: .narrow, usage: .personWeight).locale(usa)) // "198#"
averageWeight.formatted(.measurement(width: .wide, usage: .general).locale(canada)) // "90 kilograms"
averageWeight.formatted(.measurement(width: .wide, usage: .asProvided).locale(canada)) // "197.9 pounds"
averageWeight.formatted(.measurement(width: .wide, usage: .personWeight).locale(canada)) // "90 kilograms"
averageWeight.formatted(.measurement(width: .abbreviated, usage: .general).locale(canada)) // "90 kg"
averageWeight.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(canada)) // "197.9 lb"
averageWeight.formatted(.measurement(width: .abbreviated, usage: .personWeight).locale(canada)) // "90 kg"
averageWeight.formatted(.measurement(width: .narrow, usage: .general).locale(canada)) // "90kg"
averageWeight.formatted(.measurement(width: .narrow, usage: .asProvided).locale(canada)) // "197.9lb"
averageWeight.formatted(.measurement(width: .narrow, usage: .personWeight).locale(canada)) // "90kg"
// MARK: Custom for UnitTemperature
let aNiceDay = Measurement(value: 25.0, unit: UnitTemperature.celsius)
aNiceDay.formatted(.measurement(width: .wide, usage: .general).locale(usa)) // "77 degrees Fahrenheit"
aNiceDay.formatted(.measurement(width: .wide, usage: .asProvided).locale(usa)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .wide, usage: .person).locale(usa)) // "77 degrees Fahrenheit"
aNiceDay.formatted(.measurement(width: .wide, usage: .weather).locale(usa)) // "77 degrees Fahrenheit"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .general).locale(usa)) // "77°F"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(usa)) // "25°C"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .person).locale(usa)) // "77°F"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .weather).locale(usa)) // "77°F"
aNiceDay.formatted(.measurement(width: .narrow, usage: .general).locale(usa)) // "77°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .asProvided).locale(usa)) // "25°C"
aNiceDay.formatted(.measurement(width: .narrow, usage: .person).locale(usa)) // "77°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .weather).locale(usa)) // "77°"
aNiceDay.formatted(.measurement(width: .wide, usage: .general, hidesScaleName: true).locale(usa)) // "77 degrees Fahrenheit"
aNiceDay.formatted(.measurement(width: .wide, usage: .asProvided, hidesScaleName: true).locale(usa)) // "25 degrees"
aNiceDay .formatted(.measurement(width: .wide, usage: .person, hidesScaleName: true).locale(usa)) // "77 degrees Fahrenheit"
aNiceDay .formatted(.measurement(width: .wide, usage: .weather, hidesScaleName: true).locale(usa)) // "77 degrees Fahrenheit"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .general, hidesScaleName: true).locale(usa)) // "77°F"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .asProvided, hidesScaleName: true).locale(usa)) // "25°"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .person, hidesScaleName: true).locale(usa)) // "77°F"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .weather, hidesScaleName: true).locale(usa)) // "77°F"
aNiceDay.formatted(.measurement(width: .narrow, usage: .general, hidesScaleName: true).locale(usa)) // "77°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .asProvided, hidesScaleName: true).locale(usa)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .person, hidesScaleName: true).locale(usa)) // "77°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .weather, hidesScaleName: true).locale(usa)) // "77°"
aNiceDay.formatted(.measurement(width: .wide, usage: .general).locale(canada)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .wide, usage: .asProvided).locale(canada)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .wide, usage: .person).locale(canada)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .wide, usage: .weather).locale(canada)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .general).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .person).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .weather).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .narrow, usage: .general).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .asProvided).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .person).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .weather).locale(canada)) // "25°"
aNiceDay .formatted(.measurement(width: .wide, usage: .general, hidesScaleName: true).locale(canada)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .wide, usage: .asProvided, hidesScaleName: true).locale(canada)) // "25 degrees"
aNiceDay .formatted(.measurement(width: .wide, usage: .person, hidesScaleName: true).locale(canada)) // "25 degrees Celsius"
aNiceDay .formatted(.measurement(width: .wide, usage: .weather, hidesScaleName: true).locale(canada)) // "25 degrees Celsius"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .general, hidesScaleName: true).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .asProvided, hidesScaleName: true).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .person, hidesScaleName: true).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .abbreviated, usage: .weather, hidesScaleName: true).locale(canada)) // "25°C"
aNiceDay.formatted(.measurement(width: .narrow, usage: .general, hidesScaleName: true).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .asProvided, hidesScaleName: true).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .person, hidesScaleName: true).locale(canada)) // "25°"
aNiceDay.formatted(.measurement(width: .narrow, usage: .weather, hidesScaleName: true).locale(canada)) // "25°"
// MARK: Custom for UnitVolume
let onePint = Measurement(value: 1, unit: UnitVolume.pints)
onePint.formatted(.measurement(width: .wide, usage: .general).locale(usa)) // "31 cubic inches"
onePint.formatted(.measurement(width: .wide, usage: .asProvided).locale(usa)) // "1 metric pint"
onePint.formatted(.measurement(width: .wide, usage: .liquid).locale(usa)) // "1.1 pints"
onePint.formatted(.measurement(width: .abbreviated, usage: .general).locale(usa)) // "31 in³"
onePint.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(usa)) // "1 mpt"
onePint.formatted(.measurement(width: .abbreviated, usage: .liquid).locale(usa)) // "1.1 pt"
onePint.formatted(.measurement(width: .narrow, usage: .general).locale(usa)) // "31in³"
onePint.formatted(.measurement(width: .narrow, usage: .asProvided).locale(usa)) // "1mpt"
onePint.formatted(.measurement(width: .narrow, usage: .liquid).locale(usa)) // "1.1pt"
onePint.formatted(.measurement(width: .wide, usage: .general).locale(canada)) // "500 cubic centimetres"
onePint.formatted(.measurement(width: .wide, usage: .asProvided).locale(canada)) // "1 metric pint"
onePint.formatted(.measurement(width: .wide, usage: .liquid).locale(canada)) // "500 millilitres"
onePint.formatted(.measurement(width: .abbreviated, usage: .general).locale(canada)) // "500/cu cm"
onePint.formatted(.measurement(width: .abbreviated, usage: .asProvided).locale(canada)) // "1 mpt"
onePint.formatted(.measurement(width: .abbreviated, usage: .liquid).locale(canada)) // "500 mL"
onePint.formatted(.measurement(width: .narrow, usage: .general).locale(canada)) // "500cm³"
onePint.formatted(.measurement(width: .narrow, usage: .asProvided).locale(canada)) // "1mpt"
onePint.formatted(.measurement(width: .narrow, usage: .liquid).locale(canada)) // "500mL"
// MARK: - NumberFormatStyle
// See Number Style for more options.
// "6 feet, 3 inches"
myHeight.formatted(
.measurement(
width: .wide,
usage: .personHeight,
numberFormatStyle: .number.precision(.fractionLength(0))
)
.locale(usa)
)
// "6 ft, 3 in"
myHeight.formatted(
.measurement(
width: .abbreviated,
usage: .personHeight,
numberFormatStyle: .number.precision(.fractionLength(0))
)
.locale(usa)
)
// "6′ 3″"
myHeight.formatted(
.measurement(
width: .narrow,
usage: .personHeight,
numberFormatStyle: .number.precision(.fractionLength(0))
)
.locale(usa)
)
import Foundation
let letters = ["a", "b", "c", "d"]
// MARK: - ListFormatStyle
letters.formatted() // "a, b, c, and d"
letters.formatted(.list(type: .and)) // "a, b, c, and d"
letters.formatted(.list(type: .or)) // "a, b, c, or d"
letters.formatted(.list(type: .and, width: .narrow)) // "a, b, c, d"
letters.formatted(.list(type: .and, width: .standard)) // "a, b, c, and d"
letters.formatted(.list(type: .and, width: .short)) // "a, b, c, & d"
letters.formatted(.list(type: .or, width: .narrow)) // "a, b, c, or d"
letters.formatted(.list(type: .or, width: .standard)) // "a, b, c, or d"
letters.formatted(.list(type: .or, width: .short)) // "a, b, c, or d"
// MARK: Set Locale
let franceLocale = Locale(identifier: "fr_FR")
letters.formatted(.list(type: .and).locale(franceLocale)) // "a, b, c, et d"
letters.formatted(.list(type: .or).locale(franceLocale)) // "a, b, c, ou d"
letters.formatted(.list(type: .and, width: .narrow).locale(franceLocale)) // "a, b, c, d"
letters.formatted(.list(type: .and, width: .standard).locale(franceLocale)) // "a, b, c, et d"
letters.formatted(.list(type: .and, width: .short).locale(franceLocale)) // "a, b, c, et d"
letters.formatted(.list(type: .or, width: .narrow).locale(franceLocale)) // "a, b, c, ou d"
letters.formatted(.list(type: .or, width: .standard).locale(franceLocale)) // "a, b, c, ou d"
letters.formatted(.list(type: .or, width: .short).locale(franceLocale)) // "a, b, c, ou d"
// MARK: Customizing Item Display
let importantDates = [
Date(timeIntervalSinceReferenceDate: 0),
Date(timeIntervalSince1970: 0)
]
let yearOnlyFormat = Date.FormatStyle.dateTime.year()
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and)) // "2000 and 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or)) // "2000 or 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and, width: .standard)) // "2000 and 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and, width: .narrow)) // "2000, 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .and, width: .short)) // "2000 & 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or, width: .standard)) // "2000 or 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or, width: .narrow)) // "2000 or 1969"
importantDates.formatted(.list(memberStyle: yearOnlyFormat, type: .or, width: .short)) // "2000 or 1969"
let yearStyle = ListFormatStyle<Date.FormatStyle, [Date]>.init(memberStyle: .dateTime.year())
importantDates.formatted(yearStyle) // "2000 and 1969"
import Foundation
let guest = PersonNameComponents(
namePrefix: "Dr",
givenName: "Elizabeth",
middleName: "Jillian",
familyName: "Smith",
nameSuffix: "Esq.",
nickname: "Liza"
)
guest.formatted() // "Elizabeth Smith"
guest.formatted(.name(style: .abbreviated)) // "ES"
guest.formatted(.name(style: .short)) // "Liza"
guest.formatted(.name(style: .medium)) // "Elizabeth Smith"
guest.formatted(.name(style: .long)) // "Dr Elizabeth Jillian Smith Esq."
let chinaLocale = Locale(identifier: "zh_CN")
guest.formatted(.name(style: .abbreviated).locale(chinaLocale)) // "SE"
guest.formatted(.name(style: .short).locale(chinaLocale)) // "Liza"
guest.formatted(.name(style: .medium).locale(chinaLocale)) // "Smith Elizabeth"
guest.formatted(.name(style: .long).locale(chinaLocale)) // "Dr Smith Elizabeth Jillian Esq."
// Parsing
let parsingStyle = PersonNameComponents.FormatStyle(style: .long)
// namePrefix: Dr givenName: Elizabeth middleName: Jillian familyName: Smith nameSuffix: Esq.
try? parsingStyle.parseStrategy.parse("Dr Elizabeth Jillian Smith Esq.")
try? PersonNameComponents("Dr Elizabeth Jillian Smith Esq.", strategy: parsingStyle.parseStrategy)
try? PersonNameComponents("Dr Elizabeth Jillian Smith Esq.")
import Foundation
let terabyte: Int64 = 1_000_000_000_000
let formatter = ByteCountFormatter()
formatter.countStyle = .binary
formatter.allowedUnits
formatter.includesActualByteCount
formatter.countStyle = .file
terabyte.formatted(.byteCount(style: .binary)) // "931.32 GB"
terabyte.formatted(.byteCount(style: .decimal)) // "1 TB"
terabyte.formatted(.byteCount(style: .file)) // "1 TB"
terabyte.formatted(.byteCount(style: .memory)) // "931.32 GB"
terabyte.formatted(.byteCount(style: .memory, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyte.formatted(.byteCount(style: .memory, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyte.formatted(.byteCount(style: .memory, allowedUnits: .kb)) // "1,000,000,000 kB"
terabyte.formatted(.byteCount(style: .memory, allowedUnits: .mb)) // "1,000,000 MB"
// .gb, .tb, .pb, .eb, .zb, and .ybOrHigher cause a FatalError (Feedback FB10031442)
// FIXED IN iOS 16. See other iOS 16 files for examples.
// terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb))
Int64(0).formatted(.byteCount(style: .file, allowedUnits: .mb, spellsOutZero: true)) // "Zero bytes"
Int64(0).formatted(.byteCount(style: .file, allowedUnits: .mb, spellsOutZero: false)) // "0 MB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb, includesActualByteCount: true)) // "1,000,000 MB (1,000,000,000,000 bytes)"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb, includesActualByteCount: false)) // "1,000,000 MB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .all, spellsOutZero: true, includesActualByteCount: true)) // "1 TB (1,000,000,000,000 bytes)"
// Customizing
let franceLocale = Locale(identifier: "fr_FR")
terabyte.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go"
terabyte.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To"
terabyte.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To"
terabyte.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go"
let inFrench = ByteCountFormatStyle(
style: .memory,
allowedUnits: .all,
spellsOutZero: false,
includesActualByteCount: true,
locale: Locale(identifier: "fr_FR")
)
inFrench.format(terabyte) // "931,32 Go (1 000 000 000 000 octets)"
terabyte.formatted(inFrench) // "931,32 Go (1 000 000 000 000 octets)"
// MARK: - AttributedString Output
terabyte.formatted(.byteCount(style: .binary).attributed)
import Foundation
// MARK: - Int64
let terabyte: Int64 = 1_000_000_000_000
var integerByteCountStyle = ByteCountFormatStyle()
integerByteCountStyle.style = .file
integerByteCountStyle.allowedUnits = [.gb, .tb]
integerByteCountStyle.includesActualByteCount = true
integerByteCountStyle.format(terabyte) // "1 TB (1,000,000,000,000 bytes)"
terabyte.formatted(integerByteCountStyle) // "1 TB (1,000,000,000,000 bytes)"
terabyte.formatted(.byteCount(style: .binary)) // "931.32 GB"
terabyte.formatted(.byteCount(style: .decimal)) // "1 TB"
terabyte.formatted(.byteCount(style: .file)) // "1 TB"
terabyte.formatted(.byteCount(style: .memory)) // "931.32 GB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "976,562,500 kB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "953,674.3 MB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .gb)) // "931.32 GB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .tb)) // "0.91 TB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .pb)) // "0 PB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .zb)) // "0 PB"
terabyte.formatted(.byteCount(style: .file, allowedUnits: .ybOrHigher)) // 0 PB
Int64(1_000).formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 kB"
Int64(1_000_000).formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 MB"
Int64.zero.formatted(.byteCount(style: .file, spellsOutZero: true)) // "Zero kB"
Int64.zero.formatted(.byteCount(style: .file, spellsOutZero: false)) // "0 bytes"
Int64(1_000).formatted(.byteCount(style: .file, includesActualByteCount: true)) // "1 kB (1,000 bytes)"
let franceLocale = Locale(identifier: "fr_FR")
terabyte.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go"
terabyte.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To"
terabyte.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To"
terabyte.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go"
terabyte.formatted(.byteCount(style: .binary).attributed)
// MARK: - Measurement
let terabyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.terabytes)
terabyteMeasurement.formatted(.byteCount(style: .binary)) // "931.32 GB"
terabyteMeasurement.formatted(.byteCount(style: .decimal)) // "1 TB"
terabyteMeasurement.formatted(.byteCount(style: .file)) // "1 TB"
terabyteMeasurement.formatted(.byteCount(style: .memory)) // "931.32 GB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .bytes)) // "1,000,000,000,000 bytes"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .kb)) // "976,562,500 kB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .mb)) // "953,674.3 MB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .gb)) // "931.32 GB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .tb)) // "0.91 TB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .pb)) // "0 PB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .zb)) // "0 PB"
terabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: .ybOrHigher)) // 0 PB
let kilobyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.kilobytes)
let megabyteMeasurement = Measurement(value: 1, unit: UnitInformationStorage.megabytes)
kilobyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 kB"
megabyteMeasurement.formatted(.byteCount(style: .file, allowedUnits: [.kb, .mb])) // "1 MB"
let zeroMeasurement = Measurement(value: 0, unit: UnitInformationStorage.bytes)
zeroMeasurement.formatted(.byteCount(style: .file, spellsOutZero: true)) // "Zero kB"
zeroMeasurement.formatted(.byteCount(style: .file, spellsOutZero: false)) // "0 bytes"
megabyteMeasurement.formatted(.byteCount(style: .file, includesActualByteCount: true)) // "1 MB (1,000,000 bytes)"
terabyteMeasurement.formatted(.byteCount(style: .binary).locale(franceLocale)) // "931,32 Go"
terabyteMeasurement.formatted(.byteCount(style: .decimal).locale(franceLocale)) // "1To"
terabyteMeasurement.formatted(.byteCount(style: .file).locale(franceLocale)) // "1To"
terabyteMeasurement.formatted(.byteCount(style: .memory).locale(franceLocale)) // "931,32 Go"
terabyteMeasurement.formatted(.byteCount(style: .binary).attributed)
import Foundation
// MARK: - Customizing Existing FormatStyles
struct ToYen: FormatStyle {
typealias FormatInput = Decimal
typealias FormatOutput = String
static let multiplier = 100.0
static let yenCurrencyCode = "jpy"
let formatter: Decimal.FormatStyle.Currency
var attributed: ToYen.AttributedStyle = AttributedStyle()
init(locale: Locale? = nil) {
formatter = Decimal.FormatStyle.Currency(code: Self.yenCurrencyCode)
.scale(Self.multiplier)
.locale(locale ?? .current)
}
func format(_ value: Decimal) -> String {
formatter.format(value)
}
// This is an optional protocol method, but needed to support locale switching
func locale(_ locale: Locale) -> ToYen {
.init(locale: locale)
}
}
// MARK: Add Attributed Style support
extension ToYen {
struct AttributedStyle: FormatStyle {
typealias FormatInput = Decimal
typealias FormatOutput = AttributedString
func format(_ value: Decimal) -> AttributedString {
ToYen().formatter.attributed.format(value)
}
}
}
// MARK: Extend `FormatStyle` to simplify access
extension FormatStyle where Self == ToYen {
static var toYen: ToYen { .init() }
}
// MARK: - Usage Examples
Decimal(30.0)
Decimal(30).formatted(ToYen()) // "¥3,000"
Decimal(30).formatted(.toYen) // "¥3,000")
Decimal(30).formatted(.toYen.locale(Locale(identifier: "zh_CN"))) // "JP¥3,000"
Decimal(30).formatted(.toYen.attributed)
// MARK: - Creating a FormatStyle for a custom data type
struct ISBN {
let prefix: String
let registrationGroup: String
let registrant: String
let publication: String
let checkDigit: String
}
extension ISBN {
func formatted() -> String {
ISBN.FormatStyle().format(self)
}
func formatted<F: Foundation.FormatStyle>(_ format: F) -> F.FormatOutput where F.FormatInput == ISBN {
format.format(self)
}
}
extension ISBN {
struct FormatStyle: Codable, Sendable, Hashable {
enum DelimiterStrategy: Codable {
case hyphen
case none
}
let strategy: DelimiterStrategy
init(delimiter strategy: DelimiterStrategy = .hyphen) {
self.strategy = strategy
}
}
}
// MARK: - ISBN.FormatStyle instance methods
extension ISBN.FormatStyle {
func delimiter(_ strategy: DelimiterStrategy = .hyphen) -> Self {
.init(delimiter: strategy)
}
}
// MARK: - ISBN.FormatStyle `FormatStyle` conforma1tion
extension ISBN.FormatStyle: FormatStyle {
func format(_ value: ISBN) -> String {
switch strategy {
case .hyphen:
return "\(value.prefix)-\(value.registrationGroup)-\(value.registrant)-\(value.publication)-\(value.checkDigit)"
case .none:
return "\(value.prefix)\(value.registrationGroup)\(value.registrant)\(value.publication)\(value.checkDigit)"
}
}
}
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
extension FormatStyle where Self == ISBN.FormatStyle {
static var isbn: Self { .init() }
}
let isbn = ISBN(prefix: "978", registrationGroup: "17", registrant: "85889", publication: "01", checkDigit: "1")
isbn.formatted() // "978-17-85889-01-1"
isbn.formatted(.isbn) // "978-17-85889-01-1"
isbn.formatted(.isbn.delimiter(.none)) // "9781785889011"
isbn.formatted(ISBN.FormatStyle()) // "978-17-85889-01-1"
isbn.formatted(ISBN.FormatStyle(delimiter: .none)) // "9781785889011"
import PlaygroundSupport
import SwiftUI
struct ContentView: View {
static let twosdayDateComponents = DateComponents(
year: 2022,
month: 2,
day: 22,
hour: 2,
minute: 22,
second: 22,
nanosecond: 22
)
var twosday: Date {
Calendar(identifier: .gregorian).date(from: ContentView.twosdayDateComponents)!
}
var body: some View {
VStack {
Text(twosday, format: Date.FormatStyle(date: .complete, time: .complete))
Text(twosday, format: .dateTime.hour())
Text(twosday, format: .dateTime.year().month().day())
}
.padding()
}
}
PlaygroundPage.current.setLiveView(ContentView())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment