200 lines
5.5 KiB
Swift
200 lines
5.5 KiB
Swift
import Foundation
|
|
|
|
extension Date {
|
|
// MARK: - Formatting
|
|
|
|
var shortDateString: String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .short
|
|
formatter.timeStyle = .none
|
|
return formatter.string(from: self)
|
|
}
|
|
|
|
var mediumDateString: String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .medium
|
|
formatter.timeStyle = .none
|
|
return formatter.string(from: self)
|
|
}
|
|
|
|
var longDateString: String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .long
|
|
formatter.timeStyle = .none
|
|
return formatter.string(from: self)
|
|
}
|
|
|
|
var monthYearString: String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = "MMM yyyy"
|
|
return formatter.string(from: self)
|
|
}
|
|
|
|
var yearString: String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = "yyyy"
|
|
return formatter.string(from: self)
|
|
}
|
|
|
|
var iso8601String: String {
|
|
ISO8601DateFormatter().string(from: self)
|
|
}
|
|
|
|
// MARK: - Components
|
|
|
|
var startOfDay: Date {
|
|
Calendar.current.startOfDay(for: self)
|
|
}
|
|
|
|
var startOfMonth: Date {
|
|
let components = Calendar.current.dateComponents([.year, .month], from: self)
|
|
return Calendar.current.date(from: components) ?? self
|
|
}
|
|
|
|
var startOfYear: Date {
|
|
let components = Calendar.current.dateComponents([.year], from: self)
|
|
return Calendar.current.date(from: components) ?? self
|
|
}
|
|
|
|
var endOfMonth: Date {
|
|
let startOfNextMonth = Calendar.current.date(
|
|
byAdding: .month,
|
|
value: 1,
|
|
to: startOfMonth
|
|
) ?? self
|
|
return Calendar.current.date(byAdding: .day, value: -1, to: startOfNextMonth) ?? self
|
|
}
|
|
|
|
// MARK: - Comparisons
|
|
|
|
func isSameDay(as other: Date) -> Bool {
|
|
Calendar.current.isDate(self, inSameDayAs: other)
|
|
}
|
|
|
|
func isSameMonth(as other: Date) -> Bool {
|
|
let selfComponents = Calendar.current.dateComponents([.year, .month], from: self)
|
|
let otherComponents = Calendar.current.dateComponents([.year, .month], from: other)
|
|
return selfComponents.year == otherComponents.year &&
|
|
selfComponents.month == otherComponents.month
|
|
}
|
|
|
|
func isSameYear(as other: Date) -> Bool {
|
|
Calendar.current.component(.year, from: self) ==
|
|
Calendar.current.component(.year, from: other)
|
|
}
|
|
|
|
var isToday: Bool {
|
|
Calendar.current.isDateInToday(self)
|
|
}
|
|
|
|
var isYesterday: Bool {
|
|
Calendar.current.isDateInYesterday(self)
|
|
}
|
|
|
|
var isThisMonth: Bool {
|
|
isSameMonth(as: Date())
|
|
}
|
|
|
|
var isThisYear: Bool {
|
|
isSameYear(as: Date())
|
|
}
|
|
|
|
// MARK: - Calculations
|
|
|
|
func adding(days: Int) -> Date {
|
|
Calendar.current.date(byAdding: .day, value: days, to: self) ?? self
|
|
}
|
|
|
|
func adding(months: Int) -> Date {
|
|
Calendar.current.date(byAdding: .month, value: months, to: self) ?? self
|
|
}
|
|
|
|
func adding(years: Int) -> Date {
|
|
Calendar.current.date(byAdding: .year, value: years, to: self) ?? self
|
|
}
|
|
|
|
func monthsBetween(_ other: Date) -> Int {
|
|
let components = Calendar.current.dateComponents([.month], from: self, to: other)
|
|
return components.month ?? 0
|
|
}
|
|
|
|
func daysBetween(_ other: Date) -> Int {
|
|
let components = Calendar.current.dateComponents([.day], from: self, to: other)
|
|
return components.day ?? 0
|
|
}
|
|
|
|
func yearsBetween(_ other: Date) -> Double {
|
|
let days = Double(daysBetween(other))
|
|
return days / 365.25
|
|
}
|
|
|
|
// MARK: - Relative Description
|
|
|
|
var relativeDescription: String {
|
|
let formatter = RelativeDateTimeFormatter()
|
|
formatter.unitsStyle = .abbreviated
|
|
return formatter.localizedString(for: self, relativeTo: Date())
|
|
}
|
|
|
|
var friendlyDescription: String {
|
|
if isToday {
|
|
return String(localized: "date_today")
|
|
} else if isYesterday {
|
|
return String(localized: "date_yesterday")
|
|
} else if isThisMonth {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = "EEEE, d"
|
|
return formatter.string(from: self)
|
|
} else if isThisYear {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = "MMM d"
|
|
return formatter.string(from: self)
|
|
} else {
|
|
return mediumDateString
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Date Range
|
|
|
|
struct DateRange {
|
|
let start: Date
|
|
let end: Date
|
|
|
|
static var thisMonth: DateRange {
|
|
let now = Date()
|
|
return DateRange(start: now.startOfMonth, end: now)
|
|
}
|
|
|
|
static var lastMonth: DateRange {
|
|
let now = Date()
|
|
let lastMonth = now.adding(months: -1)
|
|
return DateRange(start: lastMonth.startOfMonth, end: lastMonth.endOfMonth)
|
|
}
|
|
|
|
static var thisYear: DateRange {
|
|
let now = Date()
|
|
return DateRange(start: now.startOfYear, end: now)
|
|
}
|
|
|
|
static var lastYear: DateRange {
|
|
let now = Date()
|
|
let lastYear = now.adding(years: -1)
|
|
return DateRange(start: lastYear.startOfYear, end: now.startOfYear.adding(days: -1))
|
|
}
|
|
|
|
static func month(containing date: Date) -> DateRange {
|
|
DateRange(start: date.startOfMonth, end: date.endOfMonth)
|
|
}
|
|
|
|
static func last(months: Int) -> DateRange {
|
|
let now = Date()
|
|
let start = now.adding(months: -months)
|
|
return DateRange(start: start, end: now)
|
|
}
|
|
|
|
func contains(_ date: Date) -> Bool {
|
|
date >= start && date <= end
|
|
}
|
|
}
|