Swift - проверьте, есть ли отметка времени вчера, сегодня, завтра или X дней назад

Я пытаюсь решить, как решить, существует ли данная временная метка сегодня, или +1 / -1 дней. По сути, я хотел бы сделать что-то вроде этого (Pseudocode)

IF days_from_today(timestamp) == -1 RETURN 'Yesterday'
ELSE IF days_from_today(timestamp) == 0 RETURN 'Today'
ELSE IF days_from_today(timestamp) == 1 RETURN 'Tomorrow'
ELSE IF days_from_today(timestamp) < 1 RETURN days_from_today(timestamp) + ' days ago'
ELSE RETURN 'In ' + days_from_today(timestamp) + ' ago'

Тем не менее, важно, чтобы это было в Swift, и я борюсь с объектами NSDate/NSCalendar. Я начал с разработки разницы во времени следующим образом:

let calendar = NSCalendar.currentCalendar()
let date = NSDate(timeIntervalSince1970: Double(timestamp))
let timeDifference = calendar.components([.Second,.Minute,.Day,.Hour],
    fromDate: date, toDate: NSDate(), options: NSCalendarOptions())

Однако сравнивать это непросто, потому что .Day отличается в зависимости от времени суток и временной метки. В PHP я просто использовал mktime для создания новой даты, основанной на начале дня (например, mktime(0,0,0)), но я не уверен в том, что это самый простой способ сделать это в Swift.

У кого-нибудь есть хорошая идея о том, как подойти к этому? Может быть, расширение NSDate или что-то подобное было бы лучше?

Ответ 1

Calendar имеет методы для всех трех случаев

func isDateInYesterday(_ date: Date) -> Bool
func isDateInToday(_ date: Date) -> Bool
func isDateInTomorrow(_ date: Date) -> Bool

Чтобы рассчитать дни раньше, чем вчера, используйте

func dateComponents(_ components: Set<Calendar.Component>, 
                      from start: Date, 
                          to end: Date) -> DateComponents

передайте [.day] в components и получите свойство day из результата.


Эта функция также рассматривает is in для более ранних и более поздних дат, удаляя временную часть (Swift 3+).

func dayDifference(from interval : TimeInterval) -> String
{
    let calendar = Calendar.current
    let date = Date(timeIntervalSince1970: interval)
    if calendar.isDateInYesterday(date) { return "Yesterday" }
    else if calendar.isDateInToday(date) { return "Today" }
    else if calendar.isDateInTomorrow(date) { return "Tomorrow" }
    else {
        let startOfNow = calendar.startOfDay(for: Date())
        let startOfTimeStamp = calendar.startOfDay(for: date)
        let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
        let day = components.day!
        if day < 1 { return "\(-day) days ago" }
        else { return "In \(day) days" }
    }
}

В качестве альтернативы вы можете использовать DateFormatter для вчера, сегодня и завтра, чтобы бесплатно получить локализованные строки

func dayDifference(from interval : TimeInterval) -> String
{
    let calendar = Calendar.current
    let date = Date(timeIntervalSince1970: interval)
    let startOfNow = calendar.startOfDay(for: Date())
    let startOfTimeStamp = calendar.startOfDay(for: date)
    let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
    let day = components.day!
    if abs(day) < 2 {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .none
        formatter.doesRelativeDateFormatting = true
        return formatter.string(from: date)
    } else if day > 1 {
        return "In \(day) days"
    } else {
        return "\(-day) days ago"
    }
}

Обновление:

В macOS 10.15/iOS 13 была введена RelativeDateTimeFormatter для возврата (локализованных) строк относительно определенной даты.

Ответ 2

Свифт 3/4/5:

Calendar.current.isDateInToday(yourDate)
Calendar.current.isDateInYesterday(yourDate)
Calendar.current.isDateInTomorrow(yourDate)

Дополнительно:

Calendar.current.isDateInWeekend(yourDate)

Обратите внимание, что для некоторых стран выходные дни могут отличаться от субботы-воскресенья, это зависит от календаря.

Вы также можете использовать autoupdatingCurrent вместо current календаря, который будет отслеживать обновления пользователя. Вы используете это так же:

Calendar.autoupdatingCurrent.isDateInToday(yourDate)

Calendar является псевдонимом типа для NSCalendar.

Ответ 3

Обновление Swift 4:

    let calendar = Calendar.current
    let date = Date()

    calendar.isDateInYesterday(date)
    calendar.isDateInToday(date)
    calendar.isDateInTomorrow(date)

Ответ 4

NSCalender имеет новые методы, которые вы можете использовать напрямую.

NSCalendar.currentCalendar().isDateInTomorrow(NSDate())//Replace NSDate() with your date
NSCalendar.currentCalendar().isDateInYesterday()
NSCalendar.currentCalendar().isDateInTomorrow()

Надеюсь это поможет

Ответ 5

В Swift 5 и iOS 13 используйте RelativeDateTimeFormatter,

let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named 

formatter.localizedString(from: DateComponents(day: -1)) // "yesterday"
formatter.localizedString(from: DateComponents(day: 1)) // "Tomorrow"
formatter.localizedString(from: DateComponents(hour: 2)) // "in 2 hours"
formatter.localizedString(from: DateComponents(minute: 45)) // "in 45 minutes"

Ответ 6

1) Согласно вашему примеру вы хотите получать ярлыки "Вчера", "Сегодня" и т.д. IOS может делать это по умолчанию:

https://developer.apple.com/documentation/foundation/nsdateformatter/1415848-doesrelativedateformatting?language=objc

2) Если вы хотите вычислить свою пользовательскую метку, когда iOS не добавляет эти метки сама по себе, то в качестве альтернативы вы можете использовать 2 объекта DateFormatter имеют оба средства: doesRelativeDateFormatting == true и doesRelativeDateFormatting == false и сравнить, doesRelativeDateFormatting == false ли их строки даты результата или разные