今日、昨日、今週、先週、今月、先月…変数でNSDateを取得します


86

私がやろうとしているのは、今日、昨日、今週、先週、今月、先月の変数をUITableViewのtitleForHeaderInSectionに追加するヘッダーの比較の準備ができているNSDateを取得することです。

私が欲しいのは、日付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"];

回答:


93

日付と時刻のプログラミングガイドから適応:

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

月に応じて正確な日数が必要な場合は、を使用する必要がありNSCalendarます。


77
時間の固定定数はBADBADBADです。うるう日やうるう秒はどうですか?NSDateComponentsを使用しないと、日付スタンプの不一致が発生したときに、非常に厄介でデバッグが難しい問題が発生します。
Kendall Helmstetter Gelner

11
先週分の投稿などが必要な場合は、それほど悪くはありません。NSCalendar正確さが要求される場合は、OPが使用するように指定しました。
ベンS

2
固定定数は、実際には日数を数えるのに問題ないはずです。毎日は86400秒です。うるう秒は単純に2倍の時間がかかります。うるう秒のある日には、86400秒があります。数ヶ月間...ええ、明確に操縦します:)
クリス

3
@Chris。ただし、日数が86400秒でない場合を除きます。夏時間はどうですか?取るに足らないように聞こえますが、これは実際には、DSTが発生し、それを考慮に入れることを忘れたときにどこからともなく発生する非常に厄介なバグにつながる可能性があります。
jpswain 2013年

1
約7日間だけ戻りたい場合は、時間を差し引くだけです。今週の「月曜日」に戻りたい場合は、NSCalendarを使用します。両方のシナリオのユースケースがあります。
Johnny Rockex 2016

83

これを書くためのより良い方法かもしれませんが、ここで私がベンのNSCalendarの提案に思いつき、そこから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);

13
あなたはそれらのfromDate:[[NSDate alloc] init]ビットでメモリリークをしている。
シャギーフロッグ

21
@Shaggy Frog:彼はARCが有効になっていると思っていると思います...;)
orj 2012年

3
このコードは正しく機能しません。入力日の一部である秒の端数は考慮されません。つまり、日付は30.1234秒で、NSDateComponentsの秒値は30しかないため、NSDateから30秒を引くと、真夜中を過ぎて0.1234秒になります。また、一部の日は24時間(夏時間)より長い(または短い)ため、昨日の日付を取得するには、[components setHour:-24]ではなく[components setDay:-1]を使用する必要があります。
orj 2012年

5
@orj参考までに、ARCはその回答が投稿されてから何年も経ちました
Shaggy Frog

3
@orjまた、夏時間が変更された場合、時間を差し引いても深夜にはなりません。-1。このコードは35の賛成の価値がありません。
Nikolai Ruhe 2013年

40

NSDateComponentsは、今日入手できてうれしいです。

NSCalendar *cal = [NSCalendar currentCalendar];

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

これにより、年、月、日付のみのNSDateが作成されます。

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

昨日などを取得するには、NSDateComponentsを使用して計算できます。

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

時代のコンポーネントを含める必要はありませんか?
クリスページ

いいね。これを使用して8年前の日付を計算すると、うまく機能します。ここでリークを修正する必要があります:[[NSDateComponents alloc] init];
クレイグB

@CraigBありがとう。完全な例を示すものではありません。読者の課題として明らかなメモリ管理を
クリスチャンビール

@ChrisPage時代のコンポーネント?何のために?
クリスチャンビール

1
@ChristianBeer希望する解像度よりも大きいすべてのコンポーネントを考慮する必要があると確信しています。私はそうある(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)べきだと信じています(NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
クリスページ

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

時代のコンポーネントを含める必要はありませんか?
クリスページ

DateLastWeekを計算するときに、NSDayCalendarUnitを削除し、NSWeekdayCalendarUnitを追加する必要がありました。
jessecurry 2011

Appleは、iOS5.0周辺でNSWeekdayCalendarUnitとNSDayCalendarUnitの定義を変更しました。新しい方法は、NSWeekdayCalendarUnitを使用することです。ただし、古いバージョンでは、NSDayCalendarUnitを使用する必要があります。それは...混乱です
ダスティン

以下のためにたとえば...新しいユニットに更新する場合でも、作業には思われません@DustinDateThisWeek平日を設定することが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)

日付コンポーネントはオプションであることに注意してください。


1
@fengdさて、今日2016年6月27日午前1時の日付は2016年6月28日であり、24時間を引くと、日付は6月27日午前1時になります。私が逃したポイントは何ですか?
gokhanakkurt 2016年

1
すみません、@ gokhanakkurt。私の心にはいくつかの問題があったと思います。あなたの答えが正しいか
fengd

4

他の答えは私にはうまくいきませんでした(おそらく私のタイムゾーンのため)。これが私のやり方です:

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

iOS10以降またはMacOS10.12 +を使用している場合は、これら2つのCalendar方法を使用してこれを適切に行うことができます。

  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = default) -> Date?ドキュメント
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?ドキュメント

これは、Swift 3でこれらのメソッドを使用する方法の例と、私のタイムゾーンで出力された遊び場です。

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

ボーナス:DateIntervalsを使用して、日付がその範囲内にあるかどうかをテストできます。上から続ける:

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

なぜyesterday4月29日とnow4月28日と言うのですか?
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];

14
なぜ[NSDate date]ですか?
joshpaul 2011年

どちらも現在の日付を取得するために有効であるため、両方に時刻が含まれます。したがって、それは質問の正しい解決策ではありません!
クリスチャンビール

0

これは、日付が今月かどうかを確認するためのものです

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
}

そして2つ目はこのように弱いためにcalendarUnitのタイプを変更するだけです

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
}

これが誰かに役立つことを願っていますありがとう。


0

私はすでに同様の質問に答えました、そしてこれが私の答えがより良い理由です:

  • スウィフト3
  • DateFormatterの「昨日」と「今日」を利用します。これはすでにAppleによって翻訳されているので、作業を節約できます。
  • DateComponentsFormatterのすでに翻訳された「1週間」の文字列を使用します。(Appleの好意により、作業が減りました。)必要なのは、「%@ ago」文字列を翻訳することだけです。🙂
  • 他の回答は、日が「今日」から「昨日」などに切り替わる時刻を誤って計算します。理由のため、固定定数は大きなNO-NOです。また、他の回答では、現在の日付/時刻の終わりを使用する必要があるときに、現在の日付/時刻を使用します。
  • カレンダーとロケールにautoupdatingCurrentを使用して、Settings.appのユーザーのカレンダーと言語設定でアプリをすぐに最新の状態にします。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.