NSDate Сравнение с использованием Swift

153

Я работаю над приложением, требующим проверки срока выполнения домашней работы. Я хочу знать, наступит ли срок оплаты в течение следующей недели, и если это так, то выполнить действие.
Большая часть документации, которую я смог найти, находится в Objective-C, и я не могу понять, как это сделать в Swift. Спасибо за помощь!!

Генри Осканнлен-Миллер
источник
2
У swift нет класса даты, в котором вы используете класс Objective C NSDate - поэтому вы нашли правильную документацию
мммммм
Возможный дубликат сравнения NSDates без компонента времени . Есть много очень хороших ответов.
jww
Связанный ответ: stackoverflow.com/questions/29652771/…
jkdev
2
Swift 3 имеет Dateкласс. Он соединен с NSDate, но это называется Date.
BallpointBen

Ответы:

188

Мне нравится использовать расширения, чтобы сделать код более читабельным. Вот несколько расширений NSDate, которые помогут очистить ваш код и упростить его понимание. Я поместил это в файл sharedCode.swift:

extension NSDate {

    func isGreaterThanDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isGreater = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedDescending {
            isGreater = true
        }

        //Return Result
        return isGreater
    }

    func isLessThanDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isLess = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedAscending {
            isLess = true
        }

        //Return Result
        return isLess
    }

    func equalToDate(dateToCompare: NSDate) -> Bool {
        //Declare Variables
        var isEqualTo = false

        //Compare Values
        if self.compare(dateToCompare as Date) == ComparisonResult.orderedSame {
            isEqualTo = true
        }

        //Return Result
        return isEqualTo
    }

    func addDays(daysToAdd: Int) -> NSDate {
        let secondsInDays: TimeInterval = Double(daysToAdd) * 60 * 60 * 24
        let dateWithDaysAdded: NSDate = self.addingTimeInterval(secondsInDays)

        //Return Result
        return dateWithDaysAdded
    }

    func addHours(hoursToAdd: Int) -> NSDate {
        let secondsInHours: TimeInterval = Double(hoursToAdd) * 60 * 60
        let dateWithHoursAdded: NSDate = self.addingTimeInterval(secondsInHours)

        //Return Result
        return dateWithHoursAdded
    }
}

Теперь, если вы можете сделать что-то вроде этого:

//Get Current Date/Time
var currentDateTime = NSDate()

//Get Reminder Date (which is Due date minus 7 days lets say)
var reminderDate = dueDate.addDays(-7)

//Check if reminderDate is Greater than Right now
if(reminderDate.isGreaterThanDate(currentDateTime)) {
    //Do Something...
}
user2266987
источник
28
Вы должны упростить свой код. return self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
Олав Гаусакер,
5
isEqualToDate также предоставляется Apple. Это объявление противоречит тому, что определено Apple.
Shamas S - Восстановить Монику
4
Не каждый день имеет 24 часа
Лев Дабус
9
Этот ответ ужасен и никогда не должен быть принятым. Не когда - либо добавить временные интервалы дат, которые созданы вами. Именно поэтому и NSDateComponentsсуществует. Есть много крайних случаев, которые не обрабатываются должным образом, и нет смысла не добавлять к ним Comparableсоответствие NSDate. Я бы порекомендовал использовать решение Джона .
fpg1503
3
Лучшее решение - сделать NSDate сопоставимым, сопоставимым, тогда вы можете просто это сделатьdate1 < date2
aryaxt
209

Если вы хотите поддержать ==, <, >, <=, или >=для NSDateс, вы просто должны заявить об этом где - то:

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}

extension NSDate: Comparable { }
Джон Эстропия
источник
2
@Isuru Comparableявляется потомком Equatableпротокола, поэтому вам не нужно декларировать соответствие обоим.
Джон Эстропия
2
Просто любопытно, почему он не встроен по умолчанию ?!
dVaffection
3
@dVaffection В Objective-C (где объявлены NSDate и друзья), если вы сравните , используя ==, <, >и т.д., вы будете получать результат сравнения их адресов в памяти, а не сравнение их реальной стоимости. В Swift они по-прежнему рассматриваются как ссылки, поэтому я думаю, что выбор был либо (1) сохранять сравнения по указателю, как в ObjC, либо (2) устранять путаницу, не предоставляя реализацию для сравнений.
Джон Эстропия
2
Дополнительным преимуществом этого подхода является то Array.maxElement(), что и т. Д. Затем автоматически доступны массивы NSDates.
pr1001
1
@MarcioCruz Это всего лишь требование Swift, чтобы все реализации операторов были в глобальном масштабе. Смотрите обсуждение здесь: stackoverflow.com/questions/35246003/…
Джон Эстропия
54

Вот как вы сравниваете две NSDates в Swift, я только что протестировал их на игровой площадке XCode:

if date1.compare(date2) == NSComparisonResult.OrderedDescending
{
    NSLog("date1 after date2");
} else if date1.compare(date2) == NSComparisonResult.OrderedAscending
{
    NSLog("date1 before date2");
} else
{
    NSLog("dates are equal");
}

Итак, чтобы проверить, будет ли дата dueDateв течение недели с этого момента :

let dueDate=...

let calendar = NSCalendar.currentCalendar()
let comps = NSDateComponents()
comps.day = 7
let date2 = calendar.dateByAddingComponents(comps, toDate: NSDate(), options: NSCalendarOptions.allZeros)

if dueDate.compare(date2!) == NSComparisonResult.OrderedDescending
{
    NSLog("not due within a week");
} else if dueDate.compare(date2!) == NSComparisonResult.OrderedAscending
{
    NSLog("due within a week");
} else
{
    NSLog("due in exactly a week (to the second, this will rarely happen in practice)");
}
Отменить
источник
2
упорядоченный по убыванию означает, что date1> date2?
Генри Осканнлен-Миллер
1
Да, @ Henryoscannlain-miller.
Отмена
46

Я всегда делал это в одной строке:

let greater = date1.timeIntervalSince1970 < date2.timeIntervalSince1970

Все еще читается в ifблоке

повторное обнаружение
источник
12

В Swift3 Dateструктура в Foundationnow реализует Comparableпротокол. Итак, предыдущие NSDateподходы Swift2 заменены Swift3 Date.

/**
 `Date` represents a single point in time.

 A `Date` is independent of a particular calendar or time zone. To represent a `Date` to a user, you must interpret it in the context of a `Calendar`.
*/
public struct Date : ReferenceConvertible, Comparable, Equatable {

    // .... more         

    /**
        Returns the interval between the receiver and another given date.

        - Parameter another: The date with which to compare the receiver.

        - Returns: The interval between the receiver and the `another` parameter. If the receiver is earlier than `anotherDate`, the return value is negative. If `anotherDate` is `nil`, the results are undefined.

        - SeeAlso: `timeIntervalSince1970`
        - SeeAlso: `timeIntervalSinceNow`
        - SeeAlso: `timeIntervalSinceReferenceDate`
        */
    public func timeIntervalSince(_ date: Date) -> TimeInterval

   // .... more 

    /// Returns true if the two `Date` values represent the same point in time.
    public static func ==(lhs: Date, rhs: Date) -> Bool

    /// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
    public static func <(lhs: Date, rhs: Date) -> Bool

    /// Returns true if the left hand `Date` is later in time than the right hand `Date`.
    public static func >(lhs: Date, rhs: Date) -> Bool

    /// Returns a `Date` with a specified amount of time added to it.
    public static func +(lhs: Date, rhs: TimeInterval) -> Date

    /// Returns a `Date` with a specified amount of time subtracted from it.
    public static func -(lhs: Date, rhs: TimeInterval) -> Date

  // .... more
}

Заметка ...

В Swift3, Dateесть struct, это означает , что она value type. NSDateэто class, это reference type.

// Swift3
let a = Date()
let b = a //< `b` will copy `a`. 

// So, the addresses between `a` and `b` are different.
// `Date` is some kind different with `NSDate`.
AechoLiu
источник
6
extension NSDate {

    // MARK: - Dates comparison

    func isGreaterThanDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
    }

    func isLessThanDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedAscending
    }

    func equalToDate(dateToCompare: NSDate) -> Bool {

        return self.compare(dateToCompare) == NSComparisonResult.OrderedSame
    }
}
Юра Воеводин
источник
6

Если вы хотите сравнить даты с гранулярностью (точно такой же день или год и т. Д.) На swift 3.

func compareDate(date1:NSDate, date2:NSDate, toUnitGranularity: NSCalendar.Unit) -> Bool {

 let order = NSCalendar.current.compare(date1 as Date, to: date2 as Date, toGranularity: .day)
 switch order {
 case .orderedSame:
   return true
 default:
   return false
 }
}

Для других календарных сравнений измените .day на;

.год .месяц .день .час. минута .секунда

SashaZ
источник
5

Swift уже реализует сравнение дат, просто используйте date1> date2 и так далее.

/// Returns true if the two `Date` values represent the same point in time.
public static func ==(lhs: Date, rhs: Date) -> Bool

/// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
public static func <(lhs: Date, rhs: Date) -> Bool

/// Returns true if the left hand `Date` is later in time than the right hand `Date`.
public static func >(lhs: Date, rhs: Date) -> Bool

/// Returns a `Date` with a specified amount of time added to it.
public static func +(lhs: Date, rhs: TimeInterval) -> Date

/// Returns a `Date` with a specified amount of time subtracted from it.
public static func -(lhs: Date, rhs: TimeInterval) -> Date

/// Add a `TimeInterval` to a `Date`.
///
/// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more.
public static func +=(lhs: inout Date, rhs: TimeInterval)

/// Subtract a `TimeInterval` from a `Date`.
///
/// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more.
public static func -=(lhs: inout Date, rhs: TimeInterval)
Трунг Фан
источник
4

в Swift 3 дата сопоставима, поэтому мы можем напрямую сравнивать даты как

let date1 = Date()
let date2 = Date()

let isGreater = date1 > date2
print(isGreater)

let isEqual = date1 == date2
print(isEqual)

или в качестве альтернативы

let result = date1.compare(date2)
switch result {
    case .OrderedAscending     :   print("date 1 is earlier than date 2")
    case .OrderedDescending    :   print("date 1 is later than date 2")
    case .OrderedSame          :   print("two dates are the same")
}

лучший способ создания extensionна свидании

extension Date {

  fun isGreater(than date: Date) -> Bool {
    return self > date 
  }

  func isSmaller(than date: Date) -> Bool {
    return self < date
  }

  func isEqual(to date: Date) -> Bool {
    return self == date
  }

}

использование let isGreater = date1.isGreater(than: date2)

Сухит Патил
источник
3

Эта функция сработала для меня, чтобы сравнить, была ли одна дата (startDate) после endDate, где обе были определены как переменные NSDate:

if startDate.compare(endDate as Date) == ComparisonResult.orderedDescending
Diskprotek
источник
2

реализация в Swift

let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let files = NSFileManager.defaultManager().contentsOfDirectoryAtPath(documentsPath, error: nil)

let filesAndProperties = NSMutableArray()
for file in files! {

    let filePath = documentsPath.stringByAppendingString(file as NSString)
    let properties = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)
    let modDate = properties![NSFileModificationDate] as NSDate
    filesAndProperties.addObject(NSDictionary(objectsAndKeys: file, "path", modDate, "lastModDate"))
}

let sortedFiles = filesAndProperties.sortedArrayUsingComparator({
    (path1, path2) -> NSComparisonResult in

    var comp = (path1.objectForKey("lastModDate") as NSDate).compare(path2.objectForKey("lastModDate") as NSDate)
    if comp == .OrderedDescending {

        comp = .OrderedAscending
    } else if comp == .OrderedAscending {

        comp = .OrderedDescending
    }

    return comp
})
Янош
источник
2
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateData: String = dateFormatter.stringFromDate(date1)
let testDate: String = dateFormatter.stringFromDate(date2)
print(dateData == testDate)
Тревор Джордет
источник
1
someArray.sort({($0.dateAdded?.timeIntervalSinceReferenceDate)! < ($1.dateAdded?.timeIntervalSinceReferenceDate)!})

dateAdded является переменной NSDate в моем объекте

class MyClass {
    let dateAdded: NSDate?
}
larod
источник
1

У нас есть сценарий, чтобы проверить текущее время ложь два раза (две даты). Например, я хочу проверить текущую ложь между временем открытия клиники (больницы) и временем закрытия.

Используйте простой код.

      NSDate * now = [NSDate date];
        NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
        [outputFormatter setDateFormat:@"HH:mm:ss"];

        //current time
        NSString *currentTimeString = [outputFormatter stringFromDate:now];
        NSDate *dateCurrent = [outputFormatter dateFromString:currentTimeString];


        NSString *timeStart = @"09:00:00";
        NSString *timeEnd = @"22:00:00";

        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];

        NSDate *dateStart= [formatter timeStart];
        NSDate *dateEnd = [formatter timeEnd];
        NSComparisonResult result = [dateCurrent compare:dateStart];
        NSComparisonResult resultSecond = [date2 compare:dateEnd];

if(result == NSOrderedDescending && resultSecond == NSOrderedDescending)
        {
            NSLog(@"current time lies in starting and end time");
    }else {
            NSLog(@"current time doesn't lie in starting and end time");
        }
Maninderjit Singh
источник
1

Для swift 3 вы можете использовать приведенную ниже функцию для сравнения двух дат.

func compareDate(dateInitial:Date, dateFinal:Date) -> Bool {
    let order = Calendar.current.compare(dateInitial, to: dateFinal, toGranularity: .day)
    switch order {
    case .orderedSame:
        return true
    default:
        return false
    }
}

toGranularity может быть изменено в соответствии с ограничениями, к которым вы хотите применить свое сравнение.

Himanshu
источник
1

Продлить на СашаЗ

Swift iOS 8 и выше Когда вам нужно больше, чем просто сравнение больших или меньших дат. Например, это тот же день или предыдущий день, ...

Примечание: никогда не забывайте часовой пояс. Часовой пояс календаря имеет значение по умолчанию, но если вам не нравится значение по умолчанию, вы должны установить часовой пояс самостоятельно. Чтобы узнать, какой сегодня день, вам нужно знать, в каком часовом поясе вы спрашиваете.

extension Date {
    func compareTo(date: Date, toGranularity: Calendar.Component ) -> ComparisonResult  {
        var cal = Calendar.current
        cal.timeZone = TimeZone(identifier: "Europe/Paris")!
        return cal.compare(self, to: date, toGranularity: toGranularity)
        }
    }

Используйте это так:

if thisDate.compareTo(date: Date(), toGranularity: .day) == .orderedDescending {
// thisDate is a previous day
}

Из более сложного примера. Найти и отфильтровать все даты в массиве, которые относятся к тому же дню, что и «findThisDay»:

let formatter = DateFormatter()
formatter.timeZone = TimeZone(identifier: "Europe/Paris")
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"

let findThisDay = formatter.date(from: "2018/11/05 08:11:08")!
_ = [
    formatter.date(from: "2018/12/05 08:08:08")!, 
    formatter.date(from: "2018/11/05 08:11:08")!,
    formatter.date(from: "2018/11/05 11:08:22")!,
    formatter.date(from: "2018/11/05 22:08:22")!,
    formatter.date(from: "2018/11/05 08:08:22")!,
    formatter.date(from: "2018/11/07 08:08:22")!,
    ]
    .filter{ findThisDay.compareTo(date: $0 , toGranularity: .day) == .orderedSame }
    .map { print(formatter.string(from: $0)) }
t1ser
источник