obtenir NSDate aujourd'hui, hier, cette semaine, la semaine dernière, ce mois-ci, le mois dernier… variables

86

J'essaie de faire est d'obtenir NSDate aujourd'hui, hier, cette semaine, la semaine dernière, ce mois-ci, les variables du mois dernier prêtes à être comparées pour les en-têtes à ajouter sur titleForHeaderInSection

Ce que je veux est fait manuellement dans le code ci-dessous pour la date 2009-12-11

 NSDate *today = [NSDate dateWithString:@"2009-12-11 00:00:00 +0000"];
 NSDate *yesterday = [NSDate dateWithString:@"2009-12-10 00:00:00 +0000"];
 NSDate *thisWeek = [NSDate dateWithString:@"2009-12-06 00:00:00 +0000"];
 NSDate *lastWeek = [NSDate dateWithString:@"2009-11-30 00:00:00 +0000"];
 NSDate *thisMonth = [NSDate dateWithString:@"2009-12-01 00:00:00 +0000"];
 NSDate *lastMonth = [NSDate dateWithString:@"2009-11-01 00:00:00 +0000"];
hasnat
la source

Réponses:

93

Adapté du Guide de programmation de la date et de l'heure :

// Right now, you can remove the seconds into the day if you want
NSDate *today = [NSDate date];

// All intervals taken from Google
NSDate *yesterday = [today dateByAddingTimeInterval: -86400.0];
NSDate *thisWeek  = [today dateByAddingTimeInterval: -604800.0];
NSDate *lastWeek  = [today dateByAddingTimeInterval: -1209600.0];

// To get the correct number of seconds in each month use NSCalendar
NSDate *thisMonth = [today dateByAddingTimeInterval: -2629743.83];
NSDate *lastMonth = [today dateByAddingTimeInterval: -5259487.66];

Si vous voulez le nombre exact de jours en fonction du mois, vous devez utiliser un fichier NSCalendar.

Ben S
la source
77
Les constantes fixes pour le temps sont BAD BAD BAD. Qu'en est-il des jours bissextiles ou des secondes bissextiles? Utilisez NSDateComponents ou vous souffrirez de problèmes très désagréables et difficiles à déboguer lorsque vos horodatages commencent à ne pas correspondre.
Kendall Helmstetter Gelner
11
Ce n'est vraiment pas si mal si vous voulez juste environ la valeur de la semaine dernière de messages ou autre. J'ai précisé que l'OP devrait utiliser NSCalendarsi l'exactitude était requise.
Ben S du
2
Les constantes fixes devraient en fait être parfaites pour compter les jours: chaque jour est de 86400 secondes. Les secondes intercalaires prennent simplement deux fois plus de temps - une journée avec une seconde intercalaire a toujours 86400 «secondes». Comme pendant des mois ... ouais, tenez-vous à l'écart :)
Chris
3
@Chris sauf quand les jours ne sont pas 86400 secondes. Qu'en est-il de l'heure d'été? Cela semble insignifiant, mais cela peut en fait vous conduire à des bugs super noueux qui ne viennent de nulle part lorsque l'heure d'été arrive et que vous ne vous souvenez pas de prendre en compte.
jpswain
1
Si vous voulez juste revenir en arrière, disons 7 jours environ, vous pouvez simplement soustraire l'heure, si vous voulez revenir au «lundi» de cette semaine, utilisez NSCalendar. Il existe des cas d'utilisation pour les deux scénarios.
Johnny Rockex le
83

Peut-être une meilleure façon d'écrire ceci, mais voici ce que je suis venu sur la suggestion NSCalendar de Ben et de travailler à partir de là vers NSDateComponents

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond ) fromDate:[[NSDate alloc] init]];

[components setHour:-[components hour]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
NSDate *today = [cal dateByAddingComponents:components toDate:[[NSDate alloc] init] options:0]; //This variable should now be pointing at a date object that is the start of today (midnight);

[components setHour:-24];
[components setMinute:0];
[components setSecond:0];
NSDate *yesterday = [cal dateByAddingComponents:components toDate: today options:0];

components = [cal components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:[[NSDate alloc] init]];

[components setDay:([components day] - ([components weekday] - 1))]; 
NSDate *thisWeek  = [cal dateFromComponents:components];

[components setDay:([components day] - 7)];
NSDate *lastWeek  = [cal dateFromComponents:components];

[components setDay:([components day] - ([components day] -1))]; 
NSDate *thisMonth = [cal dateFromComponents:components];

[components setMonth:([components month] - 1)]; 
NSDate *lastMonth = [cal dateFromComponents:components];

NSLog(@"today=%@",today);
NSLog(@"yesterday=%@",yesterday);
NSLog(@"thisWeek=%@",thisWeek);
NSLog(@"lastWeek=%@",lastWeek);
NSLog(@"thisMonth=%@",thisMonth);
NSLog(@"lastMonth=%@",lastMonth);
hasnat
la source
13
Vous perdez de la mémoire partout avec ces fromDate:[[NSDate alloc] init]bits.
Shaggy Frog
21
@Shaggy Frog: Je suppose qu'il suppose que l'ARC est activé ...;)
orj
3
Ce code ne fonctionne pas correctement. Il ne prend pas en compte les fractions de seconde qui font partie de la date d'entrée. C'est-à-dire que la date peut avoir 30,1234 secondes et NSDateComponents n'a que 30 comme valeur de secondes, donc soustraire 30 secondes du NSDate vous laisse 0,1234 secondes après minuit. De plus, certains jours ont plus (ou moins) 24 heures (heure d'été), donc obtenir la date d'hier devrait vraiment utiliser [components setDay: -1] plutôt que [components setHour: -24].
orj
5
@orj FYI, ARC est sorti longtemps après la publication de cette réponse
Shaggy Frog
3
@orj De plus, lors des changements d'heure d'été, la soustraction des heures ne vous donne pas minuit. -1. Ce code ne vaut pas 35 votes positifs.
Nikolai Ruhe
40

NSDateComponents est agréable à obtenir aujourd'hui:

NSCalendar *cal = [NSCalendar currentCalendar];

NSDate *date = [NSDate date];
NSDateComponents *comps = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) 
                                                          fromDate:date];
NSDate *today = [cal dateFromComponents:comps];

Cela crée un NSDate avec seulement l'année, le mois et la date:

(gdb) po today
2010-06-22 00:00:00 +0200

Pour obtenir hier, etc., vous pouvez le calculer en utilisant NSDateComponents:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];
Bière chrétienne
la source
N'avez-vous pas besoin d'inclure le composant d'ère?
Chris Page
Agréable. Utilisé ceci pour calculer une date 8 ans dans le passé et cela fonctionne bien. Juste besoin de réparer une fuite ici: [[NSDateComponents alloc] init];
Craig B
@CraigB merci. Ce n'est pas censé être un exemple complet. Laissé de côté la gestion évidente de la mémoire comme un défi pour le lecteur;)
Christian Beer
@ChrisPage le composant de l'ère? Pourquoi?
Christian Beer
1
@ChristianBeer Je suis presque sûr que tous les composants plus grands que la résolution souhaitée doivent être pris en compte. Je crois que ça (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)devrait être (NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit).
Chris Page
19
+ (NSDate*)dateFor:(enum DateType)dateType {

    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *comps =
    [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                fromDate:[NSDate date]];

    if(dateType == DateYesterday) {

        comps.day--;
    }
    else if(dateType == DateThisWeek) {

        comps.weekday = 1;
    }
    else if(dateType == DateLastWeek) {

        comps.weekday = 1;
        comps.week--;
    }
    else if(dateType == DateThisMonth) {

        comps.day = 1;
    }
    else if(dateType == DateLastMonth) {

        comps.day = 1;
        comps.month--;
    }
    else if(dateType != DateToday)
        return nil;

    return [calendar dateFromComponents:comps];
}
Dustin
la source
N'avez-vous pas besoin d'inclure le composant d'ère?
Chris Page
J'ai dû supprimer NSDayCalendarUnit et ajouter NSWeekdayCalendarUnit lors du calcul de DateLastWeek.
jessecurry
Apple a modifié la définition de NSWeekdayCalendarUnit et NSDayCalendarUnit autour d'iOS 5.0. La nouvelle méthode consiste à utiliser NSWeekdayCalendarUnit. Mais sur les anciennes versions, vous devez utiliser NSDayCalendarUnit. It is a mess ...
Dustin
@Dustin Cela ne semble pas fonctionner, même lors de la mise à jour vers de nouvelles unités ... Par exemple DateThisWeek, le réglage du jour de la semaine n'a pas d'impact sur le NSDate ...
AnthoPak
11

Swift 4.2

let today = Date()
let yesterday = today.addingTimeInterval(-86400.0)
let thisWeek = today.addingTimeInterval(-604800.0)
let lastWeek = today.addingTimeInterval(-1209600.0)

let thisMonth = today.addingTimeInterval(-2629743.83)
let lastMonth = today.addingTimeInterval(-5259487.66)

// components of the date
let calendar = Calendar(identifier: Calendar.Identifier.gregorian)
let components = calendar.dateComponents([.year, .month, .day], from: today)
let (year, month, day) = (components.year, components.month, components.day)

Veuillez noter que les composants de date sont facultatifs.

gokhanakkurt
la source
1
@fengd bien, Aujourd'hui 27 juin 2016, à 1h du matin, la date sera le 28 juin 2016, et si je soustrais 24 heures, la date sera le 27 juin à 1 h. Quel est le point que j'ai manqué?
gokhanakkurt le
1
désolé, @gokhanakkurt. Je pense qu'il y avait un hoquet dans mon esprit. votre réponse est correcte
fengd
4

Les autres réponses ne fonctionnaient tout simplement pas pour moi (peut-être à cause de mon fuseau horaire). Voici comment je fais:

- (BOOL)isOnThisWeek:(NSDate *)dateToCompare
{
    NSCalendar * calendar = [NSCalendar currentCalendar];
    NSDate     * today    = [NSDate date];

    int todaysWeek        = [[calendar components: NSWeekCalendarUnit fromDate:today] week];
    int dateToCompareWeek = [[calendar components: NSWeekCalendarUnit fromDate:dateToCompare] week];

    int todaysYear         = [[calendar components:NSYearCalendarUnit fromDate:today] year];
    int dateToCompareYear  = [[calendar components:NSYearCalendarUnit fromDate:dateToCompare] year];

    if (todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear) {
        return YES;
    }

    return NO;
}
Lucas
la source
4

Si vous utilisez iOS 10+ ou MacOS 10.12+, vous pouvez utiliser ces deux Calendarméthodes pour le faire correctement.

  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = default) -> Date?( docs )
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?( docs )

Voici un exemple d'utilisation de ces méthodes dans Swift 3, ainsi que la sortie des terrains de jeux dans mon fuseau horaire.

let calendar = Calendar.current
let now = Date()
// => "Apr 28, 2017, 3:33 PM"

let yesterday = calendar.date(byAdding: .day, value: -1, to: now)
// => "Apr 29, 2017, 3:33 PM"
let yesterdayStartOfDay = calendar.startOfDay(for: yesterday!)
// => ""Apr 29, 2017, 12:00 AM"

let thisWeekInterval = calendar.dateInterval(of: .weekOfYear, for: now)
// => 2017-04-23 04:00:00 +0000 to 2017-04-30 04:00:00 +0000

let thisMonthInterval = calendar.dateInterval(of: .month, for: now)
// => 2017-04-01 04:00:00 +0000 to 2017-05-01 04:00:00 +0000

let aDateInLastWeek = calendar.date(byAdding: .weekOfYear, value: -1, to: now)
let lastWeekInterval = calendar.dateInterval(of: .weekOfYear, for: aDateInLastWeek!)
// => 2017-04-16 04:00:00 +0000 to 2017-04-23 04:00:00 +0000

let aDateInLastMonth = calendar.date(byAdding: .month, value: -1, to: now)
let lastMonthInterval = calendar.dateInterval(of: .weekOfYear, for: aDateInLastMonth!)
// => 2017-03-26 04:00:00 +0000 to 2017-04-02 04:00:00 +0000

Bonus: vous pouvez utiliser le DateIntervals pour tester si une date tombe dans cette plage. En continuant d'en haut:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false
bon
la source
Pourquoi est-ce que c'est le yesterday29 avril et le now28 avril?
Juan Boero
1
NSDate *today = [NSDate date]; // Today's date
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian componentsNSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:today];
NSInteger day = [weekdayComponents day];
Hayden
la source
0

J'aime beaucoup l'objet THCalendarInfo contenu dans ce projet:

http://github.com/jaredholdcroft/kcalendar

Je ne trouve pas tout à fait l'original. En utilisant cet objet, vous pouvez passer à un jour précédent, le début d'une semaine, le début d'un mois, obtenir le jour d'une semaine, le jour d'un mois ... etc. etc.

Kendall Helmstetter Gelner
la source
0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
Tischel
la source
14
Pourquoi pas [NSDate date]?
joshpaul
Comme les deux sont valides pour obtenir la date actuelle, les deux incluent l'heure. Ce n'est donc pas la bonne solution pour la question!
Christian Beer
0

Ceci est pour vérifier que la date est ce mois-ci ou non

func isOnThisMonth(dateToCompare: NSDate) -> Bool {
    let calendar: NSCalendar = NSCalendar.currentCalendar()
    let today: NSDate = NSDate()
    let todaysWeek: Int = calendar.components(NSCalendarUnit.Month, fromDate: today).month
    let dateToCompareWeek: Int = calendar.components(.Month, fromDate: dateToCompare).month
    let todaysYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: today).year
    let dateToCompareYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: dateToCompare).year
    if todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear {
        return true
    }
    return false
}

Et le second ne change que le type de calendrierUnit pour un faible comme celui-ci

func isOnThisWeek(dateToCompare: NSDate) -> Bool {
    let calendar: NSCalendar = NSCalendar.currentCalendar()
    let today: NSDate = NSDate()
    let todaysWeek: Int = calendar.components(NSCalendarUnit.Weekday, fromDate: today).weekday
    let dateToCompareWeek: Int = calendar.components(.Weekday, fromDate: dateToCompare).weekday
    let todaysYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: today).year
    let dateToCompareYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: dateToCompare).year
    if todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear {
        return true
    }
    return false
}

J'espère que cela est utile à quelqu'un Merci.

Ilesh P
la source
0

Je répondu à une question similaire déjà et voici pourquoi ma réponse est meilleure:

  • Swift 3 !
  • Utilise "Hier" et "Aujourd'hui" de DateFormatter. Ceci est déjà traduit par Apple, ce qui vous permet d'économiser du travail!
  • Utilise la chaîne "1 semaine" déjà traduite de DateComponentsFormatter. (Encore moins de travail pour vous, gracieuseté d'Apple.) Tout ce que vous avez à faire est de traduire la chaîne "% @ ago". 🙂
  • Les autres réponses calculent incorrectement l'heure à laquelle le jour passe de «aujourd'hui» à «hier» à etc. Les constantes fixes sont un grand NON-NON pour des raisons . En outre, les autres réponses utilisent la date / heure actuelle alors qu'elles doivent utiliser la fin de la date / heure du jour en cours.
  • Utilise autoupdatingCurrent pour le calendrier et les paramètres régionaux, ce qui garantit que votre application est immédiatement à jour avec le calendrier de l'utilisateur et les préférences linguistiques dans Settings.app
ChrisJF
la source