【问题标题】:Determine NSDate for Memorial Day in a given year确定给定年份阵亡将士纪念日的 NSDate
【发布时间】:2012-12-18 07:40:03
【问题描述】:

有没有更好的方法来确定给定年份的阵亡将士纪念日(5 月的最后一个星期一)的 NSDate?

NSInteger aGivenYear = 2013 ;

NSCalendar* calendar = [NSCalendar currentCalendar] ;
NSDateComponents* firstMondayInJuneComponents = [NSDateComponents new] ;
firstMondayInJuneComponents.month = 6 ;
// Thanks, Martin R., for pointing out that `weekOfMonth` is wrong for returning the first Monday in June.
firstMondayInJuneComponents.weekOfMonth = 1 ;
firstMondayInJuneComponents.weekday = 2 ; //Monday
firstMondayInJuneComponents.year = aGivenYear ;
NSDate* firstMondayInJune = [calendar dateFromComponents:firstMondayInJuneComponents] ;

NSDateComponents* subtractAWeekComponents = [NSDateComponents new] ;
subtractAWeekComponents.week = 0 ;
NSDate* memorialDay = [calendar dateByAddingComponents:subtractAWeekComponents toDate:firstMondayInJune options:0] ;

编辑

我现在看到上面示例中的firstMondayInJune 并不是所有年份都有效;它返回 2012 年的 5 月 28 日。

谢谢,Martin RweekdayOrdinal 完全符合我的期望,它以少 3 行代码返回阵亡将士纪念日:

NSInteger aGivenYear = 2013 ;

NSDateComponents* memorialDayComponents = [NSDateComponents new] ;
memorialDayComponents.year = aGivenYear ;
memorialDayComponents.month = 5 ;
memorialDayComponents.weekday = 2 ; //Monday
memorialDayComponents.weekdayOrdinal = -1 ; //The last instance of the specified weekday in the specified month & year.
NSDate* memorialDay = [calendar dateFromComponents:memorialDayComponents] ;

【问题讨论】:

  • 我没有检查你的算法,但我想它会归结为大致那么复杂的东西。人们可能会分享一些计算劳动节、感恩节、Birthington 的洗衣日等的逻辑。
  • 劳动节、感恩节和总统日都比较简单,因为它们的weekOfMonths 是普通的整数,而阵亡将士纪念日则取决于一个月中有多少个星期一。太糟糕了...
  • 但是您仍然可以使用通用逻辑进行大部分计算。或者您可以作弊并要求 5 月的第 5 个星期一,如果计算不畅,则退回到第 4 个。
  • 啊,你是对的。 weekOfMonth 不像我想的那样工作,所以它不能很容易地计算劳动节、感恩节或总统节。我需要一些共享的逻辑来弄清楚这四个;我会再做一些实验。
  • Birthington 的洗衣日怎么样?

标签: xcode macos cocoa nsdate nsdatecomponents


【解决方案1】:

要获得一个月的第一个星期一,请设置 weekdayOrdinal = 1 而不是 weekOfMonth = 1

NSInteger aGivenYear = 2012 ;

NSCalendar* calendar = [NSCalendar currentCalendar] ;
NSDateComponents* firstMondayInJuneComponents = [NSDateComponents new] ;
firstMondayInJuneComponents.month = 6 ;
firstMondayInJuneComponents.weekdayOrdinal = 1 ;
firstMondayInJuneComponents.weekday = 2 ; //Monday
firstMondayInJuneComponents.year = aGivenYear ;
NSDate* firstMondayInJune = [calendar dateFromComponents:firstMondayInJuneComponents] ;
// --> 2012-06-04

NSDateComponents* subtractAWeekComponents = [NSDateComponents new] ;
subtractAWeekComponents.week = -1 ;
NSDate* memorialDay = [calendar dateByAddingComponents:subtractAWeekComponents toDate:firstMondayInJune options:0] ;
// --> 2012-05-28

来自NSDateComponents 文档:

工作日序数单位表示工作日在 下一个较大的日历单位,例如月份。例如,2 是 每月第二个星期五的工作日序数单位。

【讨论】:

  • 谢谢,@Martin,weekdayOrdinal 正是我所希望的,它用更少的 3 行代码返回了阵亡将士纪念日。虽然没有记录,但我很高兴发现负数 weekdayOrdinal 从月底开始倒计时,这非常适合确定阵亡将士纪念日。
  • 感谢您进行一些漂亮(且正确)的日期计算。
  • @DaveDeLong:谢谢(但“漂亮的外观”是从问题中复制而来的,他只得到了weekOfMonth 错误:-) - 你能评论约翰绍尔对weekdayOrdinal = -1 的观察吗?这是获得一个月中最后一个工作日的可靠方法吗?
  • @DaveDeLong 我同意 MartinR 的观点,很高兴从马口中听到关于负的 weekdayOrdinals 的消息。与此同时,my tests show them behaving as I'd hoped.
  • 我对 ICU 文档的查看(这是 NSCalendar 等的基础)似乎表明负序数应该会起作用,就像你在这里看到的那样。
【解决方案2】:

我可能会做的是获取每月第一天的星期几(可能使用 NSDateFormatter),然后计算第一个星期一/星期二/星期四/每月的任何一天的日期,然后添加(星期# - 1) x 7 到该日期以获得第 N 个星期一/每月的任何时间。

对于阵亡将士纪念日,您首先要检查第 5 个星期一是否还在 5 月,如果不是则使用第 4 个星期一。 (或者签到,知道如果阵亡将士纪念日是 31 日,那么第一个星期一必须是 3 日或更早。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 2021-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多