-
-
Save minorbug/468790060810e0d29545 to your computer and use it in GitHub Desktop.
func timeAgoSinceDate(date:NSDate, numericDates:Bool) -> String { | |
let calendar = NSCalendar.currentCalendar() | |
let unitFlags = NSCalendarUnit.CalendarUnitMinute | NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitWeekOfYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitSecond | |
let now = NSDate() | |
let earliest = now.earlierDate(date) | |
let latest = (earliest == now) ? date : now | |
let components:NSDateComponents = calendar.components(unitFlags, fromDate: earliest, toDate: latest, options: nil) | |
if (components.year >= 2) { | |
return "\(components.year) years ago" | |
} else if (components.year >= 1){ | |
if (numericDates){ | |
return "1 year ago" | |
} else { | |
return "Last year" | |
} | |
} else if (components.month >= 2) { | |
return "\(components.month) months ago" | |
} else if (components.month >= 1){ | |
if (numericDates){ | |
return "1 month ago" | |
} else { | |
return "Last month" | |
} | |
} else if (components.weekOfYear >= 2) { | |
return "\(components.weekOfYear) weeks ago" | |
} else if (components.weekOfYear >= 1){ | |
if (numericDates){ | |
return "1 week ago" | |
} else { | |
return "Last week" | |
} | |
} else if (components.day >= 2) { | |
return "\(components.day) days ago" | |
} else if (components.day >= 1){ | |
if (numericDates){ | |
return "1 day ago" | |
} else { | |
return "Yesterday" | |
} | |
} else if (components.hour >= 2) { | |
return "\(components.hour) hours ago" | |
} else if (components.hour >= 1){ | |
if (numericDates){ | |
return "1 hour ago" | |
} else { | |
return "An hour ago" | |
} | |
} else if (components.minute >= 2) { | |
return "\(components.minute) minutes ago" | |
} else if (components.minute >= 1){ | |
if (numericDates){ | |
return "1 minute ago" | |
} else { | |
return "A minute ago" | |
} | |
} else if (components.second >= 3) { | |
return "\(components.second) seconds ago" | |
} else { | |
return "Just now" | |
} | |
} |
Using Date instead of NSDate:
func timeAgoSinceDate(_ date:Date, numericDates:Bool = false) -> String {
let calendar = NSCalendar.current
let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfYear, .month, .year, .second]
let now = Date()
let earliest = now < date ? now : date
let latest = (earliest == now) ? date : now
let components = calendar.dateComponents(unitFlags, from: earliest, to: latest)
if (components.year! >= 2) {
return "\(components.year!) years ago"
} else if (components.year! >= 1){
if (numericDates){
return "1 year ago"
} else {
return "Last year"
}
} else if (components.month! >= 2) {
return "\(components.month!) months ago"
} else if (components.month! >= 1){
if (numericDates){
return "1 month ago"
} else {
return "Last month"
}
} else if (components.weekOfYear! >= 2) {
return "\(components.weekOfYear!) weeks ago"
} else if (components.weekOfYear! >= 1){
if (numericDates){
return "1 week ago"
} else {
return "Last week"
}
} else if (components.day! >= 2) {
return "\(components.day!) days ago"
} else if (components.day! >= 1){
if (numericDates){
return "1 day ago"
} else {
return "Yesterday"
}
} else if (components.hour! >= 2) {
return "\(components.hour!) hours ago"
} else if (components.hour! >= 1){
if (numericDates){
return "1 hour ago"
} else {
return "An hour ago"
}
} else if (components.minute! >= 2) {
return "\(components.minute!) minutes ago"
} else if (components.minute! >= 1){
if (numericDates){
return "1 minute ago"
} else {
return "A minute ago"
}
} else if (components.second! >= 3) {
return "\(components.second!) seconds ago"
} else {
return "Just now"
}
}
thank you, guys!
@heinrisch let calendar = NSCalendar.current
should be changed to let calendar = Calendar.current
otherwise the month, year, week components were 0 for me.
I rewrote this to be more swift-y because I needed future dates as well. Naming could be better I guess
struct DateComponentUnitFormatter {
private struct DateComponentUnitFormat {
let unit: Calendar.Component
let singularUnit: String
let pluralUnit: String
let futureSingular: String
let pastSingular: String
}
private let formats: [DateComponentUnitFormat] = [
DateComponentUnitFormat(unit: .year,
singularUnit: "year",
pluralUnit: "years",
futureSingular: "Next year",
pastSingular: "Last year"),
DateComponentUnitFormat(unit: .month,
singularUnit: "month",
pluralUnit: "months",
futureSingular: "Next month",
pastSingular: "Last month"),
DateComponentUnitFormat(unit: .weekOfYear,
singularUnit: "week",
pluralUnit: "weeks",
futureSingular: "Next week",
pastSingular: "Last week"),
DateComponentUnitFormat(unit: .day,
singularUnit: "day",
pluralUnit: "days",
futureSingular: "Tomorrow",
pastSingular: "Yesterday"),
DateComponentUnitFormat(unit: .hour,
singularUnit: "hour",
pluralUnit: "hours",
futureSingular: "In an hour",
pastSingular: "An hour ago"),
DateComponentUnitFormat(unit: .minute,
singularUnit: "minute",
pluralUnit: "minutes",
futureSingular: "In a minute",
pastSingular: "A minute ago"),
DateComponentUnitFormat(unit: .second,
singularUnit: "second",
pluralUnit: "seconds",
futureSingular: "Just now",
pastSingular: "Just now"),
]
func string(forDateComponents dateComponents: DateComponents, useNumericDates: Bool) -> String {
for format in self.formats {
let unitValue: Int
switch format.unit {
case .year:
unitValue = dateComponents.year ?? 0
case .month:
unitValue = dateComponents.month ?? 0
case .weekOfYear:
unitValue = dateComponents.weekOfYear ?? 0
case .day:
unitValue = dateComponents.day ?? 0
case .hour:
unitValue = dateComponents.hour ?? 0
case .minute:
unitValue = dateComponents.minute ?? 0
case .second:
unitValue = dateComponents.second ?? 0
default:
assertionFailure("Date does not have requried components")
return ""
}
switch unitValue {
case 2 ..< Int.max:
return "\(unitValue) \(format.pluralUnit) ago"
case 1:
return useNumericDates ? "\(unitValue) \(format.singularUnit) ago" : format.pastSingular
case -1:
return useNumericDates ? "In \(-unitValue) \(format.singularUnit)" : format.futureSingular
case Int.min ..< -1:
return "In \(-unitValue) \(format.pluralUnit)"
default:
break
}
}
return "Just now"
}
}
extension Date {
func timeAgoSinceNow(useNumericDates: Bool = false) -> String {
let calendar = Calendar.current
let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfYear, .month, .year, .second]
let now = Date()
let components = calendar.dateComponents(unitFlags, from: self, to: now)
let formatter = DateComponentUnitFormatter()
return formatter.string(forDateComponents: components, useNumericDates: useNumericDates)
}
}
https://gist.github.com/jinthagerman/009c85b7bbd0a40dcbba747e89a501bf
Golden. Thanks for the time saver.
If anyone found themselves here and needed usage:
if let timeAgo = timeAgoSinceDate(yourInfoAsDateFormat) {
yourLabel.text = timeAgo
}
Here's a another version
import Foundation
extension Date {
fileprivate struct Item {
let multi: String
let single: String
let last: String
let value: Int?
}
fileprivate var components: DateComponents {
return Calendar.current.dateComponents(
[.minute, .hour, .day, .weekOfYear, .month, .year, .second],
from: Calendar.current.date(byAdding: .second, value: -1, to: Date())!,
to: self
)
}
fileprivate var items: [Item] {
return [
Item(multi: "years ago", single: "1 year ago", last: "Last year", value: components.year),
Item(multi: "months ago", single: "1 month ago", last: "Last month", value: components.month),
Item(multi: "weeks ago", single: "1 week ago", last: "Last week", value: components.weekday),
Item(multi: "days ago", single: "1 day ago", last: "Last day", value: components.day),
Item(multi: "minutes ago", single: "1 minute ago", last: "Last minute", value: components.minute),
Item(multi: "seconds ago", single: "Just now", last: "Last second", value: components.second)
]
}
public func timeAgo(numericDates: Bool = false) -> String {
for item in items {
switch (item.value, numericDates) {
case let (.some(step), _) where step == 0:
continue
case let (.some(step), true) where step == 1:
return item.last
case let (.some(step), false) where step == 1:
return item.single
case let (.some(step), _):
return String(step) + " " + item.multi
default:
continue
}
}
return "Just now"
}
}
print(Calendar.current.date(byAdding: .day, value: 1, to: Date())!.timeAgo())
A Swift 4 version without force unwrapping:
extension Date {
func timeAgoSinceDate(numericDates:Bool) -> String {
let calendar = Calendar.current
let now = Date()
let earliest = self < now ? self : now
let latest = self > now ? self : now
let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfMonth, .month, .year, .second]
let components: DateComponents = calendar.dateComponents(unitFlags, from: earliest, to: latest)
if let year = components.year {
if (year >= 2) {
return "\(year) years ago"
} else if (year >= 1) {
return stringToReturn(flag: numericDates, strings: ("1 year ago", "Last year"))
}
}
if let month = components.month {
if (month >= 2) {
return "\(month) months ago"
} else if (month >= 2) {
return stringToReturn(flag: numericDates, strings: ("1 month ago", "Last month"))
}
}
if let weekOfYear = components.weekOfYear {
if (weekOfYear >= 2) {
return "\(weekOfYear) months ago"
} else if (weekOfYear >= 2) {
return stringToReturn(flag: numericDates, strings: ("1 week ago", "Last week"))
}
}
if let day = components.day {
if (day >= 2) {
return "\(day) days ago"
} else if (day >= 2) {
return stringToReturn(flag: numericDates, strings: ("1 day ago", "Yesterday"))
}
}
if let hour = components.hour {
if (hour >= 2) {
return "\(hour) hours ago"
} else if (hour >= 2) {
return stringToReturn(flag: numericDates, strings: ("1 hour ago", "An hour ago"))
}
}
if let minute = components.minute {
if (minute >= 2) {
return "\(minute) minutes ago"
} else if (minute >= 2) {
return stringToReturn(flag: numericDates, strings: ("1 minute ago", "A minute ago"))
}
}
if let second = components.second {
if (second >= 3) {
return "\(second) seconds ago"
}
}
return "Just now"
}
private func stringToReturn(flag:Bool, strings: (String, String)) -> String {
if (flag){
return strings.0
} else {
return strings.1
}
}
}
Sorry for interrupting the party, but seems like "time ago" feature is implemented in DateTools with locales and just works:
https://github.com/MatthewYork/DateTools#time-ago
let timeAgoDate = 2.days.earlier
print("Time Ago: ", timeAgoDate.timeAgoSinceNow)
print("Time Ago: ", timeAgoDate.shortTimeAgoSinceNow)
//Output:
//Time Ago: 2 days ago
//Time Ago: 2d
The framework looks nice. However, sometimes only a feature is needed, not the whole framework = why to buy the whole cow if you need only milk.
This is my version. It is based on @dariukas, but fixes some bugs
import Foundation
extension Date {
///
/// Provides a humanised date. For instance: 1 minute, 1 week ago, 3 months ago
///
/// - Parameters:
// - numericDates: Set it to true to get "1 year ago", "1 month ago" or false if you prefer "Last year", "Last month"
///
func timeAgo(numericDates:Bool) -> String {
let calendar = Calendar.current
let now = Date()
let earliest = self < now ? self : now
let latest = self > now ? self : now
let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfMonth, .month, .year, .second]
let components: DateComponents = calendar.dateComponents(unitFlags, from: earliest, to: latest)
//print("")
//print(components)
if let year = components.year {
if (year >= 2) {
return "\(year) years ago"
} else if (year >= 1) {
return numericDates ? "1 year ago" : "Last year"
}
}
if let month = components.month {
if (month >= 2) {
return "\(month) months ago"
} else if (month >= 1) {
return numericDates ? "1 month ago" : "Last month"
}
}
if let weekOfMonth = components.weekOfMonth {
if (weekOfMonth >= 2) {
return "\(weekOfMonth) weeks ago"
} else if (weekOfMonth >= 1) {
return numericDates ? "1 week ago" : "Last week"
}
}
if let day = components.day {
if (day >= 2) {
return "\(day) days ago"
} else if (day >= 1) {
return numericDates ? "1 day ago" : "Yesterday"
}
}
if let hour = components.hour {
if (hour >= 2) {
return "\(hour) hours ago"
} else if (hour >= 1) {
return numericDates ? "1 hour ago" : "An hour ago"
}
}
if let minute = components.minute {
if (minute >= 2) {
return "\(minute) minutes ago"
} else if (minute >= 1) {
return numericDates ? "1 minute ago" : "A minute ago"
}
}
if let second = components.second {
if (second >= 3) {
return "\(second) seconds ago"
}
}
return "Just now"
}
}
@merlos nice, thank you !
@merlos awesome! thank you
My version in Swift 4.2:
import Foundation
extension Date {
func timeAgo(numericDates: Bool) -> String {
let calendar = Calendar.current
let now = Date()
let earliest = self < now ? self : now
let latest = self > now ? self : now
let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfMonth, .month, .year, .second]
let components: DateComponents = calendar.dateComponents(unitFlags, from: earliest, to: latest)
let year = components.year ?? 0
let month = components.month ?? 0
let weekOfMonth = components.weekOfMonth ?? 0
let day = components.day ?? 0
let hour = components.hour ?? 0
let minute = components.minute ?? 0
let second = components.second ?? 0
switch (year, month, weekOfMonth, day, hour, minute, second) {
case (let year, _, _, _, _, _, _) where year >= 2: return "\(year) years ago"
case (let year, _, _, _, _, _, _) where year == 1 && numericDates: return "1 year ago"
case (let year, _, _, _, _, _, _) where year == 1 && !numericDates: return "Last year"
case (_, let month, _, _, _, _, _) where month >= 2: return "\(month) months ago"
case (_, let month, _, _, _, _, _) where month == 1 && numericDates: return "1 month ago"
case (_, let month, _, _, _, _, _) where month == 1 && !numericDates: return "Last month"
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth >= 2: return "\(weekOfMonth) weeks ago"
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth == 1 && numericDates: return "1 week ago"
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth == 1 && !numericDates: return "Last week"
case (_, _, _, let day, _, _, _) where day >= 2: return "\(day) days ago"
case (_, _, _, let day, _, _, _) where day == 1 && numericDates: return "1 day ago"
case (_, _, _, let day, _, _, _) where day == 1 && !numericDates: return "Yesterday"
case (_, _, _, _, let hour, _, _) where hour >= 2: return "\(hour) hours ago"
case (_, _, _, _, let hour, _, _) where hour == 1 && numericDates: return "1 hour ago"
case (_, _, _, _, let hour, _, _) where hour == 1 && !numericDates: return "An hour ago"
case (_, _, _, _, _, let minute, _) where minute >= 2: return "\(minute) minutes ago"
case (_, _, _, _, _, let minute, _) where minute == 1 && numericDates: return "1 minute ago"
case (_, _, _, _, _, let minute, _) where minute == 1 && !numericDates: return "A minute ago"
case (_, _, _, _, _, _, let second) where second >= 3: return "\(second) seconds ago"
default: return "Just now"
}
}
}
Thanks