[Swift] Duration を FormatStyle 指定で表示する

     
⌛️ 2 min.
あまり使われていない(?) Duration の あまり使われていない(?) FormatStyle を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Ventura 13.4
  • Swift 5.8.1 / Xcode 14.3.1
MEMO

この記事のコードは、Playground で動作します。

Duration

Duration は、Swift5.7 から導入された 時間を表現する型の1つです。

Apple のドキュメントは、こちら

Duration は、時間の長さを表現するために使用されます。以前から使われている TimeInterval と同じ様な目的で使用されます。

TimerInterval は、秒を単位として保持していましたが、Duration は、単位を指定して 初期化することができます。
例えば、10 秒は、以下のように 定義します。

import Foundation
let du = Duration.seconds(10)
print(du)   // print-out:  10.0 seconds

.seconds だけでなく、.milliseconds という ミリ秒単位で使いやすい initializer も用意されています。

Duration と TimeInterval

Swift5.7 以前は、TimeInterval という型が、時間の長さを表現するために使用されていました。

Duration と TimeInterval の関係は以下の記事で説明しています。

[Swift] Duration と TimeInterval の変換

FormatStyle

FormatStyle は、iOS15/ macOS12 から導入された データを特定の書式に変換するための 仕組みです。

以前は、DateFormatter 等の Formatter を使うのが通常でしたが、新しく導入された FormatStyle は、Formatter に比べて直感的に記述できるようになっています。

特定の書式に変換するための FormatStyle、特定の書式からデータに変換する ParseableFormatStyle は、以下の記事で説明しています。
[Swift] FormatStyle の使い方 [Swift] ParseableFormatStyle の使い方 [Swift] FormatStyle の作り方 [Swift] ParseableFormatStyle の作り方

上記では、電話番号の書式を例に 書式への変換方法から、書式からデータへの変換方法を説明しています。

Duration 向け FormatStyle

Duration に対しても FormatStyle も用意されています。

時間長についても 例えば、5時間24分を “5:24″ と表示したい時もあるでしょうし、”05:24” と表示したいかもしれません。
すべてを分に換算して “324” 分 と表示したい時もあるかもしれません。

ということで、同じ時間長であっても、さまざまな表現を行いたいケースが考えられ、そのようなケースに便利なのが、FormatStyle です。

以降では、さまざまなケースを例にどのような表が可能か確認してみます。

デフォルト FormatStyle

まずは、特に詳細を指定しないデフォルト表示です。

import Foundation

let du = Duration.seconds(1000)
print(du)
print(du.formatted())
// print-out

1000.0 seconds
0:16:40

1000秒 = 0時間16分 40秒なので、 “0:16:40” と表示されました。デフォルトでは、時間:分:秒 となっています。

Duration.TimeFormatStyle

Duration 向けに用意されている FormatStyle は、フルパス(?) で書くと、Duration.TimeFormatStyle です。

Apple のドキュメントは、こちら

以下の FormatStyle の initializer をみると、Duration.TimeFormatStyle.Pattern というデータ型を使用してフォーマットを制御することがわかります。

init(
    pattern: Duration.TimeFormatStyle.Pattern,
    locale: Locale = .autoupdatingCurrent
)

Duration.TimeFormatStyle.Pattern についてみてみます。

Apple のドキュメントは、こちら

パターンとしては、大きく以下の3パターン用意されています。カッコ内は static var として用意されている名前です 。
時間:分 (hourMinute)
時間:分:秒 (hourMinuteSecond)
分:秒 (minuteSecond)

common pattern を使ってみる

用意されているパターンを使って、表示してみます。

import Foundation

let du = Duration.seconds(1000)
print(du.formatted(Duration.TimeFormatStyle.time(pattern: .hourMinute)))
// 0:17
print(du.formatted(.time(pattern: .hourMinute)))
// 0:17
print(du.formatted(.time(pattern: .hourMinuteSecond)))
// 0:16:40
print(du.formatted(.time(pattern: .minuteSecond)))
// 16:40

# formatted の中では、通常 .time で記述できますが、フルパス(?) で記述すると、Duration.TimeFormatStyle.time のようになります。

表示される単位より下の単位の持つ数値については、デフォルトでは FloatingPointRoudingRule の .toNearestOrEven秒 で処理されます。例えば、.hourMinute 指定すると秒が省略されるので、0:16:30 -> 0:16 となり、0:16:31 -> 0:17 となります。

pattern のカスタマイズ

簡単に使えるパターンは上記の3種類ですが、カスタマイズすることも可能です。

カスタマイズは、Duration.TimeFormatStyle.Pattern を使って 指定します。

0 を padding して表示

時間を表示する時に、桁数を合わせるために、0 を追加することはよくあります。

例えば、1時間5分を表示する時に、”1:05″ “01:05” のどちらの表示にしたいかはケースバイケースだと思います。
このような表示を調整するときは、padHourToLength を指定します。(あくまで padding ですので、1 を指定しても必要があれば2桁表示されます)

import Foundation

let du = Duration.seconds(3900)
print(du.formatted(.time(pattern: .hourMinute)))
// 1:05
print(du.formatted(.time(pattern: .hourMinute(padHourToLength: 2))))
// 01:05

0 padding できるのは、最上位の情報だけです。
hourMinute では、hour に対して 0 padding でき、minuteSecond では、minute に対して 0 padding できます。

丸めかたをカスタマイズ

表示されない桁の数値をどのように丸めるかの指定も可能です。
具体的には、1時間10分20秒を、”時間:分”と表示する時、秒をどのように扱うかの指定です。

通常(?) の 数値丸め指定がそのまま使えます。つまり、FloatingPointRoudingRule で指定します。
Apple のドキュメントは、こちら

デフォルトでは、toNearestOrEven になっています。つまり、4捨6入で、5の時は偶数になるように選択されます。
以下は、1:00:01 を .up 指定することで、1:01 表示にしています。

import Foundation

let du = Duration.seconds(60*60 + 1)
print(du.formatted(.time(pattern: .hourMinute)))
// 1:00
print(du.formatted(.time(pattern: .hourMinute(padHourToLength: 2, roundSeconds: .up))))
// 01:01

秒表示をカスタマイズ

デフォルトのパターンで表示できる最小単位は秒です。

最小単位である秒についての表示もカスタマイズできるようになっています。

最後の秒表示を 小数点表示することで、秒単位より詳細にデータを表示することを制御できます。

例えば、2.1 秒 を “0:02.1” と表示するということです。

import Foundation

let du = Duration.seconds(2.04)
// (1)
print(du.formatted(.time(pattern: 
                   .minuteSecond(padMinuteToLength: 1, fractionalSecondsLength: 0, roundFractionalSeconds: .toNearestOrEven))))
// 0:02

// (2)
print(du.formatted(.time(pattern: 
                   .minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 0, roundFractionalSeconds: .up))))
// 00:03

// (3)
print(du.formatted(.time(pattern: 
                   .minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 1, roundFractionalSeconds: .up))))
// 00:02.1

Duration には、2.04 秒を設定しています。
(1) では、.minuteSecond 指定した時のデフォルト値を明示的に設定しています。
デフォルトでは1秒未満の数値は .toNearesetOrEvent で処理されています。
(2) では、padMinuteToLength に2を渡し、1秒未満の数値処理に .up 指定しているため、00:03 と表示されます。
(3) では、fractionalSecondsLength に1を渡し、0.1秒未満を .up 指定しているため、00:02.1 と表示されます。

桁溢れの場合

表示対象に満たない 数値については、roudFractionalSeconds で処理を指定できましたが、大きい側の数値についても確認します。

たとえば、.minuteSecond で 60分以上の時間長を表示してみます。

let du = Duration.seconds(75*60)
print(du.formatted(.time(pattern: .minuteSecond)))
// 75:00

上記のように、最上位の単位で表示します。ここでは、最上位の単位である 分 が 60 を超えた量まで表示に使用されます。

まとめ

Duration を FormatStyle で表示する方法

Duration を FormatStyle で表示する方法
  • FormatStyle は、Duration.TimeFormatStyle.time
  • おおきく3パターン( hourMinuteSecond, hourMinute, minuteSecond ) が用意されている
  • 最上位の単位については、0 padding できる
  • 最下位の単位については、処理方法を指定できる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

SwiftUI おすすめ本

SwiftUI を理解するには、以下の本がおすすめです。

SwiftUI ViewMatery

SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。

英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。

View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。

超便利です

SwiftUIViewsMastery

販売元のページは、こちらです。

SwiftUI 徹底入門

# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。

Swift学習におすすめの本

詳解Swift

Swift の学習には、詳解 Swift という書籍が、おすすめです。

著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。

最新版を購入するのがおすすめです。

現時点では、上記の Swift 5 に対応した第5版が最新版です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です