概要
WWDC21 にて、Foundation に新しいフォーマッターAPI FormatStyle が追加されました。
DateFormatter では "yyyy/MM/DD" のような日付フォーマット子を使って日付を任意のフォーマットで出力できましたが、Date.FormatStyle
では一見するとそのようなAPIがないように見えました。
長らく、僕の中で「FormatStyle API で Date をカスタムフォーマットでどう出力するんだろう」というのが疑問でしたが、ChatGPT に聞いたところあっさり解決したので共有します。
(ChatGPT は偉大)
やり方
DateFormatter とは思想が変わっており、FormatStyle では、タイプセーフに日付をフォーマットできるようなAPI設計になっています。
カスタムフォーマットには、Date.VerbatimFormat
を使います。
例えば、yyyy-MM-DD
と出力したい場合、以下のようにします
import Foundation
// Date.VerbatimFormatStyle を生成
let hyphenedDateFormatStyle: Date.VerbatimFormatStyle = .init(
format: "\(year: .padded(4))-\(month: .twoDigits)-\(day: .twoDigits)",
locale: Locale(languageCode: .japanese),
timeZone: TimeZone(secondsFromGMT: 32400)!,
calendar: Calendar(identifier: .gregorian)
)
// "25 Sep 2025 at 4:19 PM"
let now: Date = .now
let formattedDate = hyphenedDateFormatStyle.format(now)
print(formattedDate) // 2025-09-25
format 引数が肝です。
String Interpolation が拡張されており、出力したい日付要素ごとにタイプセーフな形で出力できるようになっています。
逆は?
では、フォーマットされた文字列から、Date を生成する方法はどうでしょうか。
これも、VerbatimFormatStyle を使います。(3通りありますが、一旦 VerbatimFormatStyle でのやり方を紹介します。)
let hyphenedDateFormatStyle: Date.VerbatimFormatStyle = .init(
format: "\(year: .padded(4))-\(month: .twoDigits)-\(day: .twoDigits)",
locale: Locale(languageCode: .japanese),
timeZone: TimeZone(secondsFromGMT: 32400)!,
calendar: Calendar(identifier: .gregorian)
)
let parsedHyphenedDate: Date = try hyphenedDateFormatStyle.parseStrategy.parse("2025-09-25")
print(parsedHyphenedDate) // 2025-09-24 15:00:00 +0000
フォーマットが一緒であれば、 VerbatimFormatStyle のインスタンスを使いまわせます。
所感
自由度が低そうに思えた FormatStyle ですが、意外にも自由度が高かったです。
正直、yMD
等変な形式でフォーマットするユースケースはほぼないに等しいと思うので、それを考えるとこれで十分なのではないでしょうか。