Skip to content

Instantly share code, notes, and snippets.

@kunikullaya
Created January 18, 2017 05:21
Show Gist options
  • Save kunikullaya/6474fc6537ed616b1c617646d263555d to your computer and use it in GitHub Desktop.
Save kunikullaya/6474fc6537ed616b1c617646d263555d to your computer and use it in GitHub Desktop.
Date Extension for Swift
import Foundation
extension Date {
func toString(format: String = "yyyy-MM-dd") -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.dateFormat = format
return formatter.string(from: self)
}
func dateAndTimetoString(format: String = "yyyy-MM-dd HH:mm") -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.dateFormat = format
return formatter.string(from: self)
}
func timeIn24HourFormat() -> String {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.dateFormat = "HH:mm"
return formatter.string(from: self)
}
func startOfMonth() -> Date {
var components = Calendar.current.dateComponents([.year,.month], from: self)
components.day = 1
let firstDateOfMonth: Date = Calendar.current.date(from: components)!
return firstDateOfMonth
}
func endOfMonth() -> Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
}
func nextDate() -> Date {
let nextDate = Calendar.current.date(byAdding: .day, value: 1, to: self)
return nextDate ?? Date()
}
func previousDate() -> Date {
let previousDate = Calendar.current.date(byAdding: .day, value: -1, to: self)
return previousDate ?? Date()
}
func addMonths(numberOfMonths: Int) -> Date {
let endDate = Calendar.current.date(byAdding: .month, value: numberOfMonths, to: self)
return endDate ?? Date()
}
func removeMonths(numberOfMonths: Int) -> Date {
let endDate = Calendar.current.date(byAdding: .month, value: -numberOfMonths, to: self)
return endDate ?? Date()
}
func removeYears(numberOfYears: Int) -> Date {
let endDate = Calendar.current.date(byAdding: .year, value: -numberOfYears, to: self)
return endDate ?? Date()
}
func getHumanReadableDayString() -> String {
let weekdays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
]
let calendar = Calendar.current.component(.weekday, from: self)
return weekdays[calendar - 1]
}
func timeSinceDate(fromDate: Date) -> String {
let earliest = self < fromDate ? self : fromDate
let latest = (earliest == self) ? fromDate : self
let components:DateComponents = Calendar.current.dateComponents([.minute,.hour,.day,.weekOfYear,.month,.year,.second], from: earliest, to: latest)
let year = components.year ?? 0
let month = components.month ?? 0
let week = components.weekOfYear ?? 0
let day = components.day ?? 0
let hours = components.hour ?? 0
let minutes = components.minute ?? 0
let seconds = components.second ?? 0
if year >= 2{
return "\(year) years ago"
}else if (year >= 1){
return "1 year ago"
}else if (month >= 2) {
return "\(month) months ago"
}else if (month >= 1) {
return "1 month ago"
}else if (week >= 2) {
return "\(week) weeks ago"
} else if (week >= 1){
return "1 week ago"
} else if (day >= 2) {
return "\(day) days ago"
} else if (day >= 1){
return "1 day ago"
} else if (hours >= 2) {
return "\(hours) hours ago"
} else if (hours >= 1){
return "1 hour ago"
} else if (minutes >= 2) {
return "\(minutes) minutes ago"
} else if (minutes >= 1){
return "1 minute ago"
} else if (seconds >= 3) {
return "\(seconds) seconds ago"
} else {
return "Just now"
}
}
}
@23inhouse
Copy link

Thanks for posting this here! I used the timeSinceDate function. Here is my refactored version:

extension Date {
    func time(since fromDate: Date) -> String {
        let earliest = self < fromDate ? self : fromDate
        let latest = (earliest == self) ? fromDate : self

        let allComponents: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfYear, .month, .year, .second]
        let components:DateComponents = Calendar.current.dateComponents(allComponents, from: earliest, to: latest)
        let year = components.year  ?? 0
        let month = components.month  ?? 0
        let week = components.weekOfYear  ?? 0
        let day = components.day ?? 0
        let hour = components.hour ?? 0
        let minute = components.minute ?? 0
        let second = components.second ?? 0

        let descendingComponents = ["year": year, "month": month, "week": week, "day": day, "hour": hour, "minute": minute, "second": second]
        for (period, timeAgo) in descendingComponents {
            if timeAgo > 0 {
                return "\(timeAgo.of(period)) ago"
            }
        }

        return "Just now"
    }
}

extension Int {
    func of(_ name: String) -> String {
        guard self != 1 else { return "\(self) \(name)" }
        return "\(self) \(name)s"
    }
}

@23inhouse
Copy link

23inhouse commented Jul 1, 2019

Swift's Dictionaries are not ordered - hey hey

class DateTests: XCTestCase {
    func testBackToTheFuture() {
        let date = Date(timeIntervalSinceNow: 1)
        XCTAssertEqual(Date.time(since: date), "Back to the future", "Wrong time ago")
    }

    func testTimeSinceSeconds() {
        let expectations: [(Int, String)] = [
            (3, "3 seconds ago"),
            (3 * 60, "3 minutes ago"),
            (3 * 60 * 60, "3 hours ago"),
            (3 * 60 * 60 * 24, "3 days ago"),
            (3 * 60 * 60 * 24 * 7, "3 weeks ago"),
            (3 * 60 * 60 * 24 * 31, "3 months ago"),
            (3 * 60 * 60 * 24 * 366, "3 years ago"),
        ]

        for expectation in expectations {
            let (seconds, expected) = expectation
            let date = Date(timeIntervalSinceNow: Double(-seconds))
            XCTAssertEqual(Date.time(since: date), expected, "Wrong time ago")
        }
    }
}

extension Date {
    static func time(since fromDate: Date) -> String {
        guard fromDate < Date() else { return "Back to the future" }

        let allComponents: Set<Calendar.Component> = [.second, .minute, .hour, .day, .weekOfYear, .month, .year]
        let components:DateComponents = Calendar.current.dateComponents(allComponents, from: fromDate, to: Date())

        for (period, timeAgo) in [
            ("year", components.year ?? 0),
            ("month", components.month ?? 0),
            ("week", components.weekOfYear ?? 0),
            ("day", components.day ?? 0),
            ("hour", components.hour ?? 0),
            ("minute", components.minute ?? 0),
            ("second", components.second ?? 0),
            ] {
                if timeAgo > 0 {
                    return "\(timeAgo.of(period)) ago"
                }
        }

        return "Just now"
    }
}

@kunikullaya
Copy link
Author

Swift's Dictionaries are not ordered - hey hey

https://swiftdoc.org/v3.1/type/dictionaryliteral/

@NikKovIos
Copy link

A little swiftier:

var nextDay: Date {
        let nextDate = Calendar.current.date(byAdding: .day, value: 1, to: self)
        return nextDate ?? Date()
    }

    var previousDay: Date {
        let previousDate = Calendar.current.date(byAdding: .day, value: -1, to: self)
        return previousDate ?? Date()
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment