123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- import Foundation
- /// Format and convert dates and time
- ///
- /// All methods are `static`, no initalization needed.
- ///
- /// Formatters are cached to improve performance. Call `forceReinitialize()` to reset them.
- ///
- /// - Note: All examples of the formatted strings reflect **February 1, 2020 at 1:14:15 PM GMT+1**
- public class DateFormatter: NSObject {
-
- // MARK: - Formats provided by the system
-
- /// Localized short date and time string
- ///
- /// Examples in multiple locales:
- /// - 2/1/20, 1:14 PM (en_US)
- /// - 01.02.20, 13:14 (de_DE)
- /// - 01.02.20 13:14 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short date and time string or empty string if `date` is nil
- @objc
- public static func shortStyleDateTime(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if shortDateTimeDateFormatter == nil {
- shortDateTimeDateFormatter = dateFormatterWith(date: .short, andTime: .short)
- }
-
- return shortDateTimeDateFormatter!.string(from: date)
- }
-
- /// Localized short date and medium time (with seconds) string
- ///
- /// Examples in multiple locales:
- /// - 2/1/20, 1:14:15 PM (en_US)
- /// - 01.02.20, 13:14:15 (de_DE)
- /// - 01.02.20 13:14:15 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short date and medium time (with seconds) string or empty string if `date` is nil
- @objc
- public static func shortStyleDateTimeSeconds(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if shortDateMediumTimeDateFormatter == nil {
- shortDateMediumTimeDateFormatter = dateFormatterWith(date: .short, andTime: .medium)
- }
-
- return shortDateMediumTimeDateFormatter!.string(from: date)
- }
-
- /// Localized medium date and time (with seconds) string
- ///
- /// Examples in multiple locales:
- /// - Feb 1, 2020 at 1:14:15 PM (en_US)
- /// - 01.02.2020, 13:14:15 (de_DE)
- /// - 1 févr. 2020 à 13:14:15 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized medium date and time (with seconds) string or empty string if `date` is nil
- @objc
- public static func mediumStyleDateTime(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if mediumDateTimeDateFormatter == nil {
- mediumDateTimeDateFormatter = dateFormatterWith(date: .medium, andTime: .medium)
- }
-
- return mediumDateTimeDateFormatter!.string(from: date)
- }
-
- /// Localized medium date and short time (no seconds) string
- ///
- /// Examples in multiple locales:
- /// - Feb 1, 2020 at 1:14 PM (en_US)
- /// - 01.02.2020, 13:14 (de_DE)
- /// - 1 févr. 2020 à 13:14 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized medium date and short time (no seconds) string
- public static func mediumStyleDateShortStyleTime(_ date: Date) -> String {
- if mediumDateShortTimeDateFormatter == nil {
- mediumDateShortTimeDateFormatter = dateFormatterWith(date: .medium, andTime: .short)
- }
-
- return mediumDateShortTimeDateFormatter!.string(from: date)
- }
-
- /// Localized long date and time (with time zone) string
- ///
- /// Examples in multiple locales:
- /// - February 1, 2020 at 1:14:15 PM GMT+1 (en_US)
- /// - 1. Februar 2020 um 13:14:15 MEZ (de_DE)
- /// - 1 février 2020 à 13:14:15 UTC+1 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized long date and time (with time zone) string or empty string if `date` is nil
- @objc
- public static func longStyleDateTime(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if longDateTimeDateFormatter == nil {
- longDateTimeDateFormatter = dateFormatterWith(date: .long, andTime: .long)
- }
-
- return longDateTimeDateFormatter!.string(from: date)
- }
-
- /// Localized short time (no seconds) string
- ///
- /// e.g. 1:14 PM or 13:14
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short time (no seconds) string or empty string if `date` is nil
- @objc
- public static func shortStyleTimeNoDate(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if shortTimeDateFormatter == nil {
- shortTimeDateFormatter = dateFormatterWith(date: .none, andTime: .short)
- }
-
- return shortTimeDateFormatter!.string(from: date)
- }
-
- /// Localized relative medium date string
- ///
- /// - Note: Marked as private, because it's only used internally
- ///
- /// Examples in multiple locales:
- /// - Today, Yesterday, .., Feb 1, 2020 at 1:14 PM (en_US)
- /// - Heute, Gestern, Vorgestern, ..., 01.02.2020, 13:14 (de_DE)
- /// - aujourd’hui, hier, avant-hier, ..., 1 févr. 2020 à 13:14 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized relative medium date string
- private static func realtiveMediumStyleDate(_ date: Date) -> String {
- if relativeMediumDateDateForamtter == nil {
- relativeMediumDateDateForamtter = dateFormatterWith(date: .medium, andTime: .none)
- relativeMediumDateDateForamtter?.doesRelativeDateFormatting = true
- }
-
- return relativeMediumDateDateForamtter!.string(from: date)
- }
-
-
- // MARK: - Custom formats
-
- /// Localized short day, month and year string
- ///
- /// Examples in multiple locales:
- /// - 2/1/2020 (en_US)
- /// - 1.2.2020 (de_DE)
- /// - 01.02.2020 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short day, month and year string or empty string if `date` is nil
- @objc
- public static func getShortDate(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if shortDayMonthAndYearDateFormatter == nil {
- shortDayMonthAndYearDateFormatter = dateFormatter(for: "d M y")
- }
-
- return shortDayMonthAndYearDateFormatter!.string(from: date)
- }
-
- /// Localized short weekday, medium day, medium month and long year string
- ///
- /// Examples in multiple locales:
- /// - Sat, Feb 01, 2020 (en_US)
- /// - Sa. 01. Feb. 2020 (de_DE)
- /// - sam. 01 févr. 2020 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short weekday, medium day, medium month and full year string or empty string if `date` is nil
- @objc
- public static func getDayMonthAndYear(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if mediumWeekdayDayMonthAndYearDateFormatter == nil {
- mediumWeekdayDayMonthAndYearDateFormatter = dateFormatter(for: "EE dd MMM yyyy")
- }
-
- return mediumWeekdayDayMonthAndYearDateFormatter!.string(from: date)
- }
-
- /// Localized short weekday, medium day and medium month
- ///
- /// - Note: Marked as private, because it's only used internally
- ///
- /// Examples in multiple locales:
- /// - Sat, Feb 01 (en_US)
- /// - Sa. 01. Feb. (de_DE)
- /// - sam. 01 févr. (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short weekday, medium day and medium month
- @objc
- private static func mediumWeekdayDayAndMonth(_ date: Date) -> String {
- if mediumWeekdayDayAndMonthDateFormatter == nil {
- mediumWeekdayDayAndMonthDateFormatter = dateFormatter(for: "EE dd MMM")
- }
-
- return mediumWeekdayDayAndMonthDateFormatter!.string(from: date)
- }
-
- /// Localized short weekday, medium day, medium month and long year string including short time
- ///
- /// Examples in multiple locales:
- /// - Sat, Feb 01, 2020, 1:14 PM (en_US)
- /// - Sa. 01. Feb. 2020, 13:14 (de_DE)
- /// - sam. 01 févr. 2020 à 13:14 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized short weekday, medium day, medium month and long year string including short time or empty string if `date` is nil
- @objc
- public static func getFullDate(for date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if mediumWeekdayDayMonthYearAndTimeDateFormatter == nil {
- mediumWeekdayDayMonthYearAndTimeDateFormatter = dateFormatter(for: "j:mm EE dd MMM yyyy")
- }
-
- return mediumWeekdayDayMonthYearAndTimeDateFormatter!.string(from: date)
- }
-
-
- // MARK: - To `Date` converter
-
- /// Convert localized date string into into `Date`
- ///
- /// This is the reverse function of `getDayMonthAndYear(_:)`
- ///
- /// Examples for localized inputs:
- /// - Sat, Feb 01, 2020 (en_US)
- /// - Sa. 01. Feb. 2020 (de_DE)
- /// - sam. 01 févr. 2020 (fr_CH)
- ///
- /// - Parameter dateString: Date string in current locale
- /// - Returns: Parsed date or nil if parsing failed
- @objc
- public static func getDateFromDayMonthAndYearDateString(_ dateString: String) -> Date? {
- let setMediumWeekdayDayMonthAndYearDateFormatter = { mediumWeekdayDayMonthAndYearDateFormatter = dateFormatter(for: "EE dd MMM yyyy") }
-
- if mediumWeekdayDayMonthAndYearDateFormatter == nil {
- setMediumWeekdayDayMonthAndYearDateFormatter()
- }
-
- if let date = mediumWeekdayDayMonthAndYearDateFormatter!.date(from: dateString) {
- return date
- }
-
- // Try to recover from locale change by resetting the formatter
- locale = Locale.current
- setMediumWeekdayDayMonthAndYearDateFormatter()
-
- if let date = mediumWeekdayDayMonthAndYearDateFormatter!.date(from: dateString) {
- return date
- }
-
- return nil
- }
-
- /// Convert localized date and time string into `Date`
- ///
- /// This is the reverse function of `getFullDate(for:)`
- ///
- /// Examples for localized inputs:
- /// - Sat, Feb 01, 2020, 1:14 PM (en_US)
- /// - Sa. 01. Feb. 2020, 13:14 (de_DE)
- /// - sam. 01 févr. 2020 à 13:14 (fr_CH)
- ///
- /// - Parameter dateString: Date string with time in current locale
- /// - Returns: Parsed date or nil if parsing failed
- @objc
- public static func getDateFromFullDateString(_ dateString: String) -> Date? {
- let setMediumWeekdayDayMonthYearAndTimeDateFormatter = { mediumWeekdayDayMonthYearAndTimeDateFormatter = dateFormatter(for: "j:mm EE dd MMM yyyy") }
-
- if mediumWeekdayDayMonthYearAndTimeDateFormatter == nil {
- setMediumWeekdayDayMonthYearAndTimeDateFormatter()
- }
-
- if let date = mediumWeekdayDayMonthYearAndTimeDateFormatter!.date(from: dateString) {
- return date
- }
-
- // Try to recover from locale change by resetting the formatter
- locale = Locale.current
- setMediumWeekdayDayMonthYearAndTimeDateFormatter()
-
- if let date = mediumWeekdayDayMonthYearAndTimeDateFormatter!.date(from: dateString) {
- return date
- }
-
- return nil
- }
-
- // MARK: - Relative custom formats
-
- /// Localized realtive date
- ///
- /// Localized text for today and yesterday, weekday, day and month for the rest of this calendar year. For previous years it also shows the year.
- ///
- /// Examples in multiple locales:
- /// - Today, Yesterday, Sat, Feb 01, ..., Tue, Dec 31, 2019, Sat, Feb 01 2019 (en_US)
- /// - Heute, Gestern, Sa. 01. Feb., ..., Di. 31. Dez. 2019, Sa. 01. Feb. 2019 (de_DE)
- /// - aujourd’hui, hier, sam. 01 févr., ..., mar. 31 déc. 2019, sam. 01 févr. 2019 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized realtive date or empty string if `date` is nil
- @objc
- public static func relativeMediumDate(for date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if isDateInTodayOrYesterday(date) {
- return realtiveMediumStyleDate(date)
- } else if isDateInThisCalendarYear(date) {
- return mediumWeekdayDayAndMonth(date)
- } else {
- return getDayMonthAndYear(date)
- }
- }
-
- // MARK: - Accessibility formats
-
- /// Localized date and time for accessibility
- ///
- /// Examples in multiple locales:
- /// - February 1, 2020, 1:14 PM (en_US)
- /// - 1. Februar 2020, 13:14 (de_DE)
- /// - 1 février 2020 à 13:14 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized date and time for accessibility or empty string if `date` is nil
- @objc
- public static func accessibilityDateTime(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if accessibilityDateTimeDateFormatter == nil {
- accessibilityDateTimeDateFormatter = dateFormatter(for: "j:mm d MMMM yyyy")
- }
-
- return accessibilityDateTimeDateFormatter!.string(from: date)
- }
-
- /// Localized date and time for accessibility using relative dates for recent days (e.g. today, yesterday)
- ///
- /// Examples in multiple locales:
- /// - February 1, 2020 at 1:14 PM (en_US)
- /// - 1. Februar 2020 um 13:14 (de_DE)
- /// - 1 février 2020 à 13:14 (fr_CH)
- ///
- /// - Parameter date: Date to format
- /// - Returns: Localized date and time for accessibility using relative dates for recent days (e.g. today, yesterday) or empty string if `date` is nil
- @objc
- public static func accessibilityRelativeDayTime(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if accessibilityRelativeDateTimeDateFormatter == nil {
- accessibilityRelativeDateTimeDateFormatter = dateFormatterWith(date: .long, andTime: .short)
- accessibilityRelativeDateTimeDateFormatter?.doesRelativeDateFormatting = true
- }
-
- return accessibilityRelativeDateTimeDateFormatter!.string(from: date)
- }
-
- // MARK: - Locale independent time formatter
-
- /// Date independent of locale
- ///
- /// Example: 20200102-131415
- ///
- /// - Parameter date: Date to format
- /// - Returns: Formated date or empty string if `date` is nil
- @objc
- public static func getDateForWeb(_ date: Date?) -> String {
- guard let date = date else {
- return ""
- }
-
- if webDateFormatter == nil {
- webDateFormatter = Foundation.DateFormatter()
- // Always ue this locale for locale independent formats (see https://nsdateformatter.com)
- webDateFormatter?.locale = Locale(identifier: "en_US_POSIX")
- webDateFormatter?.dateFormat = "yyyyddMM-HHmmss"
- }
-
- return webDateFormatter!.string(from: date)
- }
-
- @objc
- public static func getNowDateString() -> String {
- if nowDateFormatter == nil {
- nowDateFormatter = Foundation.DateFormatter()
- webDateFormatter?.locale = Locale(identifier: "en_US_POSIX")
- webDateFormatter?.dateFormat = "yyyyMMddHHmm"
- }
-
- return nowDateFormatter!.string(from: Date())
- }
-
- // MARK: - Time conversion
-
- /// Format seconds into time string
- ///
- /// This might be replaced by `DateComponentsFormatter` in the future for better localization. It requires that
- /// `totalSeconds` is not required as an inverse function.
- ///
- /// - Parameter totalSeconds: Seconds to transform
- /// - Returns: String of format "01:02:03" with hour obmitted if it's zero
- @objc
- public static func timeFormatted(_ totalSeconds: Int) -> String {
- let seconds = totalSeconds % 60
- let minutes = (totalSeconds / 60) % 60
- let hours = totalSeconds / 60 / 60
-
- if hours == 0 {
- return String(format: "%02d:%02d", minutes, seconds)
- } else {
- return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
- }
- }
-
- /// Converts time string into seconds
- ///
- /// - Parameter timeFormatted: Time string with format "01:02:03" where the hour can be obmitted
- /// - Returns: Number of seconds
- public static func totalSeconds(_ timeFormatted: String) -> Int {
- // Convert components to `Int` or set to 0 otherwise
- let components: [Int] = timeFormatted.split(separator: ":").map { Int($0) ?? 0 }
-
- var seconds = 0
- var minutes = 0
- var hours = 0
-
- switch components.count {
- case 1:
- seconds = components.first!
- case 2:
- seconds = components[components.endIndex - 1]
- minutes = components[components.endIndex - 2]
- case 3:
- seconds = components[components.endIndex - 1]
- minutes = components[components.endIndex - 2]
- hours = components[components.endIndex - 3]
- default:
- return 0
- }
-
- return seconds + (minutes * 60) + (hours * 60 * 60)
- }
-
- // MARK: - Caching
-
- // Does it still make sense to cache formatters in 2020?
- // Probably, yes (http://jordansmith.io/performant-date-parsing/).
-
- /// Reset all cached date formatters and reset to current locale
- public static func forceReinitialize() {
- shortDateTimeDateFormatter = nil
- shortDateMediumTimeDateFormatter = nil
- mediumDateTimeDateFormatter = nil
- mediumDateShortTimeDateFormatter = nil
- longDateTimeDateFormatter = nil
- shortTimeDateFormatter = nil
- relativeMediumDateDateForamtter = nil
-
- shortDayMonthAndYearDateFormatter = nil
- mediumWeekdayDayMonthAndYearDateFormatter = nil
- mediumWeekdayDayAndMonthDateFormatter = nil
- longWeekdayDayMonthAndYearDateFormatter = nil
- mediumWeekdayDayMonthYearAndTimeDateFormatter = nil
-
- accessibilityDateTimeDateFormatter = nil
- accessibilityRelativeDateTimeDateFormatter = nil
-
- webDateFormatter = nil
-
- locale = Locale.current
- }
-
- // Note: If you add a new property reset it in `foreReinitalize()`.
-
- private static var shortDateTimeDateFormatter: Foundation.DateFormatter?
- private static var shortDateMediumTimeDateFormatter: Foundation.DateFormatter?
- private static var mediumDateTimeDateFormatter: Foundation.DateFormatter?
- private static var mediumDateShortTimeDateFormatter: Foundation.DateFormatter?
- private static var longDateTimeDateFormatter: Foundation.DateFormatter?
- private static var shortTimeDateFormatter: Foundation.DateFormatter?
- private static var relativeMediumDateDateForamtter: Foundation.DateFormatter?
-
- private static var shortDayMonthAndYearDateFormatter: Foundation.DateFormatter?
- private static var mediumWeekdayDayMonthAndYearDateFormatter: Foundation.DateFormatter?
- private static var mediumWeekdayDayAndMonthDateFormatter: Foundation.DateFormatter?
- private static var longWeekdayDayMonthAndYearDateFormatter: Foundation.DateFormatter?
- private static var mediumWeekdayDayMonthYearAndTimeDateFormatter: Foundation.DateFormatter?
-
- private static var accessibilityDateTimeDateFormatter: Foundation.DateFormatter?
- private static var accessibilityRelativeDateTimeDateFormatter: Foundation.DateFormatter?
-
- private static var webDateFormatter: Foundation.DateFormatter?
- private static var nowDateFormatter: Foundation.DateFormatter?
-
- // MARK: - Private helper functions
-
- private static func dateFormatterWith(date dateStyle: Foundation.DateFormatter.Style, andTime timeStyle: Foundation.DateFormatter.Style) -> Foundation.DateFormatter {
-
- let dateFormatter = Foundation.DateFormatter()
- dateFormatter.locale = DateFormatter.locale
-
- dateFormatter.dateStyle = dateStyle
- dateFormatter.timeStyle = timeStyle
-
- return dateFormatter
- }
-
- private static func dateFormatter(for format: String) -> Foundation.DateFormatter {
-
- let dateFormatter = Foundation.DateFormatter()
- dateFormatter.locale = DateFormatter.locale
-
- dateFormatter.setLocalizedDateFormatFromTemplate(format)
-
- return dateFormatter
- }
-
- // MARK: - Private realtive date helper
-
- /// Checks if `date` is in today or yesterday
- ///
- /// - Parameter date: Date to check
- /// - Returns: `True` if the date is in today or yesterday, `False` otherwise
- private static func isDateInTodayOrYesterday(_ date: Date) -> Bool {
- Calendar.current.isDateInToday(date) || Calendar.current.isDateInYesterday(date)
- }
-
- /// Checks if `date` is in this calendar year
- ///
- /// - Parameter date: Date to check
- /// - Returns: `True` if the date is in this calenadar year, `False` otherwise
- private static func isDateInThisCalendarYear(_ date: Date) -> Bool {
- var dateComponents = Calendar.current.dateComponents([.year], from: Date())
-
- dateComponents.second = -1
-
- guard let lastNewYearsEveJustBeforeMidnight = Calendar.current.date(from: dateComponents) else {
- return false
- }
-
- return date > lastNewYearsEveJustBeforeMidnight
- }
-
- /// Checks if `date` is younger than one year
- ///
- /// - Parameter date: Date to check
- /// - Returns: `False` if the date is a year ago or older, or not determable in the current calendar, otherwise `True`
- private static func isDateInLastYear(_ date: Date) -> Bool {
- var dateComponents = Calendar.current.dateComponents([.year, .month, .day], from: Date())
-
- guard let yearComponent = dateComponents.year else {
- return false
- }
-
- dateComponents.year = yearComponent - 1
-
- guard let dayComponent = dateComponents.day else {
- return false
- }
-
- dateComponents.day = dayComponent + 1
-
- guard let aYearAgoMidnight = Calendar.current.date(from: dateComponents) else {
- return false
- }
-
- return date > aYearAgoMidnight
- }
-
- /// Checks if `date` is in last six days
- ///
- /// i.e. if today is _Wednesday_ this function returns `true` for all dates up to and including last _Thursday_
- ///
- /// - Parameter date: Date to check
- /// - Returns: `False` if the date is in last 6 days, or not determable in the current calendar, otherwise `True`
- private static func isDateInLastSixDays(_ date: Date) -> Bool {
- var dateComponents = Calendar.current.dateComponents([.year, .month, .day], from: Date())
-
- guard let dayComponent = dateComponents.day else {
- return false
- }
-
- dateComponents.day = dayComponent - 6
-
- guard let aSevenDaysAgoMidnight = Calendar.current.date(from: dateComponents) else {
- return false
- }
-
- return date > aSevenDaysAgoMidnight
- }
-
- // MARK: - Helper for testing
-
- /// Only reassign this value for testing
- static var locale = Locale.current
- }
|