Posted at

現在との時間差を計算する(後追い)

More than 3 years have passed since last update.

Swiftで現在からの経過時間を計算するNSDateのExtension」(@hayakawatomoakiさん) を読んだら面白そうだったので、自分でも書いてみたくなりました。

 まるきり後出しなので、なるべく短く、いかにも簡単そうに書きたいです (希望)。

import Foundation

enum TimeDuration {
case Day(Int), Hour(Int), Minute(Int), Second(Int)
}

extension NSDate {
func durationFromNow() -> TimeDuration {
let value = Int(self.timeIntervalSinceNow)
let duration = (Minute: 60, Hour: 3600, Day: 3600*24)
switch abs(value) {
case 0..<duration.Minute:
return .Second(value)
case duration.Minute..<duration.Hour:
return .Minute(value / duration.Minute)
case duration.Hour..<duration.Day:
return .Hour(value / duration.Hour)
default:
return .Day(value / duration.Day)
}
}
}

 戻り値を文字列ではなく、列挙型にしてみました。測定結果を秒・分・時・日の4タイプに分け、具体的な数字をくっつけて返します。データが単純なので、処理もカンタン。

 素直に文字列を返すほうが使いやすいのですが、このくらい緩いメソッドなら使い回しもできそうです。

 使うときはこのような感じになります。

// 対象時刻を用意 (2015年1月1日)

let day = NSCalendar.currentCalendar().dateWithEra(1, year: 2015, month: 1, day: 1, hour: 0, minute: 0, second: 0, nanosecond: 0)

// 変換用のクロージャ
let s = { (n: Int, s: String) -> String in
String(abs(n)) + s + (n <= 0 ? "前" : "後")
}

// NSDateを取り出して処理
day.map {
d -> () in
var str: String
switch d.durationFromNow() {
case .Second(let n):
str = s(n, "秒")
case .Minute(let n):
str = s(n, "分")
case .Hour(let n):
str = s(n, "時間")
case .Day(let n):
str = s(n, "日")
}
println("\(str)のNSDateです") // -> 40日前のNSDateです (ヒエー)
}

 durationFromNow () の戻り値は、列挙型に整数がついてくるだけです。したがって用途に応じて文字列に変える必要があります。ここではクロージャを使っていますが、enumに関数を付けてもよさそうですね。

 なおNSDateはNSCalendarで用意しました。これだけでかなり楽ができます。