【Swift】Date構造体の使い方!日付の計算や比較方法
この記事からわかること
- SwiftのDate構造体の使い方
- 日付の差分や加算、減算などの計算方法
- UTCによる9時間のズレ
- 比較演算子を用いた比較
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
参考文献: API Collection Dates and Times
Swiftの日付や時間操作API
Swiftでは日付や時間などを操作、管理するためにいくつかの構造体やクラスが基礎となるFoundationフレークワークの中に用意されています。
役割やできること
・Date構造体
・・・特定の日時を表す(カレンダーやタイムゾーンと無関係)
・DateInterval構造体
・・・期間
・TimeIntervalタイプエイリアス
・・・秒数(Double型のエイリアス)
・DateComponents構造体
・・・日付情報の一部を取得できる
・Calendar構造体
・・・日付の比較や計算や書き換えなどが可能
・TimeZone構造体
・・・日付情報のタイムゾーンを表す
・DateFormatterクラス
・・・日付情報を任意のフォーマットで表示
ざっくり見ると上記の通りです。それぞれ日時に関する機能を提供するAPIですが細かい役割が異なります。またこれらは密接に関わりを持っているので相互の変換などを行うことも可能です。
おすすめ記事
【Swift UI】typealias(タイプエイリアス)の使い方とメリット
【Swift】DateFormatterの使い方!書式や日付形式の調整方法
【Swift】DateComponentsとは?使用方法とDate型との違い
【Swift】Calendar構造体の使い方!暦法の変更と日付の計算
Date構造体とは?
Date
構造体とは設定されているカレンダーやタイムゾーンとは関連を持たず、特定の日時を表現するオブジェクトです。引数なしのイニシャライザまたはタイププロパティ(static)nowから現在時刻のインスタンスを生成(参照)することができます。
おすすめ記事:【Swift】プロパティの種類!ストアドプロパティやタイププロパティとは?
let day = Date() // Feb 10, 2023 at 7:02 PM
print(day) // 2023-02-10 10:02:22 +0000
let day2 = Date.now // Feb 10, 2023 at 7:02 PM
print(day2) // 2023-02-10 10:02:22 +0000
9時間のズレが生じる原因
しかしprint
した時刻とオブジェクトの時刻では10時とPM 7時(19時)で日時情報には誤差が生まれています。これはDateオブジェクトがUTC(協定世界時)で日時情報を保持しているためであり、UTCと日本標準時(JTC)とは9時間ズレているため誤差が生まれてしまうのです。
playgroundで見た時に+0000
となるのは誤差が0時間(つまりUTC)であることを表しています。
日本標準時で文字列として出力したい場合はdescription(with:)
メソッドの引数に任意のロケール値を渡せば出力可能です。
print(day.description(with: Locale(identifier: "ja_JP")))
// 2023年2月10日 金曜日 19時06分57秒 日本標準時
またDateFormatter
クラスを使用した方法でも出力できます。
let df = DateFormatter()
df.calendar = Calendar(identifier: .gregorian)
df.locale = Locale(identifier: "ja_JP")
df.timeZone = TimeZone(identifier: "Asia/Tokyo")
df.dateStyle = .full
df.timeStyle = .short
print(df.string(from: Date()))
// 2023年2月10日 金曜日 19:06
任意の時間後のオブジェクトを生成する
現在時刻のオブジェクトの生成方法が分かったところで30秒後や2日後、1年後など任意の時間経過後のオブジェクトを生成する方法をみていきます。
実装するにはinit(timeIntervalSinceNow:)
を使用します。引数にはTimeInterval
型(Doubleのエイリアス)で現在から任意の日時までの秒数を渡します。
// 30秒後の日時
let dayLater = Date(timeIntervalSinceNow: 30)
print(dayLater.description(with: Locale(identifier: "ja_JP")))
// 2023年2月10日 金曜日 19時09分56秒 日本標準時
// 明日(1日後)の日付 60秒 × 60分 × 24時間
let tomorrow = Date(timeIntervalSinceNow: ( 60 * 60 * 24))
print(tomorrow.description(with: Locale(identifier: "ja_JP")))
// 2023年2月11日 土曜日 19時09分26秒 日本標準時
// 1年後の日付 60秒 × 60分 × 24時間 × 365日
let yearLater = Date(timeIntervalSinceNow: ( 60 * 60 * 24 * 365))
print(yearLater.description(with: Locale(identifier: "ja_JP")))
// 2024年2月10日 土曜日 19時09分26秒 日本標準時
その他のプロパティやメソッド
struct Date {
func compare(Date) -> ComparisonResult // 任意の日付とこの日付を比較
func distance(to: Date) -> TimeInterval // この日付から任意の日付までの間隔
func timeIntervalSince(Date) -> TimeInterval // この日付と任意の日付の間隔
var timeIntervalSinceNow: TimeInterval // この日付と現在の日付と時刻の間隔
var timeIntervalSince1970: TimeInterval // この日付と1970年1月1日の00:00:00 UTCの間隔
func addTimeInterval(TimeInterval) // この日付に時間間隔を追加
func addingTimeInterval(TimeInterval) -> Date // この日付に時間間隔を追加して、新しい日付値を作成
}
timeIntervalSince1970:UNIX形式で取得
timeIntervalSince1970
プロパティは自身の日付と1970年1月1日の00:00:00 UTCとの間隔(UNIX)をTimeInterval型の秒数で取得できます。
print(Date().timeIntervalSince1970) // 1697804562.189516
print(type(of: Date().timeIntervalSince1970)) // Double
TimeInterval
はDouble
のタイプエイリアスとなっています。またTimeInterval
型は簡単にDate
型に変換できるようになっています。
let interval = Date().timeIntervalSince1970
let date = Date(timeIntervalSince1970: TimeInterval(interval))
typealias TimeInterval = Double
日付の比較
DateオブジェクトはComparable
プロトコルに準拠しているため演算子(=><)を用いた比較が可能になっています。
let day1 = Date()
print(day1.description(with: Locale(identifier: "ja_JP")))
// 2023年2月10日 金曜日 19時16分13秒 日本標準時
let day2 = Date()
print(day2.description(with: Locale(identifier: "ja_JP")))
// 2023年2月10日 金曜日 19時16分13秒 日本標準時
if day1 == day2 {
print("OK")
} else {
print("NG") // NG
}
上記の場合は一致になりそうですが、インスタンス化のタイミングにわずかながら誤差がある(秒数がズレる)ため不一致になるのでしょうか。もちろん同じインスタンス同士は一致し、比較してみるとday2
の方が大きい(時間が進んでいる)となるようです。
if day1 == day1 {
print("OK") // OK
} else {
print("NG")
}
if day1 < day2 {
print("OK") // OK
} else {
print("NG")
}
Calendar.isDate(_:inSameDayAs:)で日付の一致を識別
公式リファレンス:isDate(_:inSameDayAs:)
日付が一致するかどうかを識別するためにはCalendar
構造体のisDate(_:inSameDayAs:)
を使用することもできます。
let interval = Date().timeIntervalSince1970
let date = Date(timeIntervalSince1970: TimeInterval(interval))
Calendar.current.isDate(date, inSameDayAs: Date()) // true
日付の計算方法
任意の日付から加算や減算などDate型のまま計算をするにはCalendar.date(byAdding:,value: ,to:)
メソッドを使用します。Calendar
からは他にも日付を操作するための様々なメソッドが用意されています。
let now = Date() // "Apr 17, 2023 at 7:03 PM"
let calendar = Calendar.current
let modifiedDate = calendar.date(byAdding: .day, value: 3, to: now)
print(modifiedDate) // Optional(2023-04-20 10:03:01 +0000)
おすすめ記事:【Swift】日付が期間内か識別する方法!switch文で範囲の定義
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。