🙌
Swiftで経過時間を簡単な形式で表示する方法
やりたいこと
Twitterの投稿日などの今からどのくらい前にツイートがあったかの日付の差分を表示するものです。ここでは単位を1つだけに絞って表示することを目標とします
以下のクラスを使ってやっていきます
- Date
- DateComponents
- DateComponentsFormatter
- Calendar
実装
extension Calendar {
public func durationString(candidate components: [Calendar.Component], style unitsStyle: DateComponentsFormatter.UnitsStyle = .abbreviated, from start: Date, to end: Date) -> String? {
for component in components {
let dateComponents = self.dateComponents([component], from: start, to: end)
if let time = dateComponents.getTime(with: component) {
if time > 0 {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = component.unit
formatter.unitsStyle = unitsStyle
formatter.calendar = self
return formatter.string(from: dateComponents)
}
}
}
return nil
}
}
extension DateComponents {
public func getTime(with component: Calendar.Component) -> Int? {
switch component {
case .second: return second
case .era: return era
case .year: return year
case .month: return month
case .day: return day
case .hour: return hour
case .minute: return minute
case .weekday: return weekday
case .weekdayOrdinal: return weekdayOrdinal
case .quarter: return quarter
case .weekOfMonth: return weekOfMonth
case .weekOfYear: return weekOfYear
case .yearForWeekOfYear: return yearForWeekOfYear
case .nanosecond: return nanosecond
default:
fatalError()
}
}
}
extension Calendar.Component {
public var unit: NSCalendar.Unit {
switch self {
case .second: return .second
case .era: return .era
case .year: return .year
case .month: return .month
case .day: return .day
case .hour: return .hour
case .minute: return .minute
case .weekday: return .weekday
case .weekdayOrdinal: return .weekdayOrdinal
case .quarter: return .quarter
case .weekOfMonth: return .weekOfMonth
case .weekOfYear: return .weekOfYear
case .yearForWeekOfYear: return .yearForWeekOfYear
case .nanosecond: return .nanosecond
case .calendar: return .calendar
case .timeZone: return .timeZone
@unknown default:
fatalError()
}
}
}
テストコード
var calendar = Calendar.current
calendar.locale = .init(identifier: "ja_JP")
let now = Date()
let oneDayAgo: Date = {
var components = DateComponents()
components.day = -1
return calendar.date(byAdding: components, to: now)!
}()
let oneHourAgo: Date = {
var components = DateComponents()
components.hour = -1
return calendar.date(byAdding: components, to: now)!
}()
let oneMinuteAgo: Date = {
var components = DateComponents()
components.minute = -1
return calendar.date(byAdding: components, to: now)!
}()
let oneSecondAgo: Date = {
var components = DateComponents()
components.second = -1
return calendar.date(byAdding: components, to: now)!
}()
let candidates: [Calendar.Component] = [.day, .hour, .minute, .second]
let dayDuration = calendar.durationString(candidate: candidates, from: oneDayAgo, to: now)
// "1日"
let hourDuration = calendar.durationString(candidate: candidates, from: oneHourAgo, to: now)
// "1時間"
let minuteDuration = calendar.durationString(candidate: candidates, from: oneMinuteAgo, to: now)
// "1分"
let secondDuration = calendar.durationString(candidate: candidates, from: oneSecondAgo, to: now)
// "1秒"
まとめ
Calendarのextensionとして実装することでLocaleの設定などもできます。
同じように見えるCalendar.Component
とNSCalendar.Unit
を変換しているだけの
extensionもあり微妙に見えるところもあるので、何か改善点などがあればコメントで教えてください。
Discussion