Skip to content

Instantly share code, notes, and snippets.

@piemonte
Created September 24, 2022 22:11
Show Gist options
  • Save piemonte/cd81e33685566512cf769c664d467c3f to your computer and use it in GitHub Desktop.
Save piemonte/cd81e33685566512cf769c664d467c3f to your computer and use it in GitHub Desktop.
//
// Foundation+TimeAgo.swift
//
// The MIT License (MIT)
//
// Copyright (c) 2015-present patrick piemonte (http://patrickpiemonte.com/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
extension Date {
public var millisecondsSince1970: Int {
Int((self.timeIntervalSince1970 * 1000.0).rounded())
}
public var yesterday: Date {
Calendar.current.date(byAdding: .day, value: -1, to: self)!
}
public func hoursFromNow() -> Int {
let date = Date()
return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
}
public func minutesFromNow() -> Int {
let date = Date()
return Calendar.current.dateComponents([.minute], from: date, to: self).hour ?? 0
}
public func hours(from date: Date) -> Int {
Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
}
public func minutes(from date: Date) -> Int {
Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
private static let shortPrettyDateFormatter = DateFormatter()
/// Returns a short pretty date.
/// - Returns: Formatted date string.
public func shortPrettyDate() -> String {
Date.shortPrettyDateFormatter.dateFormat = "M/d/yy"
return Date.shortPrettyDateFormatter.string(from: self)
}
private static let shortPrettyDateAndTimeFormatter = DateFormatter()
/// Returns a short pretty date and time.
/// - Returns: Formatted date and time string.
public func shortPrettyDateTime() -> String {
Date.shortPrettyDateAndTimeFormatter.dateFormat = "M/d/yyyy h:mma"
return Date.shortPrettyDateAndTimeFormatter.string(from: self)
}
private static let prettyDateFormatter = DateFormatter()
/// Return a short pretty date.
/// - Returns: Formatted date string.
public func prettyDate() -> String {
Date.prettyDateFormatter.dateFormat = "MMMM yyyy"
return Date.prettyDateFormatter.string(from: self)
}
private static let fullPrettyDateFormatter = DateFormatter()
/// Return a short pretty date.
/// - Returns: Formatted date string.
public func fullPrettyDate() -> String {
Date.prettyDateFormatter.dateFormat = "MMMM d, yyyy"
return Date.prettyDateFormatter.string(from: self)
}
/// Create a pretty formatted string of time passed based on the date components, ex: "Last month", "2 weeks ago", "Just now"
/// - Parameter numericDates: A flag to determine if a numerical string should be used, ex: "An hour ago" vs "1 hour ago".
/// - Returns: Formatted string of time passed.
public func timeAgo(numericDates: Bool = true) -> 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 NSLocalizedString("\(year) years ago", comment: "\(year) years ago")
case (let year, _, _, _, _, _, _) where year == 1 && numericDates: return NSLocalizedString("1 year ago", comment: "1 year ago")
case (let year, _, _, _, _, _, _) where year == 1 && !numericDates: return NSLocalizedString("Last year", comment: "Last year")
case (_, let month, _, _, _, _, _) where month >= 2: return NSLocalizedString("\(month) months ago", comment: "\(month) months ago")
case (_, let month, _, _, _, _, _) where month == 1 && numericDates: return NSLocalizedString("1 month ago", comment: "1 month ago")
case (_, let month, _, _, _, _, _) where month == 1 && !numericDates: return NSLocalizedString("Last month", comment: "Last month")
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth >= 2: return NSLocalizedString("\(weekOfMonth) weeks ago", comment: "\(weekOfMonth) weeks ago")
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth == 1 && numericDates: return NSLocalizedString("1 week ago", comment: "1 week ago")
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth == 1 && !numericDates: return NSLocalizedString("Last week", comment: "Last week")
case (_, _, _, let day, _, _, _) where day >= 2: return NSLocalizedString("\(day) days ago", comment: "\(day) days ago")
case (_, _, _, let day, _, _, _) where day == 1 && numericDates: return NSLocalizedString("1 day ago", comment: "1 day ago")
case (_, _, _, let day, _, _, _) where day == 1 && !numericDates: return NSLocalizedString("Yesterday", comment: "Yesterday")
case (_, _, _, _, let hour, _, _) where hour >= 2: return NSLocalizedString("\(hour) hours ago", comment: "\(hour) hours ago")
case (_, _, _, _, let hour, _, _) where hour == 1 && numericDates: return NSLocalizedString("1 hour ago", comment: "1 hour ago")
case (_, _, _, _, let hour, _, _) where hour == 1 && !numericDates: return NSLocalizedString("An hour ago", comment: "An hour ago")
case (_, _, _, _, _, let minute, _) where minute >= 2: return NSLocalizedString("\(minute) minutes ago", comment: "\(minute) minutes ago")
case (_, _, _, _, _, let minute, _) where minute == 1 && numericDates: return NSLocalizedString("1 minute ago", comment: "1 minute ago")
case (_, _, _, _, _, let minute, _) where minute == 1 && !numericDates: return NSLocalizedString("A minute ago", comment: "A minute ago")
case (_, _, _, _, _, _, let second) where second >= 3: return NSLocalizedString("\(second) seconds ago", comment: "\(second) seconds ago")
default: return NSLocalizedString("Just now", comment: "Just now")
}
}
/// Create a short pretty formatted string of time passed based on the date components, ex: "2y", "2mo", "now".
/// - Returns: Short formatted string of time passed.
public func shortTimeAgo() -> 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 >= 1: return NSLocalizedString("\(year)y", comment: "\(year)y")
case (_, let month, _, _, _, _, _) where month >= 1: return NSLocalizedString("\(month)mo", comment: "\(month)mo")
case (_, _, let weekOfMonth, _, _, _, _) where weekOfMonth >= 1: return NSLocalizedString("\(weekOfMonth)w", comment: "\(weekOfMonth)w")
case (_, _, _, let day, _, _, _) where day >= 1: return NSLocalizedString("\(day)d", comment: "\(day)d")
case (_, _, _, _, let hour, _, _) where hour >= 1: return NSLocalizedString("\(hour)h", comment: "\(hour)h")
case (_, _, _, _, _, let minute, _) where minute >= 1: return NSLocalizedString("\(minute)m", comment: "\(minute)m")
case (_, _, _, _, _, _, let second) where second >= 0: return NSLocalizedString("\(second)s", comment: "\(second)s")
default: return NSLocalizedString("now", comment: "now")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment