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
}
}