InvestmentTrackerApp/PortfolioJournal/Utilities/Extensions/Date+Extensions.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
}
}