J'ai un modèle de données de base avec une propriété NSDate
. Je veux filtrer la base de données par jour. Je suppose que la solution impliquera une NSPredicate
, mais je ne sais pas comment le mettre ensemble.
Je sais comment comparer le jour de deux NSDate
s en utilisant NSDateComponents
et NSCalendar
, mais comment le filtrer avec un NSPredicate
?
J'ai peut-être besoin de créer une catégorie sur ma sous-classe NSManagedObject
qui puisse renvoyer une date nue avec uniquement l'année, le mois et le jour. Ensuite, je pourrais comparer cela dans un NSPredicate
. Est-ce votre recommandation ou y a-t-il quelque chose de plus simple?
Avec un NSDate * startDate et endDate et un NSManagedObjectContext * moc :
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", startDate, endDate];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:@"EntityName" inManagedObjectContext:moc]];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
Exemple sur la façon de configurer également startDate et endDate à la réponse donnée ci-dessus:
...
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth ) fromDate:[NSDate date]];
//create a date with these components
NSDate *startDate = [calendar dateFromComponents:components];
[components setMonth:1];
[components setDay:0]; //reset the other components
[components setYear:0]; //reset the other components
NSDate *endDate = [calendar dateByAddingComponents:components toDate:startDate options:0];
predicate = [NSPredicate predicateWithFormat:@"((date >= %@) AND (date < %@)) || (date = nil)",startDate,endDate];
...
Ici, je cherchais toutes les entrées dans un délai d’un mois. Il convient de mentionner que cet exemple montre également comment rechercher des dates «nil».
Dans Swift, j'ai eu quelque chose de similaire à:
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.dateFromString(predicate)
let calendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
let components = calendar!.components(
NSCalendarUnit.CalendarUnitYear |
NSCalendarUnit.CalendarUnitMonth |
NSCalendarUnit.CalendarUnitDay |
NSCalendarUnit.CalendarUnitHour |
NSCalendarUnit.CalendarUnitMinute |
NSCalendarUnit.CalendarUnitSecond, fromDate: date!)
components.hour = 00
components.minute = 00
components.second = 00
let startDate = calendar!.dateFromComponents(components)
components.hour = 23
components.minute = 59
components.second = 59
let endDate = calendar!.dateFromComponents(components)
predicate = NSPredicate(format: "day >= %@ AND day =< %@", argumentArray: [startDate!, endDate!])
J'ai eu du mal à découvrir que l'interpolation de chaîne "\(this notation)"
ne fonctionne pas pour comparer des dates dans NSPredicate.
J'ai porté la réponse de Glauco Neves à Swift 2.0 et je l'ai intégrée dans une fonction qui reçoit une date et renvoie la NSPredicate
du jour correspondant:
func predicateForDayFromDate(date: NSDate) -> NSPredicate {
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let components = calendar!.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: date)
components.hour = 00
components.minute = 00
components.second = 00
let startDate = calendar!.dateFromComponents(components)
components.hour = 23
components.minute = 59
components.second = 59
let endDate = calendar!.dateFromComponents(components)
return NSPredicate(format: "day >= %@ AND day =< %@", argumentArray: [startDate!, endDate!])
}
Swift 3.0 extension pour Date:
extension Date{
func makeDayPredicate() -> NSPredicate {
let calendar = Calendar.current
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self)
components.hour = 00
components.minute = 00
components.second = 00
let startDate = calendar.date(from: components)
components.hour = 23
components.minute = 59
components.second = 59
let endDate = calendar.date(from: components)
return NSPredicate(format: "day >= %@ AND day =< %@", argumentArray: [startDate!, endDate!])
}
}
Ensuite, utilisez comme:
let fetchReq = NSFetchRequest(entityName: "MyObject")
fetchReq.predicate = myDate.makeDayPredicate()
Ajout à la réponse de Rafael (incroyablement utile, merci!), Portage pour Swift 3.
func predicateForDayFromDate(date: Date) -> NSPredicate {
let calendar = Calendar(identifier: Calendar.Identifier.gregorian)
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
components.hour = 00
components.minute = 00
components.second = 00
let startDate = calendar.date(from: components)
components.hour = 23
components.minute = 59
components.second = 59
let endDate = calendar.date(from: components)
return NSPredicate(format: "YOUR_DATE_FIELD >= %@ AND YOUR_DATE_FIELD =< %@", argumentArray: [startDate!, endDate!])
}
J'ai récemment passé du temps à essayer de résoudre ce même problème et à ajouter les éléments suivants à la liste des alternatives pour préparer les dates de début et de fin (inclut la méthode mise à jour pour iOS 8 et versions ultérieures) ...
NSDate *dateDay = nil;
NSDate *dateDayStart = nil;
NSDate *dateDayNext = nil;
dateDay = <<USER_INPUT>>;
dateDayStart = [[NSCalendar currentCalendar] startOfDayForDate:dateDay];
// dateDayNext EITHER
dateDayNext = [dateDayStart dateByAddingTimeInterval:(24 * 60 * 60)];
// dateDayNext OR
NSDateComponents *dateComponentDay = nil;
dateComponentDay = [[NSDateComponents alloc] init];
[dateComponentDay setDay:1];
dateDayNext = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponentDay
toDate:dateDayStart
options:NSCalendarMatchNextTime];
... et la NSPredicate
pour les données de base NSFetchRequest
(comme déjà indiqué ci-dessus dans d'autres réponses) ...
[NSPredicate predicateWithFormat:@"(dateAttribute >= %@) AND (dateAttribute < %@)", dateDayStart, dateDayNext]]
Pour moi c'est travaillé.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit ) fromDate:[NSDate date]];
//create a date with these components
NSDate *startDate = [calendar dateFromComponents:components];
[components setMonth:0];
[components setDay:0]; //reset the other components
[components setYear:0]; //reset the other components
NSDate *endDate = [calendar dateByAddingComponents:components toDate:startDate options:0];
startDate = [NSDate date];
endDate = [startDate dateByAddingTimeInterval:-(7 * 24 * 60 * 60)];//change here
NSString *startTimeStamp = [[NSNumber numberWithInt:floor([endDate timeIntervalSince1970])] stringValue];
NSString *endTimeStamp = [[NSNumber numberWithInt:floor([startDate timeIntervalSince1970])] stringValue];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"((paidDate1 >= %@) AND (paidDate1 < %@))",startTimeStamp,endTimeStamp];
NSLog(@"predicate is %@",predicate);
totalArr = [completeArray filteredArrayUsingPredicate:predicate];
[self filterAndPopulateDataBasedonIndex];
[self.tableviewObj reloadData];
NSLog(@"result is %@",totalArr);
J'ai filtré le tableau de la date actuelle à 7 jours en arrière. Je veux dire que je reçois des données d'une semaine à partir de la date actuelle. Cela devrait marcher.
Remarque: je convertis la date qui arrive avec millièmes de seconde par 1 000, et en comparant après. Faites-moi savoir si vous avez besoin de clarté.