【Swift】Date構造体の使い方!日付の計算や比較方法

この記事からわかること

  • SwiftDate構造体使い方
  • 日付差分加算減算などの計算方法
  • UTCによる9時間ズレ
  • 比較演算子を用いた比較

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

参考文献: API Collection Dates and Times

Swiftの日付や時間操作API

Swiftでは日付や時間などを操作、管理するためにいくつかの構造体やクラスが基礎となるFoundationフレークワークの中に用意されています。

役割やできること

Date構造体・・・特定の日時を表す(カレンダーやタイムゾーンと無関係)
DateInterval構造体・・・期間
TimeIntervalタイプエイリアス・・・秒数(Double型のエイリアス)
DateComponents構造体・・・日付情報の一部を取得できる
Calendar構造体・・・日付の比較や計算や書き換えなどが可能
TimeZone構造体・・・日付情報のタイムゾーンを表す
DateFormatterクラス・・・日付情報を任意のフォーマットで表示

ざっくり見ると上記の通りです。それぞれ日時に関する機能を提供するAPIですが細かい役割が異なります。またこれらは密接に関わりを持っているので相互の変換などを行うことも可能です。

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

TimeIntervalDoubleのタイプエイリアスとなっています。また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文で範囲の定義

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index