diff --git a/SoluDate.xcodeproj/project.pbxproj b/SoluDate.xcodeproj/project.pbxproj index afacb5e..9cfb514 100644 --- a/SoluDate.xcodeproj/project.pbxproj +++ b/SoluDate.xcodeproj/project.pbxproj @@ -454,6 +454,7 @@ B58CA6FE1EBF157200F03677 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/SoluDate/Info.plist b/SoluDate/Info.plist index fbe1e6b..68c0384 100644 --- a/SoluDate/Info.plist +++ b/SoluDate/Info.plist @@ -20,5 +20,9 @@ $(CURRENT_PROJECT_VERSION) NSPrincipalClass + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main diff --git a/SoluDate/SoluDate.swift b/SoluDate/SoluDate.swift index e890f72..1f85802 100644 --- a/SoluDate/SoluDate.swift +++ b/SoluDate/SoluDate.swift @@ -5,30 +5,16 @@ import Foundation -public class LuDate { - public var day: Int - public var month: Int - public var year: Int - public var leap: Int - - public init(day: Int, month: Int, year: Int, leap: Int = 0) { - self.day = day - self.month = month - self.year = year - self.leap = leap - } -} - public class SoluDateConverter { fileprivate static let PI = Double.pi - fileprivate static func juliusDaysFromDate(_ dd: Int, _ mm: Int, _ yy: Int) -> Int { - let a:Int = (14 - mm) / 12 - let y:Int = yy+4800-a - let m:Int = mm+12*a-3 - var jd:Int = dd + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 + fileprivate static func juliusDaysFromDate(_ day: Int, _ month: Int, _ year: Int) -> Int { + let a:Int = (14 - month) / 12 + let y:Int = year + 4800 - a + let m:Int = month + 12 * a - 3 + var jd:Int = day + (153 * m + 2)/5 + 365 * y + y/4 - y/100 + y/400 - 32045 if (jd < 2299161) { - jd = dd + (153*m+2)/5 + 365*y + y/4 - 32083 + jd = day + (153 * m + 2)/5 + 365 * y + y/4 - 32083 } return jd @@ -38,20 +24,20 @@ public class SoluDateConverter { var a: Int = 0 var b: Int = 0 var c: Int = 0 - if (jd > 2299160) { // After 5/10/1582, Gregorian calendar + if (jd > 2299160) { a = jd + 32044 - b = (4*a+3)/146097 - c = a - (b*146097)/4 + b = (4 * a + 3)/146097 + c = a - (b * 146097)/4 } else { b = 0 c = jd + 32082 } - let d: Int = (4*c+3)/1461 - let e: Int = c - (1461*d)/4 - let m: Int = (5*e+2)/153 - let day: Int = e - (153*m+2)/5 + 1 - let month: Int = m + 3 - 12*(m/10) - let year: Int = b*100 + d - 4800 + m/10 + let d: Int = (4 * c + 3)/1461 + let e: Int = c - (1461 * d)/4 + let m: Int = (5 * e + 2)/153 + let day: Int = e - (153 * m + 2)/5 + 1 + let month: Int = m + 3 - 12 * (m/10) + let year: Int = b * 100 + d - 4800 + m/10 return [day, month, year] } @@ -60,47 +46,47 @@ public class SoluDateConverter { } fileprivate static func sunLongitude(_ jdn: Double) -> Double { - //return CC2K.sunLongitude(jdn) return sunLongitudeAA98(jdn) } fileprivate static func sunLongitudeAA98(_ jdn: Double) -> Double { - let T: Double = (jdn - 2451545.0 ) / 36525 // Time in Julian centuries from 2000-01-01 12:00:00 GMT - let T2: Double = T*T - let dr: Double = SoluDateConverter.PI/180 // degree to radian - let M: Double = 357.52910 + 35999.05030*T - 0.0001559*T2 - 0.00000048*T*T2 // mean anomaly, degree - let L0: Double = 280.46645 + 36000.76983*T + 0.0003032*T2 // mean longitude, degree - var DL: Double = (1.914600 - 0.004817*T - 0.000014*T2)*sin(dr*M) - DL = DL + (0.019993 - 0.000101*T)*sin(dr*2*M) + 0.000290*sin(dr*3*M) - var L: Double = L0 + DL // true longitude, degree - L = L - 360*(Double(INT(L/360))) // Normalize to (0, 360) + let T: Double = (jdn - 2451545.0 ) / 36525 + let T2: Double = T * T + let dr: Double = SoluDateConverter.PI/180 + let M: Double = 357.52910 + 35999.05030 * T - 0.0001559 * T2 - 0.00000048 * T * T2 + let L0: Double = 280.46645 + 36000.76983 * T + 0.0003032 * T2 + var DL: Double = (1.914600 - 0.004817 * T - 0.000014 * T2) * sin(dr * M) + DL = DL + (0.019993 - 0.000101 * T) * sin(dr * 2 * M) + 0.000290 * sin(dr * 3 * M) + var L: Double = L0 + DL + L = L - 360 * (Double(INT(L/360))) return L } fileprivate static func newMoonAA98(_ k: Int) -> Double { - let T: Double = Double(k)/1236.85 // Time in Julian centuries from 1900 January 0.5 + let T: Double = Double(k)/1236.85 let T2: Double = T * T let T3: Double = T2 * T let dr: Double = SoluDateConverter.PI/180 - var Jd1: Double = 2415020.75933 + 29.53058868*Double(k) + 0.0001178*T2 - 0.000000155*T3 - Jd1 = Jd1 + 0.00033*sin((166.56 + 132.87*T - 0.009173*T2)*dr) - let M: Double = 359.2242 + 29.10535608*Double(k) - 0.0000333*T2 - 0.00000347*T3 - let Mpr: Double = 306.0253 + 385.81691806*Double(k) + 0.0107306*T2 + 0.00001236*T3 // Moon's mean anomaly - let F: Double = 21.2964 + 390.67050646*Double(k) - 0.0016528*T2 - 0.00000239*T3 // Moon's argument of latitude - var C1: Double = (0.1734 - 0.000393*T)*sin(M*dr) + 0.0021*sin(2*dr*M) - C1 = C1 - 0.4068*sin(Mpr*dr) + 0.0161*sin(dr*2*Mpr) - C1 = C1 - 0.0004*sin(dr*3*Mpr) - C1 = C1 + 0.0104*sin(dr*2*F) - 0.0051*sin(dr*(M+Mpr)) - C1 = C1 - 0.0074*sin(dr*(M-Mpr)) + 0.0004*sin(dr*(2*F+M)) - C1 = C1 - 0.0004*sin(dr*(2*F-M)) - 0.0006*sin(dr*(2*F+Mpr)) - C1 = C1 + 0.0010*sin(dr*(2*F-Mpr)) + 0.0005*sin(dr*(2*Mpr+M)) + var Jd1: Double = 2415020.75933 + 29.53058868 * Double(k) + 0.0001178 * T2 - 0.000000155 * T3 + Jd1 = Jd1 + 0.00033 * sin((166.56 + 132.87 * T - 0.009173 * T2) * dr) + let M: Double = 359.2242 + 29.10535608 * Double(k) - 0.0000333 * T2 - 0.00000347 * T3 + let Mpr: Double = 306.0253 + 385.81691806 * Double(k) + 0.0107306 * T2 + 0.00001236 * T3 + let F: Double = 21.2964 + 390.67050646 * Double(k) - 0.0016528 * T2 - 0.00000239 * T3 + var C1: Double = (0.1734 - 0.000393 * T) * sin(M * dr) + 0.0021 * sin(2 * dr * M) + + C1 = C1 - 0.4068 * sin(Mpr*dr) + 0.0161 * sin(dr * 2*Mpr) + C1 = C1 - 0.0004 * sin(dr * 3 * Mpr) + C1 = C1 + 0.0104 * sin(dr * 2 * F) - 0.0051 * sin(dr * (M + Mpr)) + C1 = C1 - 0.0074 * sin(dr * (M - Mpr)) + 0.0004 * sin(dr * (2 * F + M)) + C1 = C1 - 0.0004 * sin(dr * (2 * F - M)) - 0.0006 * sin(dr * (2 * F + Mpr)) + C1 = C1 + 0.0010 * sin(dr * (2 * F - Mpr)) + 0.0005 * sin(dr * (2 * Mpr + M)) var deltat: Double = 0 if (T < -11) { - deltat = 0.001 + 0.000839*T + 0.0002261*T2 - 0.00000845*T3 - 0.000000081*T*T3 + deltat = 0.001 + 0.000839 * T + 0.0002261 * T2 - 0.00000845 * T3 - 0.000000081 * T * T3 } else { - deltat = -0.000278 + 0.000265*T + 0.000262*T2 + deltat = -0.000278 + 0.000265 * T + 0.000262 * T2 } let JdNew: Double = Jd1 + C1 - deltat @@ -109,7 +95,6 @@ public class SoluDateConverter { } fileprivate static func newMoon(_ k:Int) -> Double { - //return CC2K.newMoonTime(k) return newMoonAA98(k) } @@ -122,8 +107,8 @@ public class SoluDateConverter { return INT(jd + 0.5 + timeZone/24) } - fileprivate static func getLunarMonth11(_ yy: Int, _ timeZone: Double) -> Int { - let off: Double = Double(juliusDaysFromDate(31, 12, yy)) - 2415021.076998695 + fileprivate static func getLunarMonth11(_ year: Int, _ timeZone: Double) -> Int { + let off: Double = Double(juliusDaysFromDate(31, 12, year)) - 2415021.076998695 let k: Int = INT(off / 29.530588853) var nm: Int = getNewMoonDay(k, timeZone) let sunLong: Int = INT(getSunLongitude(nm, timeZone)/30) @@ -135,38 +120,38 @@ public class SoluDateConverter { fileprivate static func getLeapMonthOffset(_ a11: Int,_ timeZone: Double) -> Int { let k: Int = INT(0.5 + (Double(a11) - 2415021.076998695) / 29.530588853) - var last: Int = 0 // Month 11 contains point of sun longutide 3*PI/2 (December solstice) - var i: Int = 1 // We start with the month following lunar month 11 - var arc: Int = INT(getSunLongitude(getNewMoonDay(k+i, timeZone), timeZone)/30) + var last: Int = 0 + var i: Int = 1 + var arc: Int = INT(getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone)/30) repeat { last = arc i = i + 1 - arc = INT(getSunLongitude(getNewMoonDay(k+i, timeZone), timeZone)/30) + arc = INT(getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone)/30) } while (arc != last && i < 14) return i-1 } - fileprivate static func convertSolar2Lunar(_ dd: Int,_ mm: Int,_ yy: Int,_ timeZone: Double) -> [Int] { + fileprivate static func convertSolar2Lunar(_ day: Int, _ month: Int, _ year: Int, _ timeZone: Double) -> [Int] { var lunarDay: Int = 0 var lunarMonth: Int = 0 var lunarYear: Int = 0 var lunarLeap: Int = 0 - let dayNumber: Int = juliusDaysFromDate(dd, mm, yy) + let dayNumber: Int = juliusDaysFromDate(day, month, year) let k: Int = INT((Double(dayNumber) - 2415021.076998695) / 29.530588853) var monthStart: Int = getNewMoonDay(k+1, timeZone) if (monthStart > dayNumber) { monthStart = getNewMoonDay(k, timeZone) } - var a11: Int = getLunarMonth11(yy, timeZone) + var a11: Int = getLunarMonth11(year, timeZone) var b11: Int = a11 if (a11 >= monthStart) { - lunarYear = yy - a11 = getLunarMonth11(yy-1, timeZone) + lunarYear = year + a11 = getLunarMonth11(year - 1, timeZone) } else { - lunarYear = yy+1 - b11 = getLunarMonth11(yy+1, timeZone) + lunarYear = year + 1 + b11 = getLunarMonth11(year + 1, timeZone) } lunarDay = dayNumber-monthStart+1 let diff: Int = INT((Double(monthStart) - Double(a11))/29) @@ -192,7 +177,8 @@ public class SoluDateConverter { return [lunarDay, lunarMonth, lunarYear, lunarLeap] } - fileprivate static func convertLunar2Solar(_ lunarDay: Int,_ lunarMonth: Int,_ lunarYear: Int,_ lunarLeap: Int,_ timeZone: Double) -> [Int]? { + fileprivate static func convertLunar2Solar(_ lunarDay: Int,_ lunarMonth: Int,_ lunarYear: Int,_ lunarLeap: Int, + _ timeZone: Double) -> [Int]? { var a11: Int = 0 var b11: Int = 0 if (lunarMonth < 11) { @@ -223,15 +209,54 @@ public class SoluDateConverter { return dateFromJuliusDays(monthStart+lunarDay-1) } + + fileprivate static func leapFromDate(_ day: Int,_ month: Int,_ year: Int) -> Int { + var lunarDay: Int = 0 + var lunarMonth: Int = 0 + var lunarYear: Int = 0 + var lunarLeap: Int = 0 + let timeZone: Double = 0 + let dayNumber: Int = juliusDaysFromDate(day, month, year) + let k: Int = INT((Double(dayNumber) - 2415021.076998695) / 29.530588853) + var monthStart: Int = getNewMoonDay(k+1, timeZone) + if (monthStart > dayNumber) { + monthStart = getNewMoonDay(k, timeZone) + } + var a11: Int = getLunarMonth11(year, timeZone) + var b11: Int = a11 + if (a11 >= monthStart) { + lunarYear = year + a11 = getLunarMonth11(year-1, timeZone) + } else { + lunarYear = year+1 + b11 = getLunarMonth11(year+1, timeZone) + } + lunarDay = dayNumber-monthStart+1 + let diff: Int = INT((Double(monthStart) - Double(a11))/29) + lunarLeap = 0 + lunarMonth = diff + 11 + + if (b11 - a11 > 365) { + let leapMonthDiff: Int = getLeapMonthOffset(a11, timeZone) + if (diff >= leapMonthDiff) { + lunarMonth = diff + 10 + if (diff == leapMonthDiff) { + lunarLeap = 1 + } + } + } + + return lunarLeap + } } extension SoluDateConverter { - public static func lunarDateFromSonarDate(_ date: Date) -> LuDate { + public static func lunarDateFromSonarDate(_ date: Date) -> Date? { let comps: DateComponents = Calendar.current.dateComponents([.day, .month, .year, .timeZone], from: date) let day = comps.day! let month = comps.month! let year = comps.year! - let rawComs = convertSolar2Lunar(day, month, year, 0.0) + let rawComs = convertSolar2Lunar(day, month, year, 7.0) var lunarComps = DateComponents() lunarComps.day = rawComs[0] lunarComps.month = rawComs[1] @@ -239,26 +264,33 @@ extension SoluDateConverter { lunarComps.year = rawComs[2] let leap = rawComs[3] lunarComps.isLeapMonth = Bool(leap as NSNumber) - - let lunarDate: LuDate = LuDate(day: rawComs[0], month: rawComs[1], year: rawComs[2], leap: rawComs[3]) - - return lunarDate + + let date = NSCalendar(identifier: NSCalendar.Identifier.gregorian)?.date(from: lunarComps as DateComponents) + + return date } - public static func sonarDateFromLunarDate(_ date: LuDate) -> Date? { - let rawComs = convertLunar2Solar(date.day, date.month, date.year, date.leap, 7.0) - guard let rawSonarComps = rawComs else { - return nil + public static func sonarDateFromLunarDate(_ date: Date) -> Date? { + let comps: DateComponents = Calendar.current.dateComponents([.day, .month, .year, .timeZone], from: date) + let day = comps.day! + let month = comps.month! + let year = comps.year! + let leap = leapFromDate(day, month, year) + let jdComps = convertLunar2Solar(day, month, year, leap, 7.0) + + var date: Date? + + if let jdComps = jdComps { + let c = NSDateComponents() + c.day = jdComps[0] + c.month = jdComps[1] + c.year = jdComps[2] + + date = NSCalendar(identifier: NSCalendar.Identifier.gregorian)?.date(from: c as DateComponents) + } else { + date = nil } - var sonarComps: DateComponents = DateComponents() - sonarComps.day = rawSonarComps[0] - sonarComps.month = rawSonarComps[1] - sonarComps.year = rawSonarComps[2] - let leap = rawSonarComps[3] - sonarComps.isLeapMonth = Bool(leap as NSNumber) - - let sonarDate: Date? = sonarComps.date - - return sonarDate + + return date } } diff --git a/SoluDate/ViewController.swift b/SoluDate/ViewController.swift index ffb694b..42c20be 100644 --- a/SoluDate/ViewController.swift +++ b/SoluDate/ViewController.swift @@ -14,15 +14,25 @@ class ViewController: UIViewController { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. - let date = SoluDateConverter.lunarDateFromSonarDate(Date()) - print("date \(date.day) \(date.month) \(date.year)") + let c = NSDateComponents() + c.day = 12 + c.month = 4 + c.year = 2017 + + let lunarDate = NSCalendar(identifier: NSCalendar.Identifier.gregorian)?.date(from: c as DateComponents) + + let sonarDate = SoluDateConverter.sonarDateFromLunarDate(lunarDate!) + + print(sonarDate) + + let date = SoluDateConverter.lunarDateFromSonarDate(sonarDate!) + print("Lunar date \(date)") + } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } - - }