M-Miyazako
2/6/2020 - 6:29 AM

日付関連

日付関連

import Foundation

/// 日時
struct DateTime {
    /// 値
    let value: Date

    /// DateTimeのインスタンスを生成する
    ///
    /// - Parameter value: 日時の値
    init(value: Date) {
        self.value = value
    }

    /// 使用するカレンダー
    var calendar: Calendar {
        return Calendar(identifier: .gregorian)
    }

    /// 日付と時刻を指定する
    ///
    /// - Parameters:
    ///   - year: 年
    ///   - month: 月
    ///   - day: 日
    ///   - hour: 時
    ///   - minute: 分
    ///   - second: 秒
    init(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) {
        let calendar = Calendar(identifier: .gregorian)
        let components = DateComponents(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
        guard let date = calendar.date(from: components) else {
            fatalError()
        }
        self.init(value: date)
    }

    /// 現在日時のインスタンス
    static var now: DateTime {
        return DateTime(value: Date())
    }

    /// 5分単位に丸める
    var by5Minutes: DateTime {
        var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self.value)
        let minute = components.minute ?? 0
        components.minute = minute - (minute % 5)
        return DateTime(value: calendar.date(from: components) ?? self.value)
    }

    /// 日本を設定したDateFormatterを返す
    ///
    /// - Returns: 日本を設定したDateFormatter
    private func japaneseFormatter() -> DateFormatter {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "ja_JP")
        return formatter
    }

    /// 文字列表現
    var description: String {
        let formatter = japaneseFormatter()
        formatter.dateStyle = .long
        formatter.timeStyle = .short
        return formatter.string(from: value)
    }

    /// 日付のみの文字列表現
    var dateOnlyDescription: String {
        let formatter = japaneseFormatter()
        formatter.dateStyle = .long
        formatter.timeStyle = .none
        return formatter.string(from: value)
    }

    /// 時刻のみの文字列表現
    var timeOnlyDescription: String {
        let formatter = japaneseFormatter()
        formatter.dateStyle = .none
        formatter.timeStyle = .short
        return formatter.string(from: value)
    }

    /// ISO8601形式の文字列表現
    var iso8601Description: String {
        let formatter = ISO8601DateFormatter()
        formatter.timeZone = TimeZone.current
        return formatter.string(from: value)
    }

    /// ISO8601形式の文字列からインスタンスを作成する
    ///
    /// - Parameter string: 文字列
    /// - Returns: DateTime
    static func fromIso8601(string: String) -> DateTime? {
        let formatter = ISO8601DateFormatter()
        formatter.timeZone = TimeZone.current
        guard let date = formatter.date(from: string) else {
            return nil
        }
        return DateTime(value: date)
    }

    /// 日の開始時間
    var startOfDate: DateTime {
        return DateTime(value: calendar.startOfDay(for: self.value))
    }

    /// 日の最終時間
    var endOfDate: DateTime {
        let date = Date(timeInterval: TimeInterval( (60 * 60 * 24) - 1 ),
                        since: calendar.startOfDay(for: self.value))
        return DateTime(value: date)
    }

    /// 過去日のインスタンスを生成する
    ///
    /// - Parameter days: 遡る日数
    func pastDays(days: Int) -> DateTime {
        return DateTime(value: value.addingTimeInterval(TimeInterval(-60 * 60 * 24 * days)))
    }

    /// 現在時刻から24時間以上過ぎているかをBool値で返す
    var is24HoursAgo: Bool {
        return value < Date().addingTimeInterval(TimeInterval(-60 * 60 * 24))
    }

    /// 指定した日時と同じ日か判定する
    ///
    /// - Parameter another: 比較する日時
    /// - Returns: 同じ日ならtrue
    func isSameDay(as another: Date) -> Bool {
        return calendar.isDate(value, inSameDayAs: another)
    }

    /// 現在日か
    ///
    /// - Returns: 現在日ならtrue
    var isToday: Bool {
        return self.isSameDay(as: Date())
    }

    /// 時刻を23:55に変更したインスタンスを生成する
    ///
    /// - Returns: 時刻を23:55に変更したインスタンス
    var lastRecordingTime: DateTime {
        return self.endOfDate.by5Minutes
    }

    /// 書式を指定して文字列表現を取得する
    ///
    /// - Parameter format: 書式
    /// - Returns: 文字列表現
    func format(_ format: String) -> String {
        let formatter = japaneseFormatter()
        formatter.dateFormat = format
        return formatter.string(from: value)
    }
}

// MARK: - Comparable

extension DateTime: Comparable {
    static func < (lhs: DateTime, rhs: DateTime) -> Bool {
        return lhs.value < rhs.value
    }

    static func == (lhs: DateTime, rhs: DateTime) -> Bool {
        return lhs.value == rhs.value
    }
}