问题
I'm working on an application that requires the use of getting dates for national holidays.
Below, I was able to get Memorial Day:
// Set the components for Memorial Day (last Monday of May)
let memorialDayComps = NSDateComponents()
memorialDayComps.weekday = 2
memorialDayComps.month = 5
memorialDayComps.year = currentYear
var mondaysOfMay = [NSDate]()
for var i = 1; i <= 5; i++ {
memorialDayComps.weekdayOrdinal = i
let monday = calendar.dateFromComponents(memorialDayComps)
let components = calendar.components(.CalendarUnitMonth, fromDate: monday!)
if components.month == 5 {
mondaysOfMay.append(monday!)
}
}
let memorialDayDate = mondaysOfMay.last
Because the dates are pretty well set, I am able to successfully create NSDate
instances for the following holidays:
- New Year's Day
- Martin Luther King, Jr. Day
- Presidents' Day
- Memorial Day
- Independence Day
- Labor Day
- Thanksgiving Day
- Christmas Day
However, the only one that I am having difficulty figuring out how to get is Easter. It varies every year, so I'm curious as to whether anyone else has been able so successfully get the date for Easter via an API or other means.
回答1:
I was able to find a gist on GitHub that has a solution that was accurate for calculating and returning an NSDate
for Easter.
The code below is what the gist contains:
// Easter calculation in swift after Anonymous Gregorian algorithm
// Also known as Meeus/Jones/Butcher algorithm
func easter(Y : Int) -> NSDate {
let a = Y % 19
let b = Int(floor(Double(Y) / 100))
let c = Y % 100
let d = Int(floor(Double(b) / 4))
let e = b % 4
let f = Int(floor(Double(b+8) / 25))
let g = Int(floor(Double(b-f+1) / 3))
let h = (19*a + b - d - g + 15) % 30
let i = Int(floor(Double(c) / 4))
let k = c % 4
let L = (32 + 2*e + 2*i - h - k) % 7
let m = Int(floor(Double(a + 11*h + 22*L) / 451))
let components = NSDateComponents()
components.year = Y
components.month = Int(floor(Double(h + L - 7*m + 114) / 31))
components.day = ((h + L - 7*m + 114) % 31) + 1
components.timeZone = NSTimeZone(forSecondsFromGMT: 0)
let cal = NSCalendar(calendarIdentifier: NSGregorianCalendar)
return cal.dateFromComponents(components)
}
println(easter(2014)) // "2014-04-20 00:00:00 +0000"
回答2:
That Easter algorithm works great! Using with Swift 4.0 and pattern matching. Pattern matching made it easier for me to add other days based on month, day, weekday, weekdayOrdinal.
extension Date {
var isUSHoliday: Bool {
let components = Calendar.current.dateComponents([.year, .month, .day, .weekday, .weekdayOrdinal], from: self)
guard let year = components.year,
let month = components.month,
let day = components.day,
let weekday = components.weekday,
let weekdayOrdinal = components.weekdayOrdinal else { return false }
let easterDateComponents = Date.dateComponentsForEaster(year: year)
let easterMonth: Int = easterDateComponents?.month ?? -1
let easterDay: Int = easterDateComponents?.day ?? -1
let memorialDay = Date.dateComponentsForMemorialDay(year: year)?.day ?? -1
// weekday is Sunday==1 ... Saturday==7
// weekdayOrdinal is nth instance of weekday in month
switch (month, day, weekday, weekdayOrdinal) {
case (1, 1, _, _): return true // Happy New Years
case (1, 0, 2, 3): return true // MLK - 3rd Mon in Jan
case (2, 0, 2, 3): return true // Washington - 3rd Mon in Feb
case (easterMonth, easterDay, _, _): return true // Easter - rocket science calculation
case (5, memorialDay, _, _): return true // Memorial Day
case (7, 4, _, _): return true // Independence Day
case (9, 0, 2, 1): return true // Labor Day - 1st Mon in Sept
case (10, 0, 2, 2): return true // Columbus Day - 2nd Mon in Oct
case (11, 11, _, _): return true // Veterans Day
case (11, 0, 5, 4): return true // Happy Thanksgiving - 4th Thurs in Nov
case (12, 25, _, _): return true // Happy Holidays
case (12, 31, _, _): return true // New years Eve
default: return false
}
}
static func dateComponentsForMemorialDay(year: Int) -> DateComponents? {
guard let memorialDay = Date.memorialDay(year: year) else { return nil }
return NSCalendar.current.dateComponents([.year, .month, .day, .weekday, .weekdayOrdinal], from: memorialDay)
}
static func memorialDay(year: Int) -> Date? {
let calendar = Calendar.current
var firstMondayJune = DateComponents()
firstMondayJune.month = 6
firstMondayJune.weekdayOrdinal = 1 // 1st in month
firstMondayJune.weekday = 2 // Monday
firstMondayJune.year = year
guard let refDate = calendar.date(from: firstMondayJune) else { return nil }
var timeMachine = DateComponents()
timeMachine.weekOfMonth = -1
return calendar.date(byAdding: timeMachine, to: refDate)
}
static func easterHoliday(year: Int) -> Date? {
guard let dateComponents = Date.dateComponentsForEaster(year: year) else { return nil }
return Calendar.current.date(from: dateComponents)
}
static func dateComponentsForEaster(year: Int) -> DateComponents? {
// Easter calculation from Anonymous Gregorian algorithm
// AKA Meeus/Jones/Butcher algorithm
let a = year % 19
let b = Int(floor(Double(year) / 100))
let c = year % 100
let d = Int(floor(Double(b) / 4))
let e = b % 4
let f = Int(floor(Double(b+8) / 25))
let g = Int(floor(Double(b-f+1) / 3))
let h = (19*a + b - d - g + 15) % 30
let i = Int(floor(Double(c) / 4))
let k = c % 4
let L = (32 + 2*e + 2*i - h - k) % 7
let m = Int(floor(Double(a + 11*h + 22*L) / 451))
var dateComponents = DateComponents()
dateComponents.month = Int(floor(Double(h + L - 7*m + 114) / 31))
dateComponents.day = ((h + L - 7*m + 114) % 31) + 1
dateComponents.year = year
guard let easter = Calendar.current.date(from: dateComponents) else { return nil } // Convert to calculate weekday, weekdayOrdinal
return Calendar.current.dateComponents([.year, .month, .day, .weekday, .weekdayOrdinal], from: easter)
}
}
回答3:
Swift 4:
func easter(Y : Int) -> Date {
let a = Y % 19
let b = Int(floor(Double(Y) / 100))
let c = Y % 100
let d = Int(floor(Double(b) / 4))
let e = b % 4
let f = Int(floor(Double(b+8) / 25))
let g = Int(floor(Double(b-f+1) / 3))
let h = (19*a + b - d - g + 15) % 30
let i = Int(floor(Double(c) / 4))
let k = c % 4
let L = (32 + 2*e + 2*i - h - k) % 7
let m = Int(floor(Double(a + 11*h + 22*L) / 451))
var components = DateComponents()
components.year = Y
components.month = Int(floor(Double(h + L - 7*m + 114) / 31))
components.day = ((h + L - 7*m + 114) % 31) + 1
components.timeZone = TimeZone(secondsFromGMT: 0)
return Calendar.autoupdatingCurrent.date(from: components)!
}
print(easter(Y: 2018)) // "2018-04-01 00:00:00 +0000"
回答4:
OBJECTIVE-C!
-(void) easterMonthAndDayForYear: (NSInteger) Y {
NSInteger a = Y % 19;
NSInteger b = (int) (floor( ((double)Y) / 100.0));
NSInteger c = Y % 100;
NSInteger d = (int)(floor(((double)b) / 4.0));
NSInteger e = b % 4;
NSInteger f = (int)(floor(((double)(b+8)) / 25.0));
NSInteger g = (int)(floor(((double)(b-f+1)) / 3.0));
NSInteger h = (19*a + b - d - g + 15) % 30;
NSInteger i = (int)(floor(((double)c) / 4.0));
NSInteger k = c % 4;
NSInteger L = (32 + 2*e + 2*i - h - k) % 7;
NSInteger m = (int)(floor(((double)(a + 11*h + 22*L)) / 451.0));
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear |NSCalendarUnitTimeZone fromDate:[NSDate date]];
components.year = Y;
components.month = (int)(floor((double)(h + L - 7*m + 114) / 31.0));
components.day = ((h + L - 7*m + 114) % 31) + 1;
components.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
self.easterDayCache[@(Y)] = @{@"month": @(components.month), @"day":@(components.day)
};
来源:https://stackoverflow.com/questions/30836183/using-nsdate-to-get-date-for-easter