NSDate'i bugün, dün, bu Hafta, geçen Hafta, bu Ay, geçen Ay alın… değişkenler


86

Yapmaya çalıştığım, NSDate'i bugün, dün, bu Hafta, geçen Hafta, bu Ay, UITableView'ın titleForHeaderInSection'a eklenecek başlıklar için karşılaştırmaya hazır değişkenleri almaktır.

İstediğim şey, 2009-12-11 tarihi için aşağıdaki kodda manuel olarak yapılır

 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"];

Yanıtlar:


93

Tarih ve Saat Programlama Kılavuzundan uyarlanmıştır :

// 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];

Aya bağlı olarak doğru tam gün sayısını istiyorsanız, bir NSCalendar.


77
Sabit zaman sabitleri KÖTÜ KÖTÜ KÖTÜ'dür. Artık günler veya artık saniyeler ne olacak? NSDateComponents'ı kullanın, aksi takdirde tarih damgalarınız uyumsuz olmaya başladığında bazı çok kötü ve hata ayıklaması zor sorunlar yaşayacaksınız.
Kendall Helmstetter Gelner

11
Yaklaşık olarak geçen haftanın gönderilerini veya her neyse, o kadar da kötü değil. Kesinlik NSCalendargerekliyse OP'nin kullanması gerektiğini belirttim.
Ben S

2
Sabit sabitler aslında günleri saymak için yeterli olmalıdır: her gün 86400 saniyedir. Artık saniyeler sadece iki kat daha uzun sürer - artık saniyeli bir günde 86400 'saniye' vardır. Aylar gelince ... evet net yönlendirmek :)
Chris

3
@Chris, günlerin 86400 saniye olmadığı zamanlar hariç. Yaz saati uygulamasına ne dersiniz? Kulağa önemsiz geliyor, ancak bu aslında sizi DST çarptığında ve onu hesaba katmayı hatırlamadığınızda hiçbir yerden çıkan süper garip hatalara götürebilir.
jpswain

1
Eğer sadece yaklaşık 7 gün geri dönmek istiyorsanız, sadece zamanı çıkartabilirsiniz, bu haftanın 'Pazartesi'sine geri dönmek istiyorsanız NSCalendar'ı kullanın. Her iki senaryo için de kullanım durumları vardır.
Johnny Rockex

83

Bunu yazmanın daha iyi bir yolu olabilir ama burada Ben'in NSCalendar önerisi üzerine bulduğum ve oradan NSDateComponents'a kadar çalıştım.

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);

13
O fromDate:[[NSDate alloc] init]parçalarla her yere hafıza sızdırıyorsun .
Shaggy Kurbağa

21
@Shaggy Frog: Sanırım ARC'nin etkin olduğunu varsayıyor ...;)
orj

3
Bu kod düzgün çalışmıyor. Giriş tarihinin parçası olan saniyenin kesirlerini hesaba katmaz. Yani, tarih 30.1234 saniyeye sahip olabilir ve NSDateComponents yalnızca 30 saniye değerine sahip olabilir, bu nedenle NSDate'ten 30 saniye çıkarıldığında gece yarısını 0.1234 saniye geçmiş olur. Ayrıca, bazı günler 24 saatten daha fazla (veya daha az) (gün ışığından yararlanma) saatlere sahiptir, bu nedenle dünün tarihini almak, [components setHour: -24] yerine gerçekten [components setDay: -1] kullanmalıdır.
orj

5
@orj Bilginize, ARC bu cevabın yayınlanmasından yıllar sonra çıktı
Shaggy Frog

3
@orj Ayrıca, gün ışığından yararlanma saati değişikliklerinde, saatleri çıkarmak size gece yarısı vermez. -1. Bu kod 35 artı oy değerinde değil.
Nikolai Ruhe

40

NSDateComponents bugün almak güzel:

NSCalendar *cal = [NSCalendar currentCalendar];

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

Bu, yalnızca yıl, ay ve tarih içeren bir NSDate oluşturur:

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

Dünü vb. Almak için NSDateComponents kullanarak hesaplayabilirsiniz:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];

Çağ bileşenini eklemenize gerek yok mu?
Chris Sayfa

Güzel. Bunu 8 yıllık bir tarihi hesaplamak için kullandım ve güzel çalışıyor. Burada bir sızıntıyı düzeltmeniz yeterlidir: [[NSDateComponents alloc] init];
Craig B

@CraigB teşekkürler. Tam bir örnek olması amaçlanmamıştır. Okuyucu için bir meydan okuma olarak bariz hafıza yönetimini bir
Christian Beer

@ChrisPage çağ bileşeni? Ne için?
Christian Beer

1
@ChristianBeer İstenilen çözünürlükten daha büyük tüm bileşenlerin dikkate alınması gerektiğinden oldukça eminim. (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)Olması gerektiğine inanıyorum (NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit).
Chris Sayfa

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];
}

Çağ bileşenini eklemenize gerek yok mu?
Chris Sayfa

DateLastWeek'i hesaplarken NSDayCalendarUnit'i kaldırmam ve NSWeekdayCalendarUnit'i eklemem gerekiyordu.
jessecurry

Apple, iOS 5.0 civarında NSWeekdayCalendarUnit ve NSDayCalendarUnit tanımlarını değiştirdi. Yeni yol NSWeekdayCalendarUnit kullanmaktır. Ancak daha eski sürümlerde NSDayCalendarUnit kullanmanız gerekir. Bu bir karmaşa ...
Dustin

@Dustin Yeni birimlere güncelleme yaparken bile işe yaramıyor gibi görünüyor ... Örneğin DateThisWeek, hafta içi ayarlamanın NSDate üzerinde bir etkisi yok…
AnthoPak

12

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)

Lütfen tarih bileşenlerinin isteğe bağlı olduğunu unutmayın.


1
@fengd well, Bugün 27 Haz 2016, 01:00 tarihinde 28 Haziran 2016 olacak ve 24 saati çıkarırsam tarih 27 Haz 1 am olacak. Kaçırdığım nokta ne?
gokhanakkurt

1
üzgünüm @gokhanakkurt. Sanırım aklımda bazı hıçkırıklar vardı. cevabınız doğru
fengd

4

Diğer cevaplar benim için işe yaramadı (belki de zaman dilimim nedeniyle). Ben böyle yaparım:

- (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;
}

4

İOS 10+ veya MacOS 10.12+ kullanıyorsanız, bunu Calendardoğru şekilde yapmak için bu iki yöntemi kullanabilirsiniz.

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

İşte bu yöntemlerin Swift 3'te nasıl kullanılacağına dair bir örnek ve zaman dilimimdeki oyun alanları çıktısı.

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: DateIntervalBir tarihin bu aralığa girip girmediğini test etmek için s'yi kullanabilirsiniz . Yukarıdan devam ediyor:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false

Neden yesterday29 Nisan ve now28 Nisan diyor?
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];


0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];

Her ikisi de geçerli tarihi almak için geçerli olduğundan, her ikisi de saati içerir. Yani soru için doğru çözüm değil!
Christian Beer

0

Bu, tarihin bu ay olup olmadığını kontrol etmek içindir.

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
}

Ve ikincisi sadece takvim tipini değiştir, bunun gibi zayıflar için birim

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
}

Umarım bu birine yardımcı olur.


0

Benzer bir soruyu zaten cevapladım ve işte cevabımın daha iyi olmasının nedeni:

  • Hızlı 3 !
  • DateFormatter'ın "Dün" ve "Bugün" ü kullanır. Bu zaten Apple tarafından çevrildi, bu da sizi işinizden kurtarır!
  • DateComponentsFormatter'ın önceden çevrilmiş "1 hafta" dizesini kullanır. (Sizin için yine daha az iş, Apple sayesinde.) Tek yapmanız gereken "% @ ago" dizesini çevirmek. 🙂
  • Diğer cevaplar, günün "bugün" den "dün" e vb. Değiştiği zamanı yanlış hesaplar. Sabit sabitler büyük bir HAYIR-HAYIR çünkü nedenler . Ayrıca, diğer cevaplar , geçerli günün tarihinin / saatinin sonunu kullanmaları gerektiğinde geçerli tarihi / saati kullanır .
  • AutoupdatingCurrent for Calendar & Locale kullanır, bu da uygulamanızın Settings.app içinde kullanıcının takvimi ve dil tercihleriyle hemen güncel olmasını sağlar.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.