├── Tests └── LunarSwiftTests │ ├── LunarYearTests.swift │ ├── LiuYaoTests.swift │ ├── FuTests.swift │ ├── HouTests.swift │ ├── ShuJiuTests.swift │ ├── TaoTests.swift │ ├── FestivalTests.swift │ ├── FotoTests.swift │ ├── NineStarTests.swift │ ├── HolidayTests.swift │ ├── LunarMonthTests.swift │ ├── SolarTests.swift │ ├── EightCharTests.swift │ └── LunarTests.swift ├── Sources └── LunarSwift │ ├── Fu.swift │ ├── ShuJiu.swift │ ├── TaoFestival.swift │ ├── JieQi.swift │ ├── FotoFestival.swift │ ├── SolarYear.swift │ ├── Holiday.swift │ ├── LiuNian.swift │ ├── LiuYue.swift │ ├── XiaoYun.swift │ ├── SolarHalfYear.swift │ ├── SolarSeason.swift │ ├── SolarMonth.swift │ ├── DaYun.swift │ ├── LunarMonth.swift │ ├── Yun.swift │ ├── NineStar.swift │ ├── TaoUtil.swift │ ├── Tao.swift │ ├── LunarTime.swift │ ├── Foto.swift │ ├── SolarWeek.swift │ ├── FotoUtil.swift │ ├── SolarUtil.swift │ ├── LunarYear.swift │ ├── EightChar.swift │ ├── Solar.swift │ └── HolidayUtil.swift ├── README_EN.md ├── CHANGELOG.md ├── README.md ├── LICENSE ├── Package.swift ├── .gitignore └── LunarSwift.podspec /Tests/LunarSwiftTests/LunarYearTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class LunarYearTests: XCTestCase { 5 | func test1() throws { 6 | XCTAssertEqual(LunarYear(lunarYear: 2023).dayCount, 384) 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/LiuYaoTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class LiuYaoTests: XCTestCase { 5 | func test1() throws { 6 | XCTAssertEqual(Solar.fromYmdHms(year: 2020, month: 12, day: 11).lunar.liuYao, "赤口") 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/FuTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class FuTests: XCTestCase { 5 | func test1() throws { 6 | let fu = Solar.fromYmdHms(year: 2011, month: 7, day: 14).lunar.fu 7 | XCTAssertEqual(fu!.description, "初伏") 8 | XCTAssertEqual(fu!.fullString, "初伏第1天") 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/HouTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class HouTests: XCTestCase { 5 | func test1() throws { 6 | let lunar = Solar.fromYmdHms(year: 2023, month: 3, day: 21).lunar 7 | XCTAssertEqual(lunar.hou.description, "春分 初候") 8 | XCTAssertEqual(lunar.wuHou.description, "玄鸟至") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/ShuJiuTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class ShuJiuTests: XCTestCase { 5 | func test1() throws { 6 | let shuJiu = Solar.fromYmdHms(year: 2021, month: 3, day: 5).lunar.shuJiu 7 | XCTAssertEqual(shuJiu!.description, "九九") 8 | XCTAssertEqual(shuJiu!.fullString, "九九第3天") 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/TaoTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class TaoTests: XCTestCase { 5 | func test1() throws { 6 | let tao = Tao.fromLunar(lunar: Lunar.fromYmdHms(lunarYear: 2021, lunarMonth: 10, lunarDay: 17, hour: 18)) 7 | XCTAssertEqual(tao.description, "四七一八年十月十七") 8 | XCTAssertEqual(tao.fullString, "道歷四七一八年,天運辛丑年,己亥月,癸酉日。十月十七日,酉時。") 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/FestivalTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class FestivalTests: XCTestCase { 5 | func test1() throws { 6 | let solar = Solar.fromYmdHms(year: 2020, month: 11, day: 26) 7 | XCTAssertEqual(solar.festivals, ["感恩节"]) 8 | } 9 | 10 | func test2() throws { 11 | let solar = Solar.fromYmdHms(year: 2020, month: 6, day: 21) 12 | XCTAssertEqual(solar.festivals, ["父亲节"]) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/FotoTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class FotoTests: XCTestCase { 5 | func test1() throws { 6 | let foto = Foto.fromLunar(lunar: Lunar.fromYmdHms(lunarYear: 2021, lunarMonth: 10, lunarDay: 14)) 7 | XCTAssertEqual(foto.fullString, "二五六五年十月十四 (三元降) (四天王巡行)") 8 | } 9 | 10 | func test2() throws { 11 | let foto = Foto.fromLunar(lunar: Lunar.fromYmdHms(lunarYear: 2021, lunarMonth: 3, lunarDay: 16)) 12 | XCTAssertEqual(foto.otherFestivals[0], "准提菩萨圣诞") 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/NineStarTests.swift: -------------------------------------------------------------------------------- 1 | @testable import LunarSwift 2 | import XCTest 3 | 4 | final class NineStarTests: XCTestCase { 5 | func test1() throws { 6 | let lunar = Solar.fromYmdHms(year: 1985, month: 2, day: 19).lunar 7 | XCTAssertEqual(lunar.yearNineStar.number, "六") 8 | } 9 | 10 | func test2() throws { 11 | let lunar = Solar.fromYmdHms(year: 2022, month: 1, day: 1).lunar 12 | XCTAssertEqual(lunar.yearNineStar.description, "六白金开阳") 13 | } 14 | 15 | func test3() throws { 16 | let lunar = Solar.fromYmdHms(year: 2033, month: 1, day: 1).lunar 17 | XCTAssertEqual(lunar.yearNineStar.description, "四绿木天权") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/HolidayTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class HolidayTests: XCTestCase { 5 | func test1() throws { 6 | let holiday = HolidayUtil.getHolidayByYmd(year: 2021, month: 6, day: 14) 7 | XCTAssertEqual(holiday!.description, "2021-06-14 端午节 2021-06-14") 8 | } 9 | 10 | func test2() throws { 11 | let holiday = HolidayUtil.getHolidayByYmd(year: 2016, month: 10, day: 4) 12 | XCTAssertEqual(holiday!.target, "2016-10-01") 13 | } 14 | 15 | func test3() throws { 16 | let holiday = HolidayUtil.getHolidayByYmd(year: 2024, month: 10, day: 4) 17 | XCTAssertEqual(holiday!.target, "2024-10-01") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/LunarSwift/Fu.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class Fu: NSObject { 5 | private var _name: String 6 | private var _index: Int 7 | 8 | public var name: String { 9 | get { 10 | _name 11 | } 12 | } 13 | 14 | public var index: Int { 15 | get { 16 | _index 17 | } 18 | } 19 | 20 | public init(name: String, index: Int) { 21 | _name = name 22 | _index = index 23 | } 24 | 25 | public override var description: String { 26 | get { 27 | "\(_name)" 28 | } 29 | } 30 | 31 | public var fullString: String { 32 | get { 33 | "\(_name)第\(_index)天" 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Sources/LunarSwift/ShuJiu.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class ShuJiu: NSObject { 5 | private var _name: String 6 | private var _index: Int 7 | 8 | public var name: String { 9 | get { 10 | _name 11 | } 12 | } 13 | 14 | public var index: Int { 15 | get { 16 | _index 17 | } 18 | } 19 | 20 | public init(name: String, index: Int) { 21 | _name = name 22 | _index = index 23 | } 24 | 25 | public override var description: String { 26 | get { 27 | "\(_name)" 28 | } 29 | } 30 | 31 | public var fullString: String { 32 | get { 33 | "\(_name)第\(_index)天" 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/LunarMonthTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class LunarMonthTests: XCTestCase { 5 | func test1() throws { 6 | let month = LunarMonth.fromYm(lunarYear: 2023, lunarMonth: 1)! 7 | XCTAssertEqual(month.index, 1) 8 | XCTAssertEqual(month.ganZhi, "甲寅") 9 | } 10 | 11 | func test2() throws { 12 | let month = LunarMonth.fromYm(lunarYear: 2023, lunarMonth: -2)! 13 | XCTAssertEqual(month.index, 3) 14 | XCTAssertEqual(month.ganZhi, "乙卯") 15 | } 16 | 17 | func test3() throws { 18 | let month = LunarMonth.fromYm(lunarYear: 2023, lunarMonth: 12)! 19 | XCTAssertEqual(month.index, 13) 20 | XCTAssertEqual(month.ganZhi, "乙丑") 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/LunarSwift/TaoFestival.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class TaoFestival: NSObject { 5 | private var _name: String 6 | private var _remark: String 7 | 8 | public var name: String { 9 | get { 10 | _name 11 | } 12 | } 13 | 14 | public var remark: String { 15 | get { 16 | _remark 17 | } 18 | } 19 | 20 | public init(name: String, remark: String = "") { 21 | _name = name 22 | _remark = remark 23 | } 24 | 25 | public override var description: String { 26 | get { 27 | "\(_name)" 28 | } 29 | } 30 | 31 | public var fullString: String { 32 | get { 33 | var s = "\(_name)" 34 | if _remark.count > 0 { 35 | s.append(_remark) 36 | } 37 | return s 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # lunar [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/6tail/lunar-swift/blob/master/LICENSE) 2 | 3 | lunar is a calendar library for Solar and Chinese Lunar. 4 | 5 | > Support since swift 5.5.1 6 | 7 | [简体中文](https://github.com/6tail/lunar-swift/blob/master/README.md) 8 | 9 | ## Example 10 | 11 | let lunar = Lunar.fromYmdHms(lunarYear: 1986, lunarMonth: 4, lunarDay: 21) 12 | print(lunar.description) 13 | print(lunar.solar.fullString) 14 | 15 | Output: 16 | 17 | 一九八六年四月廿一 18 | 1986-05-29 00:00:00 星期四 双子座 19 | 20 | ## Documentation 21 | 22 | Please visit [https://6tail.cn/calendar/api.html](https://6tail.cn/calendar/api.html "https://6tail.cn/calendar/api.html") 23 | 24 | ## Star History 25 | 26 | [![Star History Chart](https://api.star-history.com/svg?repos=6tail/lunar-swift&type=Date)](https://star-history.com/#6tail/lunar-swift&Date) 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | 5 | ## [1.0.8] - 2024-02-29 6 | 1. 修复节气当天获取下一节气仍为当前节气的问题。 7 | 2. 修复每日宜忌存在重复项的问题。 8 | 3. 八字转阳历结果按时间先后排序,转换速度大幅提升。 9 | 10 | ## [1.0.9] - 2024-03-18 11 | 1. 修复八字转阳历存在遗漏的问题。 12 | 13 | ## [1.0.10] - 2024-05-22 14 | 1. 完善LunarYear。 15 | 16 | ## [1.0.11] - 2024-11-12 17 | 1. 新增2025年法定假日数据。 18 | 19 | ## [1.1.0] - 2025-02-06 20 | 1. 修复九星错别字。 21 | 2. 优化每日宜忌算法。 22 | 3. 去除每日宜忌的重复项。 23 | 24 | ## [1.1.1] - 2025-02-28 25 | 1. 修复每日宜忌错别字。 26 | 2. 修复每日宜忌错误。 27 | 28 | 29 | ## [1.1.2] - 2025-03-06 30 | 1. 修复每日宜忌错误。 31 | 2. 更新2018之后的△T参数。 32 | 33 | ## [1.1.3] - 2025-04-07 34 | 1. 修复八字身宫计算错误的问题。 35 | 36 | ## [1.1.4] - 2025-04-29 37 | 1. 修复身宫报错的问题。 38 | 39 | ## [1.1.5] - 2025-07-21 40 | 1. 补充遗漏的获取上一气和下一气的方法。 41 | 42 | ## [1.1.6] - 2025-10-16 43 | 1. 修复:极端情况下八字转公历时刻出现遗漏的问题。 44 | 2. 优化:更新△T参数。 45 | 3. 优化:吉神宜驱、凶神宜忌。 46 | 4. 修复:候的错别字。 47 | 48 | ## [1.1.7] - 2025-10-21 49 | 1. 修复:吉神宜驱、凶神宜忌的错误。 50 | 2. 修复:农历闰月及后续月份的干支错误。 51 | 52 | ## [1.1.8] - 2025-11-05 53 | 1. 新增:2026年法定假日。 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lunar [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/6tail/lunar-swift/blob/master/LICENSE) 2 | 3 | lunar是一款无第三方依赖的日历工具,支持公历(阳历)、农历(阴历、老黄历)、道历和佛历,支持星座、儒略日、干支、生肖、节气、节日、彭祖百忌、吉神(喜神/福神/财神/阳贵神/阴贵神)方位、胎神方位、冲煞、纳音、星宿、八字、五行、十神、建除十二值星、青龙名堂等十二神、黄道日及吉凶、法定节假日及调休等。 4 | 5 | > 基于swift 5.5.1版本开发 6 | 7 | [English](https://github.com/6tail/lunar-swift/blob/master/README_EN.md) 8 | 9 | ## 示例 10 | 11 | let lunar = Lunar.fromYmdHms(lunarYear: 1986, lunarMonth: 4, lunarDay: 21) 12 | print(lunar.description) 13 | print(lunar.solar.fullString) 14 | 15 | 输出结果: 16 | 17 | 一九八六年四月廿一 18 | 1986-05-29 00:00:00 星期四 双子座 19 | 20 | ## 文档 21 | 22 | 请移步至 [https://6tail.cn/calendar/api.html](https://6tail.cn/calendar/api.html "https://6tail.cn/calendar/api.html") 23 | 24 | ## Star History 25 | 26 | [![Star History Chart](https://api.star-history.com/svg?repos=6tail/lunar-swift&type=Date)](https://star-history.com/#6tail/lunar-swift&Date) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 6tail 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "LunarSwift", 8 | products: [ 9 | // Products define the executables and libraries a package produces, and make them visible to other packages. 10 | .library( 11 | name: "LunarSwift", 12 | targets: ["LunarSwift"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 21 | .target( 22 | name: "LunarSwift", 23 | dependencies: []), 24 | .testTarget( 25 | name: "LunarSwiftTests", 26 | dependencies: ["LunarSwift"]), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /Sources/LunarSwift/JieQi.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class JieQi: NSObject { 5 | private var _name: String 6 | private var _solar: Solar 7 | private var _jie: Bool = false 8 | private var _qi: Bool = false 9 | 10 | public var name: String { 11 | get { 12 | _name 13 | } 14 | } 15 | 16 | public var jie: Bool { 17 | get { 18 | _jie 19 | } 20 | } 21 | 22 | public var qi: Bool { 23 | get { 24 | _qi 25 | } 26 | } 27 | 28 | public var solar: Solar { 29 | get { 30 | _solar 31 | } 32 | } 33 | 34 | public init(name: String, solar: Solar) { 35 | _name = name 36 | _solar = solar 37 | for i in (0.. 0 { 51 | s.append(" \(_result)") 52 | } 53 | if _remark.count > 0 { 54 | s.append(" \(_remark)") 55 | } 56 | return s 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Sources/LunarSwift/SolarYear.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class SolarYear: NSObject { 5 | public static var MONTH_COUNT = 12 6 | 7 | private var _year: Int 8 | 9 | public var year: Int { 10 | get { 11 | _year 12 | } 13 | } 14 | 15 | public init(year: Int) { 16 | _year = year 17 | } 18 | 19 | public init(date: Date) { 20 | _year = Calendar.current.component(.year, from: date) 21 | } 22 | 23 | public convenience override init() { 24 | self.init(date: Date()) 25 | } 26 | 27 | public class func fromYear(year: Int) -> SolarYear { 28 | SolarYear(year: year) 29 | } 30 | 31 | public class func fromDate(date: Date) -> SolarYear { 32 | SolarYear(date: date) 33 | } 34 | 35 | public var months: [SolarMonth] { 36 | get { 37 | var l = [SolarMonth]() 38 | let m = SolarMonth(year: _year, month: 1) 39 | l.append(m) 40 | for i in (1.. SolarYear { 48 | SolarYear.fromYear(year: _year + years) 49 | } 50 | 51 | public override var description: String { 52 | get { 53 | "\(_year)" 54 | } 55 | } 56 | 57 | public var fullString: String { 58 | get { 59 | "\(_year)年" 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Sources/LunarSwift/Holiday.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class Holiday: NSObject { 5 | private var _day: String 6 | private var _name: String 7 | private var _work: Bool 8 | private var _target: String 9 | 10 | public var day: String { 11 | get { 12 | _day 13 | } 14 | } 15 | 16 | public var name: String { 17 | get { 18 | _name 19 | } 20 | } 21 | 22 | public var work: Bool { 23 | get { 24 | _work 25 | } 26 | } 27 | 28 | public var target: String { 29 | get { 30 | _target 31 | } 32 | } 33 | 34 | public init(day: String, name: String, work: Bool, target: String) { 35 | if !day.contains("-") { 36 | let y = day.prefix(4) 37 | let m = day[day.index(day.startIndex, offsetBy: 4).. 0 { 52 | offset += _daYun.startAge - 1 53 | } 54 | offset %= LunarUtil.JIA_ZI.count 55 | return LunarUtil.JIA_ZI[offset] 56 | } 57 | } 58 | 59 | public var xun: String { 60 | get { 61 | LunarUtil.getXun(ganZhi: ganZhi) 62 | } 63 | } 64 | 65 | public var xunKong: String { 66 | get { 67 | LunarUtil.getXunKong(ganZhi: ganZhi) 68 | } 69 | } 70 | 71 | public func getLiuYue() -> [LiuYue] { 72 | var l = [LiuYue]() 73 | for i in (0..<12) { 74 | l.append(LiuYue(liuNian: self, index: i)) 75 | } 76 | return l 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Sources/LunarSwift/LiuYue.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class LiuYue: NSObject { 5 | private var _index: Int 6 | private var _liuNian: LiuNian 7 | 8 | public var index: Int { 9 | get { 10 | _index 11 | } 12 | } 13 | 14 | public var liuNian: LiuNian { 15 | get { 16 | _liuNian 17 | } 18 | } 19 | 20 | public init(liuNian: LiuNian, index: Int) { 21 | _liuNian = liuNian 22 | _index = index 23 | } 24 | 25 | public var monthInChinese: String { 26 | get { 27 | LunarUtil.MONTH[_index + 1] 28 | } 29 | } 30 | 31 | public var ganZhi: String { 32 | get { 33 | var offset = 0 34 | let yearGan = _liuNian.ganZhi.prefix(1) 35 | switch (yearGan) 36 | { 37 | case "甲": 38 | offset = 2 39 | break 40 | case "己": 41 | offset = 2 42 | break 43 | case "乙": 44 | offset = 4 45 | break 46 | case "庚": 47 | offset = 4 48 | break 49 | case "丙": 50 | offset = 6 51 | break 52 | case "辛": 53 | offset = 6 54 | break 55 | case "丁": 56 | offset = 8 57 | break 58 | case "壬": 59 | offset = 8 60 | break 61 | default: 62 | break 63 | } 64 | let gan = LunarUtil.GAN[(_index + offset) % 10 + 1] 65 | let zhi = LunarUtil.ZHI[(_index + LunarUtil.BASE_MONTH_ZHI_INDEX) % 12 + 1] 66 | return gan + zhi 67 | } 68 | } 69 | 70 | public var xun: String { 71 | get { 72 | LunarUtil.getXun(ganZhi: ganZhi) 73 | } 74 | } 75 | 76 | public var xunKong: String { 77 | get { 78 | LunarUtil.getXunKong(ganZhi: ganZhi) 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Sources/LunarSwift/XiaoYun.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class XiaoYun: NSObject { 5 | private var _year: Int 6 | private var _age: Int 7 | private var _index: Int 8 | private var _forward: Bool = false 9 | private var _daYun: DaYun 10 | private var _lunar: Lunar 11 | 12 | public var year: Int { 13 | get { 14 | _year 15 | } 16 | } 17 | public var age: Int { 18 | get { 19 | _age 20 | } 21 | } 22 | 23 | public var index: Int { 24 | get { 25 | _index 26 | } 27 | } 28 | 29 | public var forward: Bool { 30 | get { 31 | _forward 32 | } 33 | } 34 | 35 | public var daYun: DaYun { 36 | get { 37 | _daYun 38 | } 39 | } 40 | 41 | public var lunar: Lunar { 42 | get { 43 | _lunar 44 | } 45 | } 46 | 47 | public init(daYun: DaYun, index: Int, forward: Bool) { 48 | _daYun = daYun 49 | _lunar = _daYun.lunar 50 | _index = index 51 | _year = _daYun.startYear + index 52 | _age = _daYun.startAge + index 53 | _forward = forward 54 | } 55 | 56 | public var ganZhi: String { 57 | get { 58 | var offset = LunarUtil.getJiaZiIndex(ganZhi: _lunar.timeInGanZhi) 59 | var add = _index + 1 60 | if _daYun.index > 0 { 61 | add += _daYun.startAge - 1 62 | } 63 | offset += _forward ? add : -add 64 | let size = LunarUtil.JIA_ZI.count 65 | while offset < 0 66 | { 67 | offset += size 68 | } 69 | offset %= size 70 | return LunarUtil.JIA_ZI[offset] 71 | } 72 | } 73 | 74 | public var xun: String { 75 | get { 76 | LunarUtil.getXun(ganZhi: ganZhi) 77 | } 78 | } 79 | 80 | public var xunKong: String { 81 | get { 82 | LunarUtil.getXunKong(ganZhi: ganZhi) 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sources/LunarSwift/SolarHalfYear.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class SolarHalfYear: NSObject { 5 | public static var MONTH_COUNT = 6 6 | 7 | private var _year: Int 8 | private var _month: Int 9 | 10 | public var year: Int { 11 | get { 12 | _year 13 | } 14 | } 15 | 16 | public var month: Int { 17 | get { 18 | _month 19 | } 20 | } 21 | 22 | public init(year: Int, month: Int) { 23 | _year = year 24 | _month = month 25 | } 26 | 27 | public init(date: Date) { 28 | let calendar = Calendar.current 29 | _year = calendar.component(.year, from: date) 30 | _month = calendar.component(.month, from: date) 31 | } 32 | 33 | public convenience override init() { 34 | self.init(date: Date()) 35 | } 36 | 37 | public class func fromYm(year: Int, month: Int) -> SolarHalfYear { 38 | SolarHalfYear(year: year, month: month) 39 | } 40 | 41 | public class func fromDate(date: Date) -> SolarHalfYear { 42 | SolarHalfYear(date: date) 43 | } 44 | 45 | public var index: Int { 46 | get { 47 | Int(ceil(Double(_month) / Double(SolarHalfYear.MONTH_COUNT))) 48 | } 49 | } 50 | 51 | public var months: [SolarMonth] { 52 | var l = [SolarMonth]() 53 | let offset = index - 1 54 | for i in (0.. SolarHalfYear { 61 | let m = SolarMonth.fromYm(year: _year, month: _month).next(months: halfYears * SolarHalfYear.MONTH_COUNT) 62 | return SolarHalfYear.fromYm(year: m.year, month: m.month) 63 | } 64 | 65 | public override var description: String { 66 | "\(_year).\(index)" 67 | } 68 | 69 | public var fullString: String { 70 | get { 71 | "\(_year)年\(1 == index ? "上" : "下")半年" 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Sources/LunarSwift/SolarSeason.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class SolarSeason: NSObject { 5 | public static var MONTH_COUNT = 3 6 | 7 | private var _year: Int 8 | private var _month: Int 9 | 10 | public var year: Int { 11 | get { 12 | _year 13 | } 14 | } 15 | 16 | public var month: Int { 17 | get { 18 | _month 19 | } 20 | } 21 | 22 | public init(year: Int, month: Int) { 23 | _year = year 24 | _month = month 25 | } 26 | 27 | public init(date: Date) { 28 | let calendar = Calendar.current 29 | _year = calendar.component(.year, from: date) 30 | _month = calendar.component(.month, from: date) 31 | } 32 | 33 | public convenience override init() { 34 | self.init(date: Date()) 35 | } 36 | 37 | public class func fromYm(year: Int, month: Int) -> SolarSeason { 38 | SolarSeason(year: year, month: month) 39 | } 40 | 41 | public class func fromDate(date: Date) -> SolarSeason { 42 | SolarSeason(date: date) 43 | } 44 | 45 | public var index: Int { 46 | get { 47 | Int(ceil(Double(_month) / Double(SolarSeason.MONTH_COUNT))) 48 | } 49 | } 50 | 51 | public var months: [SolarMonth] { 52 | get { 53 | var l = [SolarMonth]() 54 | let offset = index - 1 55 | for i in (0.. SolarSeason { 63 | let m = SolarMonth.fromYm(year: _year, month: _month).next(months: seasons * SolarSeason.MONTH_COUNT) 64 | return SolarSeason.fromYm(year: m.year, month: m.month) 65 | } 66 | 67 | public override var description: String { 68 | get { 69 | "\(_year).\(index)" 70 | } 71 | } 72 | 73 | public var fullString: String { 74 | get { 75 | "\(_year)年\(index)季度" 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Sources/LunarSwift/SolarMonth.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class SolarMonth: NSObject { 5 | private var _year: Int 6 | private var _month: Int 7 | 8 | public var year: Int { 9 | get { 10 | _year 11 | } 12 | } 13 | 14 | public var month: Int { 15 | get { 16 | _month 17 | } 18 | } 19 | 20 | public init(year: Int, month: Int) { 21 | _year = year 22 | _month = month 23 | } 24 | 25 | public init(date: Date) { 26 | let calendar = Calendar.current 27 | _year = calendar.component(.year, from: date) 28 | _month = calendar.component(.month, from: date) 29 | } 30 | 31 | public convenience override init() { 32 | self.init(date: Date()) 33 | } 34 | 35 | public class func fromYm(year: Int, month: Int) -> SolarMonth { 36 | SolarMonth(year: year, month: month) 37 | } 38 | 39 | public class func fromDate(date: Date) -> SolarMonth { 40 | SolarMonth(date: date) 41 | } 42 | 43 | public var days: [Solar] { 44 | var l = [Solar]() 45 | let d = Solar(year: _year, month: _month, day: 1) 46 | l.append(d) 47 | let days = SolarUtil.getDaysOfMonth(year: _year, month: _month) 48 | for i in (1.. [SolarWeek] { 55 | var l = [SolarWeek]() 56 | var week = SolarWeek.fromYmd(year: _year, month: _month, day: 1, start: start) 57 | while true { 58 | l.append(week) 59 | week = week.next(weeks: 1, separateMonth: false) 60 | let firstDay = week.firstDay 61 | if firstDay.year > _year || firstDay.month > _month { 62 | break 63 | } 64 | } 65 | return l 66 | } 67 | 68 | public func next(months: Int) -> SolarMonth { 69 | var n = 1 70 | var m = months 71 | if months < 0 { 72 | n = -1 73 | m = -months 74 | } 75 | var y = _year + m / 12 * n 76 | m = _month + m % 12 * n 77 | if m > 12 { 78 | m -= 12 79 | y += 1 80 | } else if m < 1 { 81 | m += 12 82 | y -= 1 83 | } 84 | return SolarMonth.fromYm(year: y, month: m) 85 | } 86 | 87 | public override var description: String { 88 | "\(_year)-\(_month)" 89 | } 90 | 91 | public var fullString: String { 92 | get { 93 | "\(_year)年\(_month)月" 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | .DS_Store 92 | /.build 93 | /Packages 94 | /*.xcodeproj 95 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 96 | .idea/ 97 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/SolarTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class SolarTests: XCTestCase { 5 | func test1() throws { 6 | let solar = Solar.fromYmdHms(year: 2019, month: 5, day: 1) 7 | XCTAssertEqual(solar.fullString, "2019-05-01 00:00:00 星期三 (劳动节) 金牛座") 8 | XCTAssertEqual(solar.lunar.description, "二〇一九年三月廿七") 9 | } 10 | 11 | func test2() throws { 12 | XCTAssertEqual(Solar.fromYmdHms(year: 2020, month: 5, day: 24, hour: 13).lunar.description, "二〇二〇年闰四月初二") 13 | } 14 | 15 | func test3() throws { 16 | XCTAssertEqual(Solar.fromYmdHms(year: 11, month: 1, day: 1).lunar.description, "一〇年腊月初八") 17 | } 18 | 19 | func test4() throws { 20 | XCTAssertEqual(Solar.fromYmdHms(year: 11, month: 3, day: 1).lunar.description, "一一年二月初八") 21 | } 22 | 23 | func test5() throws { 24 | XCTAssertEqual(Solar.fromYmdHms(year: 26, month: 4, day: 13).lunar.description, "二六年三月初八") 25 | } 26 | 27 | func test6() throws { 28 | XCTAssertEqual(Solar.fromYmdHms(year: 2022, month: 3, day: 28).festivals[0], "全国中小学生安全教育日") 29 | } 30 | 31 | func test7() throws { 32 | XCTAssertEqual(Solar.fromYmdHms(year: 2022, month: 1, day: 31).next(days: 1).description, "2022-02-01") 33 | } 34 | 35 | func test8() throws { 36 | XCTAssertEqual(Solar.fromYmdHms(year: 2022, month: 1, day: 1).next(days: 365).description, "2023-01-01") 37 | } 38 | 39 | func test9() throws { 40 | XCTAssertEqual(Solar.fromYmdHms(year: 1582, month: 10, day: 4).next(days: 1).description, "1582-10-15") 41 | } 42 | 43 | func test10() throws { 44 | XCTAssertEqual(Solar.fromYmdHms(year: 1582, month: 10, day: 15).next(days: -1).description, "1582-10-04") 45 | } 46 | 47 | func test11() throws { 48 | XCTAssertEqual(Solar.fromYmdHms(year: 1582, month: 10, day: 15).next(days: -5).description, "1582-09-30") 49 | } 50 | 51 | func test12() throws { 52 | XCTAssertEqual(Solar.fromYmdHms(year: 1991, month: 5, day: 12).lunar.dayInGanZhi, "壬午") 53 | } 54 | 55 | func test13() throws { 56 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 8, day: 31).nextMonth(months: 1).description, "2023-09-30") 57 | } 58 | 59 | func test14() throws { 60 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 8, day: 31).nextMonth(months: 2).description, "2023-10-31") 61 | } 62 | 63 | func test15() throws { 64 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 8, day: 31).nextMonth(months: 6).description, "2024-02-29") 65 | } 66 | 67 | func test16() throws { 68 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 8, day: 31).nextYear(years: 2).description, "2025-08-31") 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Sources/LunarSwift/DaYun.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class DaYun: NSObject { 5 | private var _startYear: Int 6 | private var _endYear: Int 7 | private var _startAge: Int 8 | private var _endAge: Int 9 | private var _index: Int 10 | private var _yun: Yun 11 | private var _lunar: Lunar 12 | 13 | public var startYear: Int { 14 | get { 15 | _startYear 16 | } 17 | } 18 | 19 | public var endYear: Int { 20 | get { 21 | _endYear 22 | } 23 | } 24 | 25 | public var startAge: Int { 26 | get { 27 | _startAge 28 | } 29 | } 30 | 31 | public var endAge: Int { 32 | get { 33 | _endAge 34 | } 35 | } 36 | 37 | public var index: Int { 38 | get { 39 | _index 40 | } 41 | } 42 | 43 | public var yun: Yun { 44 | get { 45 | _yun 46 | } 47 | } 48 | 49 | public var lunar: Lunar { 50 | get { 51 | _lunar 52 | } 53 | } 54 | 55 | public init(yun: Yun, index: Int) { 56 | _yun = yun 57 | _lunar = yun.lunar 58 | _index = index 59 | let birthYear = _lunar.solar.year 60 | let year = _yun.startSolar.year 61 | if index < 1 { 62 | _startYear = birthYear 63 | _startAge = 1 64 | _endYear = year - 1 65 | _endAge = year - birthYear 66 | } else { 67 | let add = (index - 1) * 10 68 | _startYear = year + add 69 | _startAge = _startYear - birthYear + 1 70 | _endYear = _startYear + 9 71 | _endAge = _startAge + 9 72 | } 73 | } 74 | 75 | public var ganZhi: String { 76 | get { 77 | if _index < 1 { 78 | return "" 79 | } 80 | var offset = LunarUtil.getJiaZiIndex(ganZhi: _lunar.monthInGanZhiExact) 81 | offset += _yun.forward ? _index : -_index 82 | let size = LunarUtil.JIA_ZI.count 83 | if offset >= size { 84 | offset -= size 85 | } 86 | if offset < 0 { 87 | offset += size 88 | } 89 | return LunarUtil.JIA_ZI[offset] 90 | } 91 | } 92 | 93 | public var xun: String { 94 | get { 95 | LunarUtil.getXun(ganZhi: ganZhi) 96 | } 97 | } 98 | 99 | public var xunKong: String { 100 | get { 101 | LunarUtil.getXunKong(ganZhi: ganZhi) 102 | } 103 | } 104 | 105 | public func getLiuNian(n: Int = 10) -> [LiuNian] { 106 | var l = [LiuNian]() 107 | var size = n 108 | if _index < 1 { 109 | size = _endYear - _startYear + 1 110 | } 111 | for i in (0.. [XiaoYun] { 118 | var l = [XiaoYun]() 119 | var size = n 120 | if _index < 1 { 121 | size = _endYear - _startYear + 1 122 | } 123 | for i in (0.. LunarMonth? { 51 | LunarYear.fromYear(lunarYear: lunarYear).getMonth(lunarMonth: lunarMonth) 52 | } 53 | 54 | public var leap: Bool { 55 | _month < 0 56 | } 57 | 58 | override public var description: String { 59 | var r = "" 60 | if leap { 61 | r = "闰" 62 | } 63 | let m = abs(_month) 64 | return "\(_year)年\(r)\(LunarUtil.MONTH[m])月(\(_dayCount))天" 65 | } 66 | 67 | public var gan: String { 68 | LunarUtil.GAN[ganIndex + 1] 69 | } 70 | 71 | public var zhi: String { 72 | LunarUtil.ZHI[_zhiIndex + 1] 73 | } 74 | 75 | public var ganZhi: String { 76 | "\(gan)\(zhi)" 77 | } 78 | 79 | public var positionXi: String { 80 | LunarUtil.POSITION_XI[ganIndex + 1] 81 | } 82 | 83 | public var positionXiDesc: String { 84 | LunarUtil.POSITION_DESC[positionXi]! 85 | } 86 | 87 | public var positionYangGui: String { 88 | LunarUtil.POSITION_YANG_GUI[ganIndex + 1] 89 | } 90 | 91 | public var positionYangGuiDesc: String { 92 | LunarUtil.POSITION_DESC[positionYangGui]! 93 | } 94 | 95 | public var positionYinGui: String { 96 | LunarUtil.POSITION_YIN_GUI[ganIndex + 1] 97 | } 98 | 99 | public var positionYinGuiDesc: String { 100 | LunarUtil.POSITION_DESC[positionYinGui]! 101 | } 102 | 103 | public var positionFu: String { 104 | getPositionFu(sect: 2) 105 | } 106 | 107 | public var positionFuDesc: String { 108 | getPositionFuDesc(sect: 2) 109 | } 110 | 111 | public func getPositionFu(sect: Int) -> String { 112 | (1 == sect ? LunarUtil.POSITION_FU : LunarUtil.POSITION_FU_2)[ganIndex + 1] 113 | } 114 | 115 | public func getPositionFuDesc(sect: Int) -> String { 116 | LunarUtil.POSITION_DESC[getPositionFu(sect: sect)]! 117 | } 118 | 119 | public var positionCai: String { 120 | LunarUtil.POSITION_CAI[ganIndex + 1] 121 | } 122 | 123 | public var positionCaiDesc: String { 124 | LunarUtil.POSITION_DESC[positionCai]! 125 | } 126 | 127 | public var nineStar: NineStar { 128 | let index = LunarYear.fromYear(lunarYear: _year).zhiIndex % 3 129 | let m = abs(_month) 130 | let monthZhiIndex = (13 + m) % 12 131 | var n = 27 - index * 3 132 | if monthZhiIndex < LunarUtil.BASE_MONTH_ZHI_INDEX { 133 | n -= 3 134 | } 135 | return NineStar(index: (n - monthZhiIndex) % 9 - 1) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Sources/LunarSwift/Yun.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class Yun: NSObject { 5 | private var _gender: Int = 0 6 | private var _startYear: Int = 0 7 | private var _startMonth: Int = 0 8 | private var _startDay: Int = 0 9 | private var _startHour: Int = 0 10 | private var _forward: Bool = false 11 | private var _lunar: Lunar 12 | 13 | public var gender: Int { 14 | get { 15 | _gender 16 | } 17 | } 18 | 19 | public var startYear: Int { 20 | get { 21 | _startYear 22 | } 23 | } 24 | 25 | public var startMonth: Int { 26 | get { 27 | _startMonth 28 | } 29 | } 30 | 31 | public var startDay: Int { 32 | get { 33 | _startDay 34 | } 35 | } 36 | 37 | public var startHour: Int { 38 | get { 39 | _startHour 40 | } 41 | } 42 | 43 | public var forward: Bool { 44 | get { 45 | _forward 46 | } 47 | } 48 | 49 | public var lunar: Lunar { 50 | get { 51 | _lunar 52 | } 53 | } 54 | 55 | public init(eightChar: EightChar, gender: Int, sect: Int = 1) { 56 | _lunar = eightChar.lunar 57 | _gender = gender 58 | // 阳 59 | let yang = 0 == _lunar.yearGanIndexExact % 2 60 | // 男 61 | let man = 1 == gender 62 | _forward = (yang && man) || (!yang && !man) 63 | 64 | // 上节 65 | let prev = _lunar.prevJie 66 | // 下节 67 | let next = _lunar.nextJie 68 | // 出生日期 69 | let current = _lunar.solar 70 | // 阳男阴女顺推,阴男阳女逆推 71 | let start = _forward ? current : prev.solar 72 | let end = _forward ? next.solar : current 73 | 74 | var year: Int 75 | var month: Int 76 | var day: Int 77 | var hour = 0 78 | 79 | if 2 == sect { 80 | var minutes = end.subtractMinute(solar: start) 81 | year = Int(minutes / 4320) 82 | minutes -= year * 4320 83 | month = Int(minutes / 360) 84 | minutes -= month * 360 85 | day = Int(minutes / 12) 86 | minutes -= day * 12 87 | hour = minutes * 2 88 | } else { 89 | let endTimeZhiIndex = (end.hour == 23) 90 | ? 11 91 | : LunarUtil.getTimeZhiIndex(hm: String(format: "%02d:%02d", end.hour, end.minute)) 92 | let startTimeZhiIndex = (start.hour == 23) 93 | ? 11 94 | : LunarUtil.getTimeZhiIndex(hm: String(format: "%02d:%02d", start.hour, start.minute)) 95 | // 时辰差 96 | var hourDiff = endTimeZhiIndex - startTimeZhiIndex 97 | // 天数差 98 | var dayDiff = end.subtract(solar: start) 99 | if hourDiff < 0 { 100 | hourDiff += 12 101 | dayDiff -= 1 102 | } 103 | let monthDiff = Int(hourDiff * 10 / 30) 104 | month = dayDiff * 4 + monthDiff 105 | day = hourDiff * 10 - monthDiff * 30 106 | year = month / 12 107 | month = month - year * 12 108 | } 109 | _startYear = year 110 | _startMonth = month 111 | _startDay = day 112 | _startHour = hour 113 | } 114 | 115 | public var startSolar: Solar { 116 | get { 117 | var solar = _lunar.solar 118 | solar = solar.nextYear(years: _startYear) 119 | solar = solar.nextMonth(months: _startMonth) 120 | solar = solar.next(days: _startDay) 121 | return solar.nextHour(hours: _startHour) 122 | } 123 | } 124 | 125 | public func getDaYun(n: Int = 10) -> [DaYun] { 126 | var l = [DaYun]() 127 | for i in (0.. NineStar { 42 | return NineStar(index: index) 43 | } 44 | 45 | public var number: String { 46 | NineStar.NUMBER[_index] 47 | } 48 | 49 | public var color: String { 50 | NineStar.COLOR[_index] 51 | } 52 | 53 | public var wuXing: String { 54 | NineStar.WU_XING[_index] 55 | } 56 | 57 | public var position: String { 58 | NineStar.POSITION[_index] 59 | } 60 | 61 | public var positionDesc: String { 62 | LunarUtil.POSITION_DESC[position]! 63 | } 64 | 65 | public var nameInXuanKong: String { 66 | NineStar.NAME_XUAN_KONG[_index] 67 | } 68 | 69 | public var nameInBeiDou: String { 70 | NineStar.NAME_BEI_DOU[_index] 71 | } 72 | 73 | public var nameInQiMen: String { 74 | NineStar.NAME_QI_MEN[_index] 75 | } 76 | 77 | public var nameInTaiYi: String { 78 | NineStar.NAME_TAI_YI[_index] 79 | } 80 | 81 | public var luckInQiMen: String { 82 | NineStar.LUCK_QI_MEN[_index] 83 | } 84 | 85 | public var luckInXuanKong: String { 86 | NineStar.LUCK_XUAN_KONG[_index] 87 | } 88 | 89 | public var yinYangInQiMen: String { 90 | NineStar.YIN_YANG_QI_MEN[_index] 91 | } 92 | 93 | public var typeInTaiYi: String { 94 | NineStar.TYPE_TAI_YI[_index] 95 | } 96 | 97 | public var baMenInQiMen: String { 98 | NineStar.BA_MEN_QI_MEN[_index] 99 | } 100 | 101 | public var songInTaiYi: String { 102 | NineStar.SONG_TAI_YI[_index] 103 | } 104 | 105 | override public var description: String { 106 | "\(number)\(color)\(wuXing)\(nameInBeiDou)" 107 | } 108 | 109 | public var fullString: String { 110 | var s = "\(number)\(color)\(wuXing) \(position)(\(positionDesc)) \(nameInBeiDou) 玄空[\(nameInXuanKong) \(luckInXuanKong)] 奇门[\(nameInQiMen) \(luckInQiMen)" 111 | if !baMenInQiMen.isEmpty { 112 | s += " \(baMenInQiMen)门] " 113 | } 114 | s += " \(yinYangInQiMen) 太乙[\(nameInTaiYi) \(typeInTaiYi)]" 115 | return s 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Sources/LunarSwift/TaoUtil.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class TaoUtil { 5 | 6 | public static var SAN_HUI: [String] = ["1-7", "7-7", "10-15"] 7 | public static var SAN_YUAN: [String] = ["1-15", "7-15", "10-15"] 8 | public static var WU_LA: [String] = ["1-1", "5-5", "7-7", "10-1", "12-8"] 9 | public static var AN_WU: [String] = ["未", "戌", "辰", "寅", "午", "子", "酉", "申", "巳", "亥", "卯", "丑"] 10 | public static var BA_HUI: [String: String] = [ 11 | "丙午": "天会", 12 | "壬午": "地会", 13 | "壬子": "人会", 14 | "庚午": "日会", 15 | "庚申": "月会", 16 | "辛酉": "星辰会", 17 | "甲辰": "五行会", 18 | "甲戌": "四时会" 19 | ] 20 | public static var BA_JIE: [String: String] = [ 21 | "立春": "东北方度仙上圣天尊同梵炁始青天君下降", 22 | "春分": "东方玉宝星上天尊同青帝九炁天君下降", 23 | "立夏": "东南方好生度命天尊同梵炁始丹天君下降", 24 | "夏至": "南方玄真万福天尊同赤帝三炁天君下降", 25 | "立秋": "西南方太灵虚皇天尊同梵炁始素天君下降", 26 | "秋分": "西方太妙至极天尊同白帝七炁天君下降", 27 | "立冬": "西北方无量太华天尊同梵炁始玄天君下降", 28 | "冬至": "北方玄上玉宸天尊同黑帝五炁天君下降" 29 | ] 30 | public static var FESTIVAL: [String: [[String]]] = [ 31 | "1-1": [["天腊之辰", "天腊,此日五帝会于东方九炁青天"]], 32 | "1-3": [["郝真人圣诞"], ["孙真人圣诞"]], 33 | "1-5": [["孙祖清静元君诞"]], 34 | "1-7": [["举迁赏会", "此日上元赐福,天官同地水二官考校罪福"]], 35 | "1-9": [["玉皇上帝圣诞"]], 36 | "1-13": [["关圣帝君飞升"]], 37 | "1-15": [["上元天官圣诞"], ["老祖天师圣诞"]], 38 | "1-19": [["长春邱真人(邱处机)圣诞"]], 39 | "1-28": [["许真君(许逊天师)圣诞"]], 40 | "2-1": [["勾陈天皇大帝圣诞"], ["长春刘真人(刘渊然)圣诞"]], 41 | "2-2": [["土地正神诞"], ["姜太公圣诞"]], 42 | "2-3": [["文昌梓潼帝君圣诞"]], 43 | "2-6": [["东华帝君圣诞"]], 44 | "2-13": [["度人无量葛真君圣诞"]], 45 | "2-15": [["太清道德天尊(太上老君)圣诞"]], 46 | "2-19": [["慈航真人圣诞"]], 47 | "3-1": [["谭祖(谭处端)长真真人圣诞"]], 48 | "3-3": [["玄天上帝圣诞"]], 49 | "3-6": [["眼光娘娘圣诞"]], 50 | "3-15": [["天师张大真人圣诞"], ["财神赵公元帅圣诞"]], 51 | "3-16": [["三茅真君得道之辰"], ["中岳大帝圣诞"]], 52 | "3-18": [["王祖(王处一)玉阳真人圣诞"], ["后土娘娘圣诞"]], 53 | "3-19": [["太阳星君圣诞"]], 54 | "3-20": [["子孙娘娘圣诞"]], 55 | "3-23": [["天后妈祖圣诞"]], 56 | "3-26": [["鬼谷先师诞"]], 57 | "3-28": [["东岳大帝圣诞"]], 58 | "4-1": [["长生谭真君成道之辰"]], 59 | "4-10": [["何仙姑圣诞"]], 60 | "4-14": [["吕祖纯阳祖师圣诞"]], 61 | "4-15": [["钟离祖师圣诞"]], 62 | "4-18": [["北极紫微大帝圣诞"], ["泰山圣母碧霞元君诞"], ["华佗神医先师诞"]], 63 | "4-20": [["眼光圣母娘娘诞"]], 64 | "4-28": [["神农先帝诞"]], 65 | "5-1": [["南极长生大帝圣诞"]], 66 | "5-5": [["地腊之辰", "地腊,此日五帝会于南方三炁丹天"], ["南方雷祖圣诞"], ["地祗温元帅圣诞"], ["雷霆邓天君圣诞"]], 67 | "5-11": [["城隍爷圣诞"]], 68 | "5-13": [["关圣帝君降神"], ["关平太子圣诞"]], 69 | "5-18": [["张天师圣诞"]], 70 | "5-20": [["马祖丹阳真人圣诞"]], 71 | "5-29": [["紫青白祖师圣诞"]], 72 | "6-1": [["南斗星君下降"]], 73 | "6-2": [["南斗星君下降"]], 74 | "6-3": [["南斗星君下降"]], 75 | "6-4": [["南斗星君下降"]], 76 | "6-5": [["南斗星君下降"]], 77 | "6-6": [["南斗星君下降"]], 78 | "6-10": [["刘海蟾祖师圣诞"]], 79 | "6-15": [["灵官王天君圣诞"]], 80 | "6-19": [["慈航(观音)成道日"]], 81 | "6-23": [["火神圣诞"]], 82 | "6-24": [["南极大帝中方雷祖圣诞"], ["关圣帝君圣诞"]], 83 | "6-26": [["二郎真君圣诞"]], 84 | "7-7": [["道德腊之辰", "道德腊,此日五帝会于西方七炁素天"], ["庆生中会", "此日中元赦罪,地官同天水二官考校罪福"]], 85 | "7-12": [["西方雷祖圣诞"]], 86 | "7-15": [["中元地官大帝圣诞"]], 87 | "7-18": [["王母娘娘圣诞"]], 88 | "7-20": [["刘祖(刘处玄)长生真人圣诞"]], 89 | "7-22": [["财帛星君文财神增福相公李诡祖圣诞"]], 90 | "7-26": [["张三丰祖师圣诞"]], 91 | "8-1": [["许真君飞升日"]], 92 | "8-3": [["九天司命灶君诞"]], 93 | "8-5": [["北方雷祖圣诞"]], 94 | "8-10": [["北岳大帝诞辰"]], 95 | "8-15": [["太阴星君诞"]], 96 | "9-1": [["北斗九皇降世之辰"]], 97 | "9-2": [["北斗九皇降世之辰"]], 98 | "9-3": [["北斗九皇降世之辰"]], 99 | "9-4": [["北斗九皇降世之辰"]], 100 | "9-5": [["北斗九皇降世之辰"]], 101 | "9-6": [["北斗九皇降世之辰"]], 102 | "9-7": [["北斗九皇降世之辰"]], 103 | "9-8": [["北斗九皇降世之辰"]], 104 | "9-9": [["北斗九皇降世之辰"], ["斗姥元君圣诞"], ["重阳帝君圣诞"], ["玄天上帝飞升"], ["酆都大帝圣诞"]], 105 | "9-22": [["增福财神诞"]], 106 | "9-23": [["萨翁真君圣诞"]], 107 | "9-28": [["五显灵官马元帅圣诞"]], 108 | "10-1": [["民岁腊之辰", "民岁腊,此日五帝会于北方五炁黑天"], ["东皇大帝圣诞"]], 109 | "10-3": [["三茅应化真君圣诞"]], 110 | "10-6": [["天曹诸司五岳五帝圣诞"]], 111 | "10-15": [["下元水官大帝圣诞"], ["建生大会", "此日下元解厄,水官同天地二官考校罪福"]], 112 | "10-18": [["地母娘娘圣诞"]], 113 | "10-19": [["长春邱真君飞升"]], 114 | "10-20": [["虚靖天师(即三十代天师弘悟张真人)诞"]], 115 | "11-6": [["西岳大帝圣诞"]], 116 | "11-9": [["湘子韩祖圣诞"]], 117 | "11-11": [["太乙救苦天尊圣诞"]], 118 | "11-26": [["北方五道圣诞"]], 119 | "12-8": [["王侯腊之辰", "王侯腊,此日五帝会于上方玄都玉京"]], 120 | "12-16": [["南岳大帝圣诞"], ["福德正神诞"]], 121 | "12-20": [["鲁班先师圣诞"]], 122 | "12-21": [["天猷上帝圣诞"]], 123 | "12-22": [["重阳祖师圣诞"]], 124 | "12-23": [["祭灶王", "最适宜谢旧年太岁,开启拜新年太岁"]], 125 | "12-25": [["玉帝巡天"], ["天神下降"]], 126 | "12-29": [["清静孙真君(孙不二)成道"]] 127 | ] 128 | 129 | } 130 | -------------------------------------------------------------------------------- /Sources/LunarSwift/Tao.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class Tao: NSObject { 5 | public static var BIRTH_YEAR: Int = -2697 6 | 7 | private var _lunar: Lunar 8 | 9 | public var lunar: Lunar { 10 | get { 11 | _lunar 12 | } 13 | } 14 | 15 | public init(lunar: Lunar) { 16 | _lunar = lunar 17 | } 18 | 19 | public class func fromLunar(lunar: Lunar) -> Tao { 20 | Tao(lunar: lunar) 21 | } 22 | 23 | public class func fromYmdHms(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) -> Tao { 24 | Tao(lunar: Lunar.fromYmdHms(lunarYear: year + BIRTH_YEAR, lunarMonth: month, lunarDay: day, hour: hour, minute: minute, second: second)) 25 | } 26 | 27 | public var year: Int { 28 | get { 29 | _lunar.year - Tao.BIRTH_YEAR 30 | } 31 | } 32 | 33 | public var month: Int { 34 | get { 35 | _lunar.month 36 | } 37 | } 38 | 39 | public var day: Int { 40 | get { 41 | _lunar.day 42 | } 43 | } 44 | 45 | public var yearInChinese: String { 46 | get { 47 | let y = "\(year)" 48 | var s = "" 49 | for i in (0.. 1 { 76 | remark = o[1] 77 | } 78 | l.append(TaoFestival(name: o[0], remark: remark)) 79 | } 80 | } 81 | let jq = _lunar.jieQi 82 | if "冬至" == jq { 83 | l.append(TaoFestival(name: "元始天尊圣诞")) 84 | } else if "夏至" == jq { 85 | l.append(TaoFestival(name: "灵宝天尊圣诞")) 86 | } 87 | var name = TaoUtil.BA_JIE[jq] 88 | if nil != name { 89 | l.append(TaoFestival(name: name!)) 90 | } 91 | name = TaoUtil.BA_HUI[_lunar.dayInGanZhi] 92 | if nil != name { 93 | l.append(TaoFestival(name: name!)) 94 | } 95 | return l 96 | } 97 | } 98 | 99 | private func isDayIn(days: [String]) -> Bool { 100 | days.contains("\(month)-\(day)") 101 | } 102 | 103 | public var daySanHui: Bool { 104 | get { 105 | isDayIn(days: TaoUtil.SAN_HUI) 106 | } 107 | } 108 | 109 | public var daySanYuan: Bool { 110 | get { 111 | isDayIn(days: TaoUtil.SAN_YUAN) 112 | } 113 | } 114 | 115 | public var dayWuLa: Bool { 116 | get { 117 | isDayIn(days: TaoUtil.WU_LA) 118 | } 119 | } 120 | 121 | public var dayBaJie: Bool { 122 | get { 123 | nil != TaoUtil.BA_JIE[_lunar.jieQi] 124 | } 125 | } 126 | 127 | public var dayBaHui: Bool { 128 | get { 129 | nil != TaoUtil.BA_HUI[_lunar.dayInGanZhi] 130 | } 131 | } 132 | 133 | public var dayMingWu: Bool { 134 | get { 135 | "戊" == _lunar.dayGan 136 | } 137 | } 138 | 139 | public var dayAnWu: Bool { 140 | get { 141 | TaoUtil.AN_WU[abs(month) - 1] == _lunar.dayZhi 142 | } 143 | } 144 | 145 | public var dayWu: Bool { 146 | get { 147 | dayMingWu || dayAnWu 148 | } 149 | } 150 | 151 | public var dayTianShe: Bool { 152 | get { 153 | let mz = _lunar.monthZhi 154 | let dgz = _lunar.dayInGanZhi 155 | if "寅卯辰".contains(mz) { 156 | if "戊寅" == dgz { 157 | return true 158 | } 159 | } else if "巳午未".contains(mz) { 160 | if "甲午" == dgz { 161 | return true 162 | } 163 | } else if "申酉戌".contains(mz) { 164 | if "戊申" == dgz { 165 | return true 166 | } 167 | } else if "亥子丑".contains(mz) { 168 | if "甲子" == dgz { 169 | return true 170 | } 171 | } 172 | return false 173 | } 174 | } 175 | 176 | public override var description: String { 177 | get { 178 | "\(yearInChinese)年\(monthInChinese)月\(dayInChinese)" 179 | } 180 | } 181 | 182 | public var fullString: String { 183 | get { 184 | "道歷\(yearInChinese)年,天運\(_lunar.yearInGanZhi)年,\(_lunar.monthInGanZhi)月,\(_lunar.dayInGanZhi)日。\(monthInChinese)月\(dayInChinese)日,\(_lunar.timeZhi)時。" 185 | } 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /LunarSwift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint LunarSwift.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "LunarSwift" 19 | spec.version = "1.1.8" 20 | spec.summary = "A calendar library for Solar and Chinese Lunar." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = <<-DESC 28 | lunar是一款无第三方依赖的日历工具,支持公历(阳历)、农历(阴历、老黄历)、道历和佛历,支持星座、儒略日、干支、生肖、节气、节日、彭祖百忌、吉神(喜神/福神/财神/阳贵神/阴贵神)方位、胎神方位、冲煞、纳音、星宿、八字、五行、十神、建除十二值星、青龙名堂等十二神、黄道日及吉凶、法定节假日及调休等。 29 | DESC 30 | 31 | spec.homepage = "https://github.com/6tail/lunar-swift" 32 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See https://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | spec.license = "MIT" 43 | # spec.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | spec.author = { "6tail" => "6tail@6tail.cn" } 57 | # Or just: spec.author = "6tail" 58 | # spec.authors = { "6tail" => "6tail@6tail.cn" } 59 | # spec.social_media_url = "https://twitter.com/6tail" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # spec.platform = :ios 68 | # spec.platform = :ios, "5.0" 69 | 70 | # When using multiple platforms 71 | spec.ios.deployment_target = "11.0" 72 | # spec.ios.deployment_target = "5.0" 73 | spec.osx.deployment_target = "10.13" 74 | # spec.osx.deployment_target = "10.7" 75 | # spec.watchos.deployment_target = "2.0" 76 | # spec.tvos.deployment_target = "9.0" 77 | 78 | 79 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 80 | # 81 | # Specify the location from where the source should be retrieved. 82 | # Supports git, hg, bzr, svn and HTTP. 83 | # 84 | 85 | spec.source = { :git => "https://github.com/6tail/lunar-swift.git", :tag => "#{spec.version}" } 86 | 87 | 88 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 89 | # 90 | # CocoaPods is smart about how it includes source code. For source files 91 | # giving a folder will include any swift, h, m, mm, c & cpp files. 92 | # For header files it will include any header in the folder. 93 | # Not including the public_header_files will make all headers public. 94 | # 95 | 96 | spec.source_files = "Sources/**/*.swift" 97 | # spec.exclude_files = "Classes/Exclude" 98 | 99 | # spec.public_header_files = "Classes/**/*.h" 100 | 101 | 102 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 103 | # 104 | # A list of resources included with the Pod. These are copied into the 105 | # target bundle with a build phase script. Anything else will be cleaned. 106 | # You can preserve files from being cleaned, please don't preserve 107 | # non-essential files like tests, examples and documentation. 108 | # 109 | 110 | # spec.resource = "icon.png" 111 | # spec.resources = "Resources/*.png" 112 | 113 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 114 | 115 | 116 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 117 | # 118 | # Link your library with frameworks, or libraries. Libraries do not include 119 | # the lib prefix of their name. 120 | # 121 | 122 | # spec.framework = "SomeFramework" 123 | # spec.frameworks = "SomeFramework", "AnotherFramework" 124 | 125 | # spec.library = "iconv" 126 | # spec.libraries = "iconv", "xml2" 127 | 128 | 129 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 130 | # 131 | # If your library depends on compiler flags you can set them in the xcconfig hash 132 | # where they will only apply to your library. If you depend on other Podspecs 133 | # you can include multiple dependencies to ensure it works. 134 | 135 | # spec.requires_arc = true 136 | 137 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 138 | # spec.dependency "JSONKit", "~> 1.4" 139 | spec.swift_versions = "5.0" 140 | 141 | end 142 | -------------------------------------------------------------------------------- /Sources/LunarSwift/LunarTime.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class LunarTime: NSObject { 5 | private var _ganIndex: Int 6 | private var _zhiIndex: Int 7 | private var _lunar: Lunar 8 | 9 | public var ganIndex: Int { 10 | _ganIndex 11 | } 12 | 13 | public var zhiIndex: Int { 14 | _zhiIndex 15 | } 16 | 17 | public var lunar: Lunar { 18 | _lunar 19 | } 20 | 21 | public init(lunarYear: Int, lunarMonth: Int, lunarDay: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) { 22 | _lunar = Lunar.fromYmdHms(lunarYear: lunarYear, lunarMonth: lunarMonth, lunarDay: lunarDay, hour: hour, minute: minute, second: second) 23 | _zhiIndex = LunarUtil.getTimeZhiIndex(hm: String(format: "%02d:%02d", hour, minute)) 24 | _ganIndex = (_lunar.dayGanIndexExact % 5 * 2 + _zhiIndex) % 10 25 | } 26 | 27 | public class func fromYmdHms(lunarYear: Int, lunarMonth: Int, lunarDay: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) -> LunarTime { 28 | LunarTime(lunarYear: lunarYear, lunarMonth: lunarMonth, lunarDay: lunarDay, hour: hour, minute: minute, second: second) 29 | } 30 | 31 | public var gan: String { 32 | LunarUtil.GAN[_ganIndex + 1] 33 | } 34 | 35 | public var zhi: String { 36 | LunarUtil.ZHI[_zhiIndex + 1] 37 | } 38 | 39 | public var ganZhi: String { 40 | "\(gan)\(zhi)" 41 | } 42 | 43 | public var shengXiao: String { 44 | LunarUtil.SHENG_XIAO[_zhiIndex + 1] 45 | } 46 | 47 | public var naYin: String { 48 | LunarUtil.NAYIN[ganZhi]! 49 | } 50 | 51 | public var positionXi: String { 52 | LunarUtil.POSITION_XI[_ganIndex + 1] 53 | } 54 | 55 | public var positionXiDesc: String { 56 | LunarUtil.POSITION_DESC[positionXi]! 57 | } 58 | 59 | public var positionYangGui: String { 60 | LunarUtil.POSITION_YANG_GUI[_ganIndex + 1] 61 | } 62 | 63 | public var positionYangGuiDesc: String { 64 | LunarUtil.POSITION_DESC[positionYangGui]! 65 | } 66 | 67 | public var positionYinGui: String { 68 | LunarUtil.POSITION_YIN_GUI[_ganIndex + 1] 69 | } 70 | 71 | public var positionYinGuiDesc: String { 72 | LunarUtil.POSITION_DESC[positionYinGui]! 73 | } 74 | 75 | public var positionFu: String { 76 | getPositionFu(sect: 2) 77 | } 78 | 79 | public var positionFuDesc: String { 80 | getPositionFuDesc(sect: 2) 81 | } 82 | 83 | public func getPositionFu(sect: Int) -> String { 84 | (1 == sect ? LunarUtil.POSITION_FU : LunarUtil.POSITION_FU_2)[_ganIndex + 1] 85 | } 86 | 87 | public func getPositionFuDesc(sect: Int) -> String { 88 | LunarUtil.POSITION_DESC[getPositionFu(sect: sect)]! 89 | } 90 | 91 | public var positionCai: String { 92 | LunarUtil.POSITION_CAI[_ganIndex + 1] 93 | } 94 | 95 | public var positionCaiDesc: String { 96 | LunarUtil.POSITION_DESC[positionCai]! 97 | } 98 | 99 | public var tianShen: String { 100 | LunarUtil.TIAN_SHEN[(_zhiIndex + LunarUtil.ZHI_TIAN_SHEN_OFFSET[_lunar.dayZhiExact]!) % 12 + 1] 101 | } 102 | 103 | public var tianShenType: String { 104 | LunarUtil.TIAN_SHEN_TYPE[tianShen]! 105 | } 106 | 107 | public var tianShenLuck: String { 108 | LunarUtil.TIAN_SHEN_TYPE_LUCK[tianShenType]! 109 | } 110 | 111 | public var yi: [String] { 112 | LunarUtil.getTimeYi(dayGanZhi: _lunar.dayInGanZhiExact, timeGanZhi: ganZhi) 113 | } 114 | 115 | public var ji: [String] { 116 | LunarUtil.getTimeJi(dayGanZhi: _lunar.dayInGanZhiExact, timeGanZhi: ganZhi) 117 | } 118 | 119 | public var chong: String { 120 | LunarUtil.CHONG[_zhiIndex] 121 | } 122 | 123 | public var sha: String { 124 | LunarUtil.SHA[zhi]! 125 | } 126 | 127 | public var chongShengXiao: String { 128 | for i in 0 ..< LunarUtil.ZHI.count { 129 | if LunarUtil.ZHI[i] == chong { 130 | return LunarUtil.SHENG_XIAO[i] 131 | } 132 | } 133 | return "" 134 | } 135 | 136 | public var chongGan: String { 137 | LunarUtil.CHONG_GAN[_ganIndex] 138 | } 139 | 140 | public var chongGanTie: String { 141 | LunarUtil.CHONG_GAN_TIE[_ganIndex] 142 | } 143 | 144 | public var chongDesc: String { 145 | "(\(chongGan)\(chong))\(chongShengXiao)" 146 | } 147 | 148 | override public var description: String { 149 | "\(ganZhi)" 150 | } 151 | 152 | public var xun: String { 153 | LunarUtil.getXun(ganZhi: ganZhi) 154 | } 155 | 156 | public var xunKong: String { 157 | LunarUtil.getXunKong(ganZhi: ganZhi) 158 | } 159 | 160 | public var nineStar: NineStar { 161 | let solarYmd = lunar.solar.ymd 162 | let jieQi = lunar.jieQiTable 163 | let asc = solarYmd >= jieQi["冬至"]!.ymd && solarYmd < jieQi["夏至"]!.ymd 164 | var start = asc ? 7 : 3 165 | if "子午卯酉".contains(lunar.dayZhi) { 166 | start = asc ? 1 : 9 167 | } else if "辰戌丑未".contains(lunar.dayZhi) { 168 | start = asc ? 4 : 6 169 | } 170 | var index = asc ? start + _zhiIndex - 1 : start - _zhiIndex - 1 171 | if index > 8 { 172 | index -= 9 173 | } 174 | if index < 0 { 175 | index += 9 176 | } 177 | return NineStar(index: index) 178 | } 179 | 180 | public var minHm: String { 181 | let hour = lunar.hour 182 | if hour < 1 { 183 | return "00:00" 184 | } else if hour > 22 { 185 | return "23:00" 186 | } 187 | return String(format: "%02d:00", hour % 2 == 0 ? hour - 1 : hour) 188 | } 189 | 190 | public var maxHm: String { 191 | let hour = lunar.hour 192 | if hour < 1 { 193 | return "00:59" 194 | } else if hour > 22 { 195 | return "23:59" 196 | } 197 | return String(format: "%02d:59", hour % 2 == 0 ? hour : hour + 1) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Sources/LunarSwift/Foto.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class Foto: NSObject { 5 | public static var DEAD_YEAR: Int = -543 6 | 7 | private var _lunar: Lunar 8 | 9 | public var lunar: Lunar { 10 | get { 11 | _lunar 12 | } 13 | } 14 | 15 | public init(lunar: Lunar) { 16 | _lunar = lunar 17 | } 18 | 19 | public class func fromLunar(lunar: Lunar) -> Foto { 20 | Foto(lunar: lunar) 21 | } 22 | 23 | public class func fromYmdHms(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) -> Foto { 24 | Foto(lunar: Lunar.fromYmdHms(lunarYear: year + DEAD_YEAR - 1, lunarMonth: month, lunarDay: day, hour: hour, minute: minute, second: second)) 25 | } 26 | 27 | public var year: Int { 28 | get { 29 | let sy = _lunar.solar.year 30 | var y = sy - Foto.DEAD_YEAR 31 | if sy == _lunar.year { 32 | y += 1 33 | } 34 | return y 35 | } 36 | } 37 | 38 | public var month: Int { 39 | get { 40 | _lunar.month 41 | } 42 | } 43 | 44 | public var day: Int { 45 | get { 46 | _lunar.day 47 | } 48 | } 49 | 50 | public var yearInChinese: String { 51 | get { 52 | let y = "\(year)" 53 | var s = "" 54 | for i in (0.. 1 { 84 | result = o[1] 85 | } 86 | if size > 2 && "true" == o[2] { 87 | everyMonth = true 88 | } 89 | if size > 3 { 90 | remark = o[3] 91 | } 92 | l.append(FotoFestival(name: o[0], result: result, everyMonth: everyMonth, remark: remark)) 93 | } 94 | } 95 | return l 96 | } 97 | } 98 | 99 | public var otherFestivals: [String] { 100 | get { 101 | var l = [String]() 102 | //获取几月几日对应的节日 103 | let fs = FotoUtil.OTHER_FESTIVAL["\(month)-\(day)"] 104 | if nil != fs { 105 | for f in fs! { 106 | l.append(f) 107 | } 108 | } 109 | return l 110 | } 111 | } 112 | 113 | public var monthZhai: Bool { 114 | get { 115 | 1 == month || 5 == month || 9 == month 116 | } 117 | } 118 | 119 | public var dayYangGong: Bool { 120 | get { 121 | for o in festivals { 122 | if "杨公忌" == o.name { 123 | return true 124 | } 125 | } 126 | return false 127 | } 128 | } 129 | 130 | public var dayZhaiShuoWang: Bool { 131 | get { 132 | 1 == day || 15 == day 133 | } 134 | } 135 | 136 | public var dayZhaiSix: Bool { 137 | get { 138 | if 8 == day || 14 == day || 15 == day || 23 == day || 29 == day || 30 == day { 139 | return true 140 | } else if 28 == day { 141 | let m = LunarMonth.fromYm(lunarYear: _lunar.year, lunarMonth: _lunar.month) 142 | return nil != m && 30 != m!.dayCount 143 | } 144 | return false 145 | } 146 | } 147 | 148 | public var dayZhaiTen: Bool { 149 | get { 150 | 1 == day || 8 == day || 14 == day || 15 == day || 18 == day || 23 == day || 24 == day || 28 == day || 29 == day || 30 == day 151 | } 152 | } 153 | 154 | public var dayZhaiGuanYin: Bool { 155 | get { 156 | let k = "\(month)-\(day)" 157 | for v in FotoUtil.DAY_ZHAI_GUAN_YIN { 158 | if k == v { 159 | return true 160 | } 161 | } 162 | return false 163 | } 164 | } 165 | 166 | public var xiu: String { 167 | get { 168 | FotoUtil.getXiu(month: month, day: day) 169 | } 170 | } 171 | 172 | public var xiuLuck: String { 173 | get { 174 | LunarUtil.XIU_LUCK[xiu]! 175 | } 176 | } 177 | 178 | public var xiuSong: String { 179 | get { 180 | LunarUtil.XIU_SONG[xiu]! 181 | } 182 | } 183 | 184 | public var zheng: String { 185 | get { 186 | LunarUtil.ZHENG[xiu]! 187 | } 188 | } 189 | 190 | public var animal: String { 191 | get { 192 | LunarUtil.ANIMAL[xiu]! 193 | } 194 | } 195 | 196 | public var gong: String { 197 | get { 198 | LunarUtil.GONG[xiu]! 199 | } 200 | } 201 | 202 | public var shou: String { 203 | get { 204 | LunarUtil.SHOU[gong]! 205 | } 206 | } 207 | 208 | public override var description: String { 209 | get { 210 | "\(yearInChinese)年\(monthInChinese)月\(dayInChinese)" 211 | } 212 | } 213 | 214 | public var fullString: String { 215 | get { 216 | var s = description 217 | for f in festivals { 218 | s += " (\(f.description))" 219 | } 220 | return s 221 | } 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /Sources/LunarSwift/SolarWeek.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class SolarWeek: NSObject { 5 | private var _year: Int 6 | private var _month: Int 7 | private var _day: Int 8 | private var _start: Int 9 | 10 | public var year: Int { 11 | get { 12 | _year 13 | } 14 | } 15 | 16 | public var month: Int { 17 | get { 18 | _month 19 | } 20 | } 21 | 22 | public var day: Int { 23 | get { 24 | _day 25 | } 26 | } 27 | 28 | public var start: Int { 29 | get { 30 | _start 31 | } 32 | } 33 | 34 | public init(year: Int, month: Int, day: Int, start: Int) { 35 | _year = year 36 | _month = month 37 | _day = day 38 | _start = start 39 | } 40 | 41 | public init(date: Date, start: Int) { 42 | let calendar = Calendar.current 43 | _year = calendar.component(.year, from: date) 44 | _month = calendar.component(.month, from: date) 45 | _day = calendar.component(.day, from: date) 46 | _start = start 47 | } 48 | 49 | public convenience init(start: Int) { 50 | self.init(date: Date(), start: start) 51 | } 52 | 53 | public class func fromYmd(year: Int, month: Int, day: Int, start: Int) -> SolarWeek { 54 | SolarWeek(year: year, month: month, day: day, start: start) 55 | } 56 | 57 | public class func fromDate(date: Date, start: Int) -> SolarWeek { 58 | SolarWeek(date: date, start: start) 59 | } 60 | 61 | public var index: Int { 62 | get { 63 | var offset = Solar.fromYmdHms(year: _year, month: _month, day: 1).week - _start 64 | if offset < 0 65 | { 66 | offset += 7 67 | } 68 | return Int(ceil(Double(_day + offset) / Double(7))) 69 | } 70 | } 71 | 72 | public var indexInYear: Int { 73 | get { 74 | var offset = Solar.fromYmdHms(year: _year, month: 1, day: 1).week - _start 75 | if offset < 0 76 | { 77 | offset += 7 78 | } 79 | return Int(ceil(Double(SolarUtil.getDaysInYear(year: _year, month: _month, day: _day) + offset) / Double(7))) 80 | } 81 | } 82 | 83 | public var firstDay: Solar { 84 | get { 85 | let c = Solar.fromYmdHms(year: _year, month: _month, day: _day) 86 | var prev = c.week - _start 87 | if prev < 0 88 | { 89 | prev += 7 90 | } 91 | return c.next(days: -prev) 92 | } 93 | } 94 | 95 | public var firstDayInMonth: Solar? { 96 | get { 97 | for d in days { 98 | if d.month == _month { 99 | return d 100 | } 101 | } 102 | return nil 103 | } 104 | } 105 | 106 | public var days: [Solar] { 107 | get { 108 | var l = [Solar]() 109 | let d = firstDay 110 | l.append(d) 111 | for i in (1..<7) { 112 | l.append(d.next(days: i)) 113 | } 114 | return l 115 | } 116 | } 117 | 118 | public var daysInMonth: [Solar] { 119 | get { 120 | var l = [Solar]() 121 | for d in days { 122 | if d.month == _month { 123 | l.append(d) 124 | } 125 | } 126 | return l 127 | } 128 | } 129 | 130 | public func next(weeks: Int, separateMonth: Bool) -> SolarWeek { 131 | if 0 == weeks { 132 | return SolarWeek(year: _year, month: _month, day: _day, start: _start) 133 | } 134 | var c = Solar.fromYmdHms(year: _year, month: _month, day: _day) 135 | if separateMonth { 136 | var n = weeks 137 | var week = SolarWeek(year: c.year, month: c.month, day: c.day, start: _start) 138 | var month = week.month 139 | let plus = n > 0 140 | while 0 != n 141 | { 142 | c = c.next(days: plus ? 7 : -7) 143 | week = SolarWeek(year: c.year, month: c.month, day: c.day, start: _start) 144 | var weekMonth = week.month 145 | if month != weekMonth 146 | { 147 | let index = week.index 148 | if plus 149 | { 150 | if 1 == index 151 | { 152 | let firstDay = week.firstDay 153 | week = SolarWeek(year: firstDay.year, month: firstDay.month, day: firstDay.day, start: _start) 154 | weekMonth = week.month 155 | } 156 | else 157 | { 158 | c = Solar(year: week.year, month: week.month, day: 1) 159 | week = SolarWeek(year: c.year, month: c.month, day: c.day, start: _start) 160 | } 161 | } 162 | else 163 | { 164 | if SolarUtil.getWeeksOfMonth(year: week.year, month: week.month, start: _start) == index 165 | { 166 | let lastDay = week.firstDay.next(days: 6) 167 | week = SolarWeek(year: lastDay.year, month: lastDay.month, day: lastDay.day, start: _start) 168 | weekMonth = week.month 169 | } 170 | else 171 | { 172 | c = Solar(year: week.year, month: week.month, day: SolarUtil.getDaysOfMonth(year: week.year, month: week.month)) 173 | week = SolarWeek(year: c.year, month: c.month, day: c.day, start: _start) 174 | } 175 | } 176 | month = weekMonth 177 | } 178 | n -= plus ? 1 : -1 179 | } 180 | return week 181 | } else { 182 | c = c.next(days: weeks * 7) 183 | return SolarWeek(year: c.year, month: c.month, day: c.day, start: _start) 184 | } 185 | } 186 | 187 | public override var description: String { 188 | get { 189 | "\(_year).\(_month).\(index)" 190 | } 191 | } 192 | 193 | public var fullString: String { 194 | get { 195 | "\(_year)年\(_month)月第\(index)周" 196 | } 197 | } 198 | 199 | } 200 | -------------------------------------------------------------------------------- /Tests/LunarSwiftTests/LunarTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LunarSwift 3 | 4 | final class LunarTests: XCTestCase { 5 | func test1() throws { 6 | let lunar = Lunar.fromYmdHms(lunarYear: 2019, lunarMonth: 3, lunarDay: 27) 7 | XCTAssertEqual(lunar.description, "二〇一九年三月廿七") 8 | XCTAssertEqual(lunar.solar.fullString, "2019-05-01 00:00:00 星期三 (劳动节) 金牛座") 9 | } 10 | 11 | func test2() throws { 12 | XCTAssertEqual(Solar.fromYmdHms(year: 1, month: 1, day: 1, hour: 12).lunar.description, "〇年冬月十八") 13 | } 14 | 15 | func test3() throws { 16 | XCTAssertEqual(Solar.fromYmdHms(year: 9999, month: 12, day: 31, hour: 12).lunar.description, "九九九九年腊月初二") 17 | } 18 | 19 | func test4() throws { 20 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 1905, lunarMonth: 1, lunarDay: 1, hour: 12).solar.description, "1905-02-04") 21 | } 22 | 23 | func test5() throws { 24 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 2038, lunarMonth: 12, lunarDay: 29, hour: 12).solar.description, "2039-01-23") 25 | } 26 | 27 | func test6() throws { 28 | let lunar = Lunar.fromYmdHms(lunarYear: 2020, lunarMonth: -4, lunarDay: 2, hour: 13) 29 | XCTAssertEqual(lunar.description, "二〇二〇年闰四月初二") 30 | XCTAssertEqual(lunar.solar.description, "2020-05-24") 31 | } 32 | 33 | func test7() throws { 34 | let lunar = Lunar.fromYmdHms(lunarYear: 2020, lunarMonth: 12, lunarDay: 10, hour: 13) 35 | XCTAssertEqual(lunar.description, "二〇二〇年腊月初十") 36 | XCTAssertEqual(lunar.solar.description, "2021-01-22") 37 | } 38 | 39 | func test8() throws { 40 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 1500, lunarMonth: 1, lunarDay: 1, hour: 12).solar.description, "1500-01-31") 41 | } 42 | 43 | func test9() throws { 44 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 1500, lunarMonth: 12, lunarDay: 29, hour: 12).solar.description, "1501-01-18") 45 | } 46 | 47 | func test10() throws { 48 | XCTAssertEqual(Solar.fromYmdHms(year: 1582, month: 10, day: 4, hour: 12).lunar.description, "一五八二年九月十八") 49 | } 50 | 51 | func test11() throws { 52 | XCTAssertEqual(Solar.fromYmdHms(year: 1582, month: 10, day: 15, hour: 12).lunar.description, "一五八二年九月十九") 53 | } 54 | 55 | func test12() throws { 56 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 2021, lunarMonth: 12, lunarDay: 29).festivals[0], "除夕") 57 | } 58 | 59 | func test13() throws { 60 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 2020, lunarMonth: 12, lunarDay: 30).festivals[0], "除夕") 61 | } 62 | 63 | func test14() throws { 64 | XCTAssertEqual(Solar.fromYmdHms(year: 2022, month: 1, day: 31).lunar.festivals[0], "除夕") 65 | } 66 | 67 | func test15() throws { 68 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 2033, lunarMonth: -11, lunarDay: 1).solar.description, "2033-12-22") 69 | } 70 | 71 | func test16() throws { 72 | XCTAssertEqual(Solar.fromYmdHms(year: 1990, month: 10, day: 8).lunar.monthInGanZhiExact, "乙酉") 73 | } 74 | 75 | func test17() throws { 76 | XCTAssertEqual(Solar.fromYmdHms(year: 1990, month: 10, day: 9).lunar.monthInGanZhiExact, "丙戌") 77 | } 78 | 79 | func test18() throws { 80 | XCTAssertEqual(Solar.fromYmdHms(year: 1990, month: 10, day: 8).lunar.monthInGanZhi, "丙戌") 81 | } 82 | 83 | func test19() throws { 84 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 37, lunarMonth: -12, lunarDay: 1).monthInChinese, "闰腊") 85 | } 86 | 87 | func test20() throws { 88 | XCTAssertEqual(Solar.fromYmdHms(year: 5553, month: 1, day: 22).lunar.description, "五五五二年闰腊月初二") 89 | } 90 | 91 | func test21() throws { 92 | XCTAssertEqual(Solar.fromYmdHms(year: 7013, month: 12, day: 24).lunar.description, "七〇一三年闰冬月初四") 93 | } 94 | 95 | func test22() throws { 96 | XCTAssertEqual(Solar.fromYmdHms(year: 4, month: 2, day: 10).lunar.yearShengXiao, "鼠") 97 | } 98 | 99 | func test23() throws { 100 | XCTAssertEqual(Solar.fromYmdHms(year: 4, month: 2, day: 9).lunar.yearShengXiao, "猪") 101 | } 102 | 103 | func test24() throws { 104 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 1518, lunarMonth: 1, lunarDay: 1).solar.ymd, "1518-02-10") 105 | } 106 | 107 | func test25() throws { 108 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 793, lunarMonth: 1, lunarDay: 1).solar.ymd, "0793-02-15") 109 | } 110 | 111 | func test26() throws { 112 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 2025, lunarMonth: -6, lunarDay: 1).solar.ymd, "2025-07-25") 113 | } 114 | 115 | func test27() throws { 116 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 2025, lunarMonth: 6, lunarDay: 1).solar.ymd, "2025-06-25") 117 | } 118 | 119 | func test28() throws { 120 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 41, lunarMonth: 1, lunarDay: 1).solar.ymd, "0041-02-20") 121 | } 122 | 123 | func test29() throws { 124 | XCTAssertEqual(Lunar.fromYmdHms(lunarYear: 1537, lunarMonth: 1, lunarDay: 1).solar.ymd, "1537-02-10") 125 | } 126 | 127 | func test30() throws { 128 | XCTAssertEqual(Solar.fromYmdHms(year: 2021, month: 11, day: 27).lunar.dayYi, ["嫁娶", "祭祀", "祈福", "求嗣", "开光", "出行", "解除", "出火", "拆卸", "进人口", "入宅", "移徙", "安床", "栽种", "动土", "修造", "纳畜", "入殓", "安葬", "立碑", "除服", "成服"]) 129 | } 130 | 131 | func test31() throws { 132 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 3, day: 17).lunar.dayJiShen, ["月德", "天愿", "六合", "金堂"]) 133 | } 134 | 135 | func test32() throws { 136 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 3, day: 17).lunar.dayXiongSha, ["月煞", "月虚", "四击", "天牢"]) 137 | } 138 | 139 | func test33() throws { 140 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 10, day: 14).lunar.dayJiShen, ["阴德", "续世", "明堂"]) 141 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 10, day: 14).lunar.dayXiongSha, ["游祸", "天贼", "血忌", "重日"]) 142 | 143 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 11, day: 11).lunar.dayJiShen, ["母仓", "时阳", "生气", "圣心", "除神", "鸣吠"]) 144 | 145 | XCTAssertEqual(Solar.fromYmdHms(year: 2024, month: 2, day: 10).lunar.dayJiShen, ["守日", "天巫", "福德", "六仪", "金堂", "金匮"]) 146 | XCTAssertEqual(Solar.fromYmdHms(year: 2024, month: 2, day: 10).lunar.dayXiongSha, ["厌对", "招摇", "九空", "九坎", "九焦", "复日"]) 147 | } 148 | 149 | func test34() throws { 150 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 11, day: 14).lunar.dayJiShen, ["官日", "天马", "吉期", "要安", "鸣吠对"]) 151 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 11, day: 14).lunar.dayXiongSha, ["大时", "大败", "咸池", "触水龙", "白虎"]) 152 | } 153 | 154 | func test36() throws { 155 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 11, day: 14).lunar.dayJiShen, ["官日", "天马", "吉期", "要安", "鸣吠对"]) 156 | XCTAssertEqual(Solar.fromYmdHms(year: 2023, month: 11, day: 14).lunar.dayXiongSha, ["大时", "大败", "咸池", "触水龙", "白虎"]) 157 | } 158 | 159 | func test35() throws { 160 | XCTAssertEqual(Solar.fromYmdHms(year: 2022, month: 4, day: 5).lunar.dayJi, ["出火", "入宅", "安葬", "伐木"]) 161 | } 162 | 163 | func test37() throws { 164 | XCTAssertEqual(Solar.fromYmdHms(year: 2021, month: 11, day: 27).lunar.dayYi, ["嫁娶", "祭祀", "祈福", "求嗣", "开光", "出行", "解除", "出火", "拆卸", "进人口", "入宅", "移徙", "安床", "栽种", "动土", "修造", "纳畜", "入殓", "安葬", "立碑", "除服", "成服"]) 165 | } 166 | 167 | func test38() throws { 168 | XCTAssertEqual(Solar.fromYmdHms(year: 2025, month: 2, day: 7).lunar.dayYi, ["嫁娶", "祭祀", "祈福", "出行", "解除", "出火", "拆卸", "动土", "入宅", "移徙", "安床", "上梁", "栽种", "纳畜", "破土", "启钻", "安葬"]) 169 | XCTAssertEqual(Solar.fromYmdHms(year: 2025, month: 2, day: 7).lunar.dayJi, ["开市", "立券", "理发", "作灶"]) 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /Sources/LunarSwift/FotoUtil.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class FotoUtil { 5 | public static var DAY_ZHAI_GUAN_YIN: [String] = ["1-8", "2-7", "2-9", "2-19", "3-3", "3-6", "3-13", "4-22", "5-3", "5-17", "6-16", "6-18", "6-19", "6-23", "7-13", "8-16", "9-19", "9-23", "10-2", "11-19", "11-24", "12-25"] 6 | public static var XIU_27: [String] = ["角", "亢", "氐", "房", "心", "尾", "箕", "斗", "女", "虚", "危", "室", "壁", "奎", "娄", "胃", "昴", "毕", "觜", "参", "井", "鬼", "柳", "星", "张", "翼", "轸"] 7 | public static var XIU_OFFSET: [Int] = [11, 13, 15, 17, 19, 21, 24, 0, 2, 4, 7, 9] 8 | private static var dj = "犯者夺纪" 9 | private static var js = "犯者减寿" 10 | private static var ss = "犯者损寿" 11 | private static var xl = "犯者削禄夺纪" 12 | private static var jw = "犯者三年内夫妇俱亡" 13 | private static var y: [String] = ["杨公忌"] 14 | private static var t: [String] = ["四天王巡行", "", "true"] 15 | private static var d: [String] = ["斗降", dj, "true"] 16 | private static var s: [String] = ["月朔", dj, "true"] 17 | private static var w: [String] = ["月望", dj, "true"] 18 | private static var h: [String] = ["月晦", js, "true"] 19 | private static var l: [String] = ["雷斋日", js, "true"] 20 | private static var j: [String] = ["九毒日", "犯者夭亡,奇祸不测"] 21 | private static var r: [String] = ["人神在阴", "犯者得病", "true", "宜先一日即戒"] 22 | private static var m: [String] = ["司命奏事", js, "true", "如月小,即戒廿九"] 23 | private static var hh: [String] = ["月晦", js, "true", "如月小,即戒廿九"] 24 | 25 | public static var FESTIVAL: [String: [[String]]] = [ 26 | "1-1": [["天腊,玉帝校世人神气禄命", xl], s], 27 | "1-3": [["万神都会", dj], d], 28 | "1-5": [["五虚忌"]], 29 | "1-6": [["六耗忌"], l], 30 | "1-7": [["上会日", ss]], 31 | "1-8": [["五殿阎罗天子诞", dj], t], 32 | "1-9": [["玉皇上帝诞", dj]], 33 | "1-13": [y], 34 | "1-14": [["三元降", js], t], 35 | "1-15": [["三元降", js], ["上元神会", dj], w, t], 36 | "1-16": [["三元降", js]], 37 | "1-19": [["长春真人诞"]], 38 | "1-23": [["三尸神奏事"], t], 39 | "1-25": [h, ["天地仓开日", "犯者损寿,子带疾"]], 40 | "1-27": [d], 41 | "1-28": [r], 42 | "1-29": [t], 43 | "1-30": [hh, m, t], 44 | "2-1": [["一殿秦广王诞", dj], s], 45 | "2-2": [["万神都会", dj], ["福德土地正神诞", "犯者得祸"]], 46 | "2-3": [["文昌帝君诞", xl], d], 47 | "2-6": [["东华帝君诞"], l], 48 | "2-8": [["释迦牟尼佛出家", dj], ["三殿宋帝王诞", dj], ["张大帝诞", dj], t], 49 | "2-11": [y], 50 | "2-14": [t], 51 | "2-15": [["释迦牟尼佛涅槃", xl], ["太上老君诞", xl], ["月望", xl, "true"], t], 52 | "2-17": [["东方杜将军诞"]], 53 | "2-18": [["四殿五官王诞", xl], ["至圣先师孔子讳辰", xl]], 54 | "2-19": [["观音大士诞", dj]], 55 | "2-21": [["普贤菩萨诞"]], 56 | "2-23": [t], 57 | "2-25": [h], 58 | "2-27": [d], 59 | "2-28": [r], 60 | "2-29": [t], 61 | "2-30": [hh, m, t], 62 | "3-1": [["二殿楚江王诞", dj], s], 63 | "3-3": [["玄天上帝诞", dj], d], 64 | "3-6": [l], 65 | "3-8": [["六殿卞城王诞", dj], t], 66 | "3-9": [["牛鬼神出", "犯者产恶胎"], y], 67 | "3-12": [["中央五道诞"]], 68 | "3-14": [t], 69 | "3-15": [["昊天上帝诞", dj], ["玄坛诞", dj], w, t], 70 | "3-16": [["准提菩萨诞", dj]], 71 | "3-19": [["中岳大帝诞"], ["后土娘娘诞"], ["三茅降"]], 72 | "3-20": [["天地仓开日", ss], ["子孙娘娘诞"]], 73 | "3-23": [t], 74 | "3-25": [h], 75 | "3-27": [["七殿泰山王诞"], d], 76 | "3-28": [r, ["苍颉至圣先师诞", xl], ["东岳大帝诞"]], 77 | "3-29": [t], 78 | "3-30": [hh, m, t], 79 | "4-1": [["八殿都市王诞", dj], s], 80 | "4-3": [d], 81 | "4-4": [["万神善会", "犯者失瘼夭胎"], ["文殊菩萨诞"]], 82 | "4-6": [l], 83 | "4-7": [["南斗、北斗、西斗同降", js], y], 84 | "4-8": [["释迦牟尼佛诞", dj], ["万神善会", "犯者失瘼夭胎"], ["善恶童子降", "犯者血死"], ["九殿平等王诞"], t], 85 | "4-14": [["纯阳祖师诞", js], t], 86 | "4-15": [w, ["钟离祖师诞"], t], 87 | "4-16": [["天地仓开日", ss]], 88 | "4-17": [["十殿转轮王诞", dj]], 89 | "4-18": [["天地仓开日", ss], ["紫徽大帝诞", ss]], 90 | "4-20": [["眼光圣母诞"]], 91 | "4-23": [t], 92 | "4-25": [h], 93 | "4-27": [d], 94 | "4-28": [r], 95 | "4-29": [t], 96 | "4-30": [hh, m, t], 97 | "5-1": [["南极长生大帝诞", dj], s], 98 | "5-3": [d], 99 | "5-5": [["地腊", xl], ["五帝校定生人官爵", xl], j, y], 100 | "5-6": [j, l], 101 | "5-7": [j], 102 | "5-8": [["南方五道诞"], t], 103 | "5-11": [["天地仓开日", ss], ["天下都城隍诞"]], 104 | "5-12": [["炳灵公诞"]], 105 | "5-13": [["关圣降", xl]], 106 | "5-14": [["夜子时为天地交泰", jw], t], 107 | "5-15": [w, j, t], 108 | "5-16": [["九毒日", jw], ["天地元气造化万物之辰", jw]], 109 | "5-17": [j], 110 | "5-18": [["张天师诞"]], 111 | "5-22": [["孝娥神诞", dj]], 112 | "5-23": [t], 113 | "5-25": [j, h], 114 | "5-26": [j], 115 | "5-27": [j, d], 116 | "5-28": [r], 117 | "5-29": [t], 118 | "5-30": [hh, m, t], 119 | "6-1": [s], 120 | "6-3": [["韦驮菩萨圣诞"], d, y], 121 | "6-5": [["南赡部洲转大轮", ss]], 122 | "6-6": [["天地仓开日", ss], l], 123 | "6-8": [t], 124 | "6-10": [["金粟如来诞"]], 125 | "6-14": [t], 126 | "6-15": [w, t], 127 | "6-19": [["观世音菩萨成道", dj]], 128 | "6-23": [["南方火神诞", "犯者遭回禄"], t], 129 | "6-24": [["雷祖诞", xl], ["关帝诞", xl]], 130 | "6-25": [h], 131 | "6-27": [d], 132 | "6-28": [r], 133 | "6-29": [t], 134 | "6-30": [hh, m, t], 135 | "7-1": [s, y], 136 | "7-3": [d], 137 | "7-5": [["中会日", ss, "", "一作初七"]], 138 | "7-6": [l], 139 | "7-7": [["道德腊", xl], ["五帝校生人善恶", xl], ["魁星诞", xl]], 140 | "7-8": [t], 141 | "7-10": [["阴毒日", "", "", "大忌"]], 142 | "7-12": [["长真谭真人诞"]], 143 | "7-13": [["大势至菩萨诞", js]], 144 | "7-14": [["三元降", js], t], 145 | "7-15": [w, ["三元降", dj], ["地官校籍", dj], t], 146 | "7-16": [["三元降", js]], 147 | "7-18": [["西王母诞", dj]], 148 | "7-19": [["太岁诞", dj]], 149 | "7-22": [["增福财神诞", xl]], 150 | "7-23": [t], 151 | "7-25": [h], 152 | "7-27": [d], 153 | "7-28": [r], 154 | "7-29": [y, t], 155 | "7-30": [["地藏菩萨诞", dj], hh, m, t], 156 | "8-1": [s, ["许真君诞"]], 157 | "8-3": [d, ["北斗诞", xl], ["司命灶君诞", "犯者遭回禄"]], 158 | "8-5": [["雷声大帝诞", dj]], 159 | "8-6": [l], 160 | "8-8": [t], 161 | "8-10": [["北斗大帝诞"]], 162 | "8-12": [["西方五道诞"]], 163 | "8-14": [t], 164 | "8-15": [w, ["太明朝元", "犯者暴亡", "", "宜焚香守夜"], t], 165 | "8-16": [["天曹掠刷真君降", "犯者贫夭"]], 166 | "8-18": [["天人兴福之辰", "", "", "宜斋戒,存想吉事"]], 167 | "8-23": [["汉恒候张显王诞"], t], 168 | "8-24": [["灶君夫人诞"]], 169 | "8-25": [h], 170 | "8-27": [d, ["至圣先师孔子诞", xl], y], 171 | "8-28": [r, ["四天会事"]], 172 | "8-29": [t], 173 | "8-30": [["诸神考校", "犯者夺算"], hh, m, t], 174 | "9-1": [s, ["南斗诞", xl], ["北斗九星降世", dj, "", "此九日俱宜斋戒"]], 175 | "9-3": [d, ["五瘟神诞"]], 176 | "9-6": [l], 177 | "9-8": [t], 178 | "9-9": [["斗母诞", xl], ["酆都大帝诞"], ["玄天上帝飞升"]], 179 | "9-10": [["斗母降", dj]], 180 | "9-11": [["宜戒"]], 181 | "9-13": [["孟婆尊神诞"]], 182 | "9-14": [t], 183 | "9-15": [w, t], 184 | "9-17": [["金龙四大王诞", "犯者遭水厄"]], 185 | "9-19": [["日宫月宫会合", js], ["观世音菩萨诞", js]], 186 | "9-23": [t], 187 | "9-25": [h, y], 188 | "9-27": [d], 189 | "9-28": [r], 190 | "9-29": [t], 191 | "9-30": [["药师琉璃光佛诞", "犯者危疾"], hh, m, t], 192 | "10-1": [s, ["民岁腊", dj], ["四天王降", "犯者一年内死"]], 193 | "10-3": [d, ["三茅诞"]], 194 | "10-5": [["下会日", js], ["达摩祖师诞", js]], 195 | "10-6": [l, ["天曹考察", dj]], 196 | "10-8": [["佛涅槃日", "", "", "大忌色欲"], t], 197 | "10-10": [["四天王降", "犯者一年内死"]], 198 | "10-11": [["宜戒"]], 199 | "10-14": [["三元降", js], t], 200 | "10-15": [w, ["三元降", dj], ["下元水府校籍", dj], t], 201 | "10-16": [["三元降", js], t], 202 | "10-23": [y, t], 203 | "10-25": [h], 204 | "10-27": [d, ["北极紫徽大帝降"]], 205 | "10-28": [r], 206 | "10-29": [t], 207 | "10-30": [hh, m, t], 208 | "11-1": [s], 209 | "11-3": [d], 210 | "11-4": [["至圣先师孔子诞", xl]], 211 | "11-6": [["西岳大帝诞"]], 212 | "11-8": [t], 213 | "11-11": [["天地仓开日", dj], ["太乙救苦天尊诞", dj]], 214 | "11-14": [t], 215 | "11-15": [["月望", "上半夜犯男死 下半夜犯女死"], ["四天王巡行", "上半夜犯男死 下半夜犯女死"]], 216 | "11-17": [["阿弥陀佛诞"]], 217 | "11-19": [["太阳日宫诞", "犯者得奇祸"]], 218 | "11-21": [y], 219 | "11-23": [["张仙诞", "犯者绝嗣"], t], 220 | "11-25": [["掠刷大夫降", "犯者遭大凶"], h], 221 | "11-26": [["北方五道诞"]], 222 | "11-27": [d], 223 | "11-28": [r], 224 | "11-29": [t], 225 | "11-30": [hh, m, t], 226 | "12-1": [s], 227 | "12-3": [d], 228 | "12-6": [["天地仓开日", js], l], 229 | "12-7": [["掠刷大夫降", "犯者得恶疾"]], 230 | "12-8": [["王侯腊", dj], ["释迦如来成佛之辰"], t, ["初旬内戊日,亦名王侯腊", dj]], 231 | "12-12": [["太素三元君朝真"]], 232 | "12-14": [t], 233 | "12-15": [w, t], 234 | "12-16": [["南岳大帝诞"]], 235 | "12-19": [y], 236 | "12-20": [["天地交道", "犯者促寿"]], 237 | "12-21": [["天猷上帝诞"]], 238 | "12-23": [["五岳诞降"], t], 239 | "12-24": [["司今朝天奏人善恶", "犯者得大祸"]], 240 | "12-25": [["三清玉帝同降,考察善恶", "犯者得奇祸"], h], 241 | "12-27": [d], 242 | "12-28": [r], 243 | "12-29": [["华严菩萨诞"], t], 244 | "12-30": [["诸神下降,察访善恶", "犯者男女俱亡"]], 245 | ] 246 | 247 | public static var OTHER_FESTIVAL: [String: [String]] = [ 248 | "1-1": ["弥勒菩萨圣诞"], 249 | "1-6": ["定光佛圣诞"], 250 | "2-8": ["释迦牟尼佛出家"], 251 | "2-15": ["释迦牟尼佛涅槃"], 252 | "2-19": ["观世音菩萨圣诞"], 253 | "2-21": ["普贤菩萨圣诞"], 254 | "3-16": ["准提菩萨圣诞"], 255 | "4-4": ["文殊菩萨圣诞"], 256 | "4-8": ["释迦牟尼佛圣诞"], 257 | "4-15": ["佛吉祥日"], 258 | "4-28": ["药王菩萨圣诞"], 259 | "5-13": ["伽蓝菩萨圣诞"], 260 | "6-3": ["韦驮菩萨圣诞"], 261 | "6-19": ["观音菩萨成道"], 262 | "7-13": ["大势至菩萨圣诞"], 263 | "7-15": ["佛欢喜日"], 264 | "7-24": ["龙树菩萨圣诞"], 265 | "7-30": ["地藏菩萨圣诞"], 266 | "8-15": ["月光菩萨圣诞"], 267 | "8-22": ["燃灯佛圣诞"], 268 | "9-9": ["摩利支天菩萨圣诞"], 269 | "9-19": ["观世音菩萨出家"], 270 | "9-30": ["药师琉璃光佛圣诞"], 271 | "10-5": ["达摩祖师圣诞"], 272 | "10-20": ["文殊菩萨出家"], 273 | "11-17": ["阿弥陀佛圣诞"], 274 | "11-19": ["日光菩萨圣诞"], 275 | "12-8": ["释迦牟尼佛成道"], 276 | "12-23": ["监斋菩萨圣诞"], 277 | "12-29": ["华严菩萨圣诞"], 278 | ] 279 | 280 | public class func getXiu(month: Int, day: Int) -> String { 281 | XIU_27[(XIU_OFFSET[abs(month) - 1] + day - 1) % XIU_27.count] 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /Sources/LunarSwift/SolarUtil.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class SolarUtil { 5 | public static var WEEK: [String] = ["日", "一", "二", "三", "四", "五", "六"] 6 | public static var DAYS_OF_MONTH: [Int] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 7 | public static var XING_ZUO: [String] = ["白羊", "金牛", "双子", "巨蟹", "狮子", "处女", "天秤", "天蝎", "射手", "摩羯", "水瓶", "双鱼"] 8 | public static var FESTIVAL: [String: String] = [ 9 | "1-1": "元旦节", 10 | "2-14": "情人节", 11 | "3-8": "妇女节", 12 | "3-12": "植树节", 13 | "3-15": "消费者权益日", 14 | "4-1": "愚人节", 15 | "5-1": "劳动节", 16 | "5-4": "青年节", 17 | "6-1": "儿童节", 18 | "7-1": "建党节", 19 | "8-1": "建军节", 20 | "9-10": "教师节", 21 | "10-1": "国庆节", 22 | "10-31": "万圣节前夜", 23 | "11-1": "万圣节", 24 | "12-24": "平安夜", 25 | "12-25": "圣诞节", 26 | ] 27 | public static var WEEK_FESTIVAL: [String: String] = [ 28 | "3-0-1": "全国中小学生安全教育日", 29 | "5-2-0": "母亲节", 30 | "5-3-0": "全国助残日", 31 | "6-3-0": "父亲节", 32 | "9-3-6": "全民国防教育日", 33 | "10-1-1": "世界住房日", 34 | "11-4-4": "感恩节", 35 | ] 36 | public static var OTHER_FESTIVAL: [String: [String]] = [ 37 | "1-8": ["周恩来逝世纪念日"], 38 | "1-10": ["中国人民警察节"], 39 | "1-14": ["日记情人节"], 40 | "1-21": ["列宁逝世纪念日"], 41 | "1-26": ["国际海关日"], 42 | "1-27": ["国际大屠杀纪念日"], 43 | "2-2": ["世界湿地日"], 44 | "2-4": ["世界抗癌日"], 45 | "2-7": ["京汉铁路罢工纪念日"], 46 | "2-10": ["国际气象节"], 47 | "2-19": ["邓小平逝世纪念日"], 48 | "2-20": ["世界社会公正日"], 49 | "2-21": ["国际母语日"], 50 | "2-24": ["第三世界青年日"], 51 | "3-1": ["国际海豹日"], 52 | "3-3": ["世界野生动植物日", "全国爱耳日"], 53 | "3-5": ["周恩来诞辰纪念日", "中国青年志愿者服务日"], 54 | "3-6": ["世界青光眼日"], 55 | "3-7": ["女生节"], 56 | "3-12": ["孙中山逝世纪念日"], 57 | "3-14": ["马克思逝世纪念日", "白色情人节"], 58 | "3-17": ["国际航海日"], 59 | "3-18": ["全国科技人才活动日", "全国爱肝日"], 60 | "3-20": ["国际幸福日"], 61 | "3-21": ["世界森林日", "世界睡眠日", "国际消除种族歧视日"], 62 | "3-22": ["世界水日"], 63 | "3-23": ["世界气象日"], 64 | "3-24": ["世界防治结核病日"], 65 | "3-29": ["中国黄花岗七十二烈士殉难纪念日"], 66 | "4-2": ["国际儿童图书日", "世界自闭症日"], 67 | "4-4": ["国际地雷行动日"], 68 | "4-7": ["世界卫生日"], 69 | "4-8": ["国际珍稀动物保护日"], 70 | "4-12": ["世界航天日"], 71 | "4-14": ["黑色情人节"], 72 | "4-15": ["全民国家安全教育日"], 73 | "4-22": ["世界地球日", "列宁诞辰纪念日"], 74 | "4-23": ["世界读书日"], 75 | "4-24": ["中国航天日"], 76 | "4-25": ["儿童预防接种宣传日"], 77 | "4-26": ["世界知识产权日", "全国疟疾日"], 78 | "4-28": ["世界安全生产与健康日"], 79 | "4-30": ["全国交通安全反思日"], 80 | "5-2": ["世界金枪鱼日"], 81 | "5-3": ["世界新闻自由日"], 82 | "5-5": ["马克思诞辰纪念日"], 83 | "5-8": ["世界红十字日"], 84 | "5-11": ["世界肥胖日"], 85 | "5-12": ["全国防灾减灾日", "护士节"], 86 | "5-14": ["玫瑰情人节"], 87 | "5-15": ["国际家庭日"], 88 | "5-19": ["中国旅游日"], 89 | "5-20": ["网络情人节"], 90 | "5-22": ["国际生物多样性日"], 91 | "5-25": ["525心理健康节"], 92 | "5-27": ["上海解放日"], 93 | "5-29": ["国际维和人员日"], 94 | "5-30": ["中国五卅运动纪念日"], 95 | "5-31": ["世界无烟日"], 96 | "6-3": ["世界自行车日"], 97 | "6-5": ["世界环境日"], 98 | "6-6": ["全国爱眼日"], 99 | "6-8": ["世界海洋日"], 100 | "6-11": ["中国人口日"], 101 | "6-14": ["世界献血日", "亲亲情人节"], 102 | "6-17": ["世界防治荒漠化与干旱日"], 103 | "6-20": ["世界难民日"], 104 | "6-21": ["国际瑜伽日"], 105 | "6-25": ["全国土地日"], 106 | "6-26": ["国际禁毒日", "联合国宪章日"], 107 | "7-1": ["香港回归纪念日"], 108 | "7-6": ["国际接吻日", "朱德逝世纪念日"], 109 | "7-7": ["七七事变纪念日"], 110 | "7-11": ["世界人口日", "中国航海日"], 111 | "7-14": ["银色情人节"], 112 | "7-18": ["曼德拉国际日"], 113 | "7-30": ["国际友谊日"], 114 | "8-3": ["男人节"], 115 | "8-5": ["恩格斯逝世纪念日"], 116 | "8-6": ["国际电影节"], 117 | "8-8": ["全民健身日"], 118 | "8-9": ["国际土著人日"], 119 | "8-12": ["国际青年节"], 120 | "8-14": ["绿色情人节"], 121 | "8-19": ["世界人道主义日", "中国医师节"], 122 | "8-22": ["邓小平诞辰纪念日"], 123 | "8-29": ["全国测绘法宣传日"], 124 | "9-3": ["中国抗日战争胜利纪念日"], 125 | "9-5": ["中华慈善日"], 126 | "9-8": ["世界扫盲日"], 127 | "9-9": ["毛泽东逝世纪念日", "全国拒绝酒驾日"], 128 | "9-14": ["世界清洁地球日", "相片情人节"], 129 | "9-15": ["国际民主日"], 130 | "9-16": ["国际臭氧层保护日"], 131 | "9-17": ["世界骑行日"], 132 | "9-18": ["九一八事变纪念日"], 133 | "9-20": ["全国爱牙日"], 134 | "9-21": ["国际和平日"], 135 | "9-27": ["世界旅游日"], 136 | "9-30": ["中国烈士纪念日"], 137 | "10-1": ["国际老年人日"], 138 | "10-2": ["国际非暴力日"], 139 | "10-4": ["世界动物日"], 140 | "10-11": ["国际女童日"], 141 | "10-10": ["辛亥革命纪念日"], 142 | "10-13": ["国际减轻自然灾害日", "中国少年先锋队诞辰日"], 143 | "10-14": ["葡萄酒情人节"], 144 | "10-16": ["世界粮食日"], 145 | "10-17": ["全国扶贫日"], 146 | "10-20": ["世界统计日"], 147 | "10-24": ["世界发展信息日", "程序员节"], 148 | "10-25": ["抗美援朝纪念日"], 149 | "11-5": ["世界海啸日"], 150 | "11-8": ["记者节"], 151 | "11-9": ["全国消防日"], 152 | "11-11": ["光棍节"], 153 | "11-12": ["孙中山诞辰纪念日"], 154 | "11-14": ["电影情人节"], 155 | "11-16": ["国际宽容日"], 156 | "11-17": ["国际大学生节"], 157 | "11-19": ["世界厕所日"], 158 | "11-28": ["恩格斯诞辰纪念日"], 159 | "11-29": ["国际声援巴勒斯坦人民日"], 160 | "12-1": ["世界艾滋病日"], 161 | "12-2": ["全国交通安全日"], 162 | "12-3": ["世界残疾人日"], 163 | "12-4": ["全国法制宣传日"], 164 | "12-5": ["世界弱能人士日", "国际志愿人员日"], 165 | "12-7": ["国际民航日"], 166 | "12-9": ["世界足球日", "国际反腐败日"], 167 | "12-10": ["世界人权日"], 168 | "12-11": ["国际山岳日"], 169 | "12-12": ["西安事变纪念日"], 170 | "12-13": ["国家公祭日"], 171 | "12-14": ["拥抱情人节"], 172 | "12-18": ["国际移徙者日"], 173 | "12-26": ["毛泽东诞辰纪念日"], 174 | ] 175 | 176 | public class func isLeapYear(year: Int) -> Bool { 177 | if year < 1600 { 178 | return year % 4 == 0 179 | } 180 | return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) 181 | } 182 | 183 | public class func getDaysOfYear(year: Int) -> Int { 184 | if 1582 == year { 185 | return 355 186 | } 187 | return isLeapYear(year: year) ? 366 : 365 188 | } 189 | 190 | public class func getDaysOfMonth(year: Int, month: Int) -> Int { 191 | if 1582 == year && 10 == month { 192 | return 21 193 | } 194 | let m = month - 1 195 | var d = DAYS_OF_MONTH[m] 196 | if m == 1 && isLeapYear(year: year) { 197 | d += 1 198 | } 199 | return d 200 | } 201 | 202 | public class func getDaysInYear(year: Int, month: Int, day: Int) -> Int { 203 | var days = 0 204 | for i in 1 ..< month { 205 | days += getDaysOfMonth(year: year, month: i) 206 | } 207 | var d = day 208 | if 1582 == year && 10 == month { 209 | if day >= 15 { 210 | d -= 10 211 | } else if day > 4 { 212 | fatalError("wrong solar year \(year) month \(month) day \(day)") 213 | } 214 | } 215 | days += d 216 | return days 217 | } 218 | 219 | public class func getWeeksOfMonth(year: Int, month: Int, start: Int) -> Int { 220 | Int(ceil(Double(getDaysOfMonth(year: year, month: month) + Solar.fromYmdHms(year: year, month: month, day: 1).week - start) / Double(7))) 221 | } 222 | 223 | public class func getWeek(year: Int, month: Int, day: Int) -> Int { 224 | var y = year 225 | var m = month 226 | // 蔡勒公式 227 | if m < 3 { 228 | m += 12 229 | y -= 1 230 | } 231 | let c = y / 100 232 | y = y - c * 100 233 | let x = y + y / 4 + c / 4 - 2 * c 234 | var w = 0 235 | if isBefore(ay: year, am: month, ad: day, ah: 0, ai: 0, ac: 0, by: 1582, bm: 10, bd: 15, bh: 0, bi: 0, bc: 0) { 236 | w = (x + 13 * (m + 1) / 5 + day + 2) % 7 237 | } else { 238 | w = (x + 26 * (m + 1) / 10 + day - 1) % 7 239 | } 240 | return (w + 7) % 7 241 | } 242 | 243 | public class func isBefore(ay: Int, am: Int, ad: Int, ah: Int, ai: Int, ac: Int, by: Int, bm: Int, bd: Int, bh: Int, bi: Int, bc: Int) -> Bool { 244 | if ay > by { 245 | return false 246 | } 247 | if ay < by { 248 | return true 249 | } 250 | if am > bm { 251 | return false 252 | } 253 | if am < bm { 254 | return true 255 | } 256 | if ad > bd { 257 | return false 258 | } 259 | if ad < bd { 260 | return true 261 | } 262 | if ah > bh { 263 | return false 264 | } 265 | if ah < bh { 266 | return true 267 | } 268 | if ai > bi { 269 | return false 270 | } 271 | if ai < bi { 272 | return true 273 | } 274 | return ac < bc 275 | } 276 | 277 | public class func isAfter(ay: Int, am: Int, ad: Int, ah: Int, ai: Int, ac: Int, by: Int, bm: Int, bd: Int, bh: Int, bi: Int, bc: Int) -> Bool { 278 | if ay > by { 279 | return true 280 | } 281 | if ay < by { 282 | return false 283 | } 284 | if am > bm { 285 | return true 286 | } 287 | if am < bm { 288 | return false 289 | } 290 | if ad > bd { 291 | return true 292 | } 293 | if ad < bd { 294 | return false 295 | } 296 | if ah > bh { 297 | return true 298 | } 299 | if ah < bh { 300 | return false 301 | } 302 | if ai > bi { 303 | return true 304 | } 305 | if ai < bi { 306 | return false 307 | } 308 | return ac > bc 309 | } 310 | 311 | public class func getDaysBetween(ay: Int, am: Int, ad: Int, by: Int, bm: Int, bd: Int) -> Int { 312 | var n: Int 313 | var days: Int 314 | if ay == by { 315 | n = getDaysInYear(year: by, month: bm, day: bd) - getDaysInYear(year: ay, month: am, day: ad) 316 | } else if ay > by { 317 | days = getDaysOfYear(year: by) - getDaysInYear(year: by, month: bm, day: bd) 318 | for i in by + 1 ..< ay { 319 | days += getDaysOfYear(year: i) 320 | } 321 | days += getDaysInYear(year: ay, month: am, day: ad) 322 | n = -days 323 | } else { 324 | days = getDaysOfYear(year: ay) - getDaysInYear(year: ay, month: am, day: ad) 325 | for i in ay + 1 ..< by { 326 | days += getDaysOfYear(year: i) 327 | } 328 | days += getDaysInYear(year: by, month: bm, day: bd) 329 | n = days 330 | } 331 | return n 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /Sources/LunarSwift/LunarYear.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class LunarYear: NSObject { 5 | public static var YUAN: [String] = ["下", "上", "中"] 6 | public static var YUN: [String] = ["七", "八", "九", "一", "二", "三", "四", "五", "六"] 7 | private static var LEAP_11: [Int] = [75, 94, 170, 265, 322, 398, 469, 553, 583, 610, 678, 735, 754, 773, 849, 887, 936, 1050, 1069, 1126, 1145, 1164, 1183, 1259, 1278, 1308, 1373, 1403, 1441, 1460, 1498, 1555, 1593, 1612, 1631, 1642, 2033, 2128, 2147, 2242, 2614, 2728, 2910, 3062, 3244, 3339, 3616, 3711, 3730, 3825, 4007, 4159, 4197, 4322, 4341, 4379, 4417, 4531, 4599, 4694, 4713, 4789, 4808, 4971, 5085, 5104, 5161, 5180, 5199, 5294, 5305, 5476, 5677, 5696, 5772, 5791, 5848, 5886, 6049, 6068, 6144, 6163, 6258, 6402, 6440, 6497, 6516, 6630, 6641, 6660, 6679, 6736, 6774, 6850, 6869, 6899, 6918, 6994, 7013, 7032, 7051, 7070, 7089, 7108, 7127, 7146, 7222, 7271, 7290, 7309, 7366, 7385, 7404, 7442, 7461, 7480, 7491, 7499, 7594, 7624, 7643, 7662, 7681, 7719, 7738, 7814, 7863, 7882, 7901, 7939, 7958, 7977, 7996, 8034, 8053, 8072, 8091, 8121, 8159, 8186, 8216, 8235, 8254, 8273, 8311, 8330, 8341, 8349, 8368, 8444, 8463, 8474, 8493, 8531, 8569, 8588, 8626, 8664, 8683, 8694, 8702, 8713, 8721, 8751, 8789, 8808, 8816, 8827, 8846, 8884, 8903, 8922, 8941, 8971, 9036, 9066, 9085, 9104, 9123, 9142, 9161, 9180, 9199, 9218, 9256, 9294, 9313, 9324, 9343, 9362, 9381, 9419, 9438, 9476, 9514, 9533, 9544, 9552, 9563, 9571, 9582, 9601, 9639, 9658, 9666, 9677, 9696, 9734, 9753, 9772, 9791, 9802, 9821, 9886, 9897, 9916, 9935, 9954, 9973, 9992] 8 | private static var LEAP_12: [Int] = [37, 56, 113, 132, 151, 189, 208, 227, 246, 284, 303, 341, 360, 379, 417, 436, 458, 477, 496, 515, 534, 572, 591, 629, 648, 667, 697, 716, 792, 811, 830, 868, 906, 925, 944, 963, 982, 1001, 1020, 1039, 1058, 1088, 1153, 1202, 1221, 1240, 1297, 1335, 1392, 1411, 1422, 1430, 1517, 1525, 1536, 1574, 3358, 3472, 3806, 3988, 4751, 4941, 5066, 5123, 5275, 5343, 5438, 5457, 5495, 5533, 5552, 5715, 5810, 5829, 5905, 5924, 6421, 6535, 6793, 6812, 6888, 6907, 7002, 7184, 7260, 7279, 7374, 7556, 7746, 7757, 7776, 7833, 7852, 7871, 7966, 8015, 8110, 8129, 8148, 8224, 8243, 8338, 8406, 8425, 8482, 8501, 8520, 8558, 8596, 8607, 8615, 8645, 8740, 8778, 8835, 8865, 8930, 8960, 8979, 8998, 9017, 9055, 9074, 9093, 9112, 9150, 9188, 9237, 9275, 9332, 9351, 9370, 9408, 9427, 9446, 9457, 9465, 9495, 9560, 9590, 9628, 9647, 9685, 9715, 9742, 9780, 9810, 9818, 9829, 9848, 9867, 9905, 9924, 9943, 9962, 10000] 9 | private static var YMC: [Int] = [11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 10 | private static var CACHE_YEAR: LunarYear? 11 | 12 | private var _year: Int 13 | private var _ganIndex: Int 14 | private var _zhiIndex: Int 15 | private var _months: [LunarMonth] 16 | private var _jieQiJulianDays: [Double] 17 | 18 | public var year: Int { 19 | _year 20 | } 21 | 22 | public var ganIndex: Int { 23 | _ganIndex 24 | } 25 | 26 | public var zhiIndex: Int { 27 | _zhiIndex 28 | } 29 | 30 | public var months: [LunarMonth] { 31 | _months 32 | } 33 | 34 | public var monthsInYear: [LunarMonth] { 35 | var l: [LunarMonth] = [] 36 | for m in _months { 37 | if m.year == _year { 38 | l.append(m) 39 | } 40 | } 41 | return l 42 | } 43 | 44 | public var dayCount: Int { 45 | var n = 0 46 | for m in _months { 47 | if m.year == _year { 48 | n += m.dayCount 49 | } 50 | } 51 | return n 52 | } 53 | 54 | public var leapMonth: Int { 55 | for m in _months { 56 | if m.year == _year && m.leap { 57 | return abs(m.month) 58 | } 59 | } 60 | return 0 61 | } 62 | 63 | public var jieQiJulianDays: [Double] { 64 | _jieQiJulianDays 65 | } 66 | 67 | public var gan: String { 68 | LunarUtil.GAN[_ganIndex + 1] 69 | } 70 | 71 | public var zhi: String { 72 | LunarUtil.ZHI[_zhiIndex + 1] 73 | } 74 | 75 | public var ganZhi: String { 76 | "\(gan)\(zhi)" 77 | } 78 | 79 | public var yuan: String { 80 | "\(LunarYear.YUAN[((_year + 2696) / 60) % 3])元" 81 | } 82 | 83 | public var yun: String { 84 | "\(LunarYear.YUN[((year + 2696) / 20) % 9])运" 85 | } 86 | 87 | public var positionXi: String { 88 | "\(LunarUtil.POSITION_XI[_ganIndex + 1])" 89 | } 90 | 91 | public var positionXiDesc: String { 92 | LunarUtil.POSITION_DESC[positionXi]! 93 | } 94 | 95 | public var positionYangGui: String { 96 | "\(LunarUtil.POSITION_YANG_GUI[_ganIndex + 1])" 97 | } 98 | 99 | public var positionYangGuiDesc: String { 100 | LunarUtil.POSITION_DESC[positionYangGui]! 101 | } 102 | 103 | public var positionYinGui: String { 104 | "\(LunarUtil.POSITION_YIN_GUI[_ganIndex + 1])" 105 | } 106 | 107 | public var positionYinGuiDesc: String { 108 | LunarUtil.POSITION_DESC[positionYinGui]! 109 | } 110 | 111 | public var positionCai: String { 112 | "\(LunarUtil.POSITION_CAI[_ganIndex + 1])" 113 | } 114 | 115 | public var positionCaiDesc: String { 116 | LunarUtil.POSITION_DESC[positionCai]! 117 | } 118 | 119 | public func getPositionFu(sect: Int) -> String { 120 | let index = _ganIndex + 1 121 | if 1 == sect { 122 | return LunarUtil.POSITION_FU[index] 123 | } 124 | return LunarUtil.POSITION_FU_2[index] 125 | } 126 | 127 | public func getPositionFuDesc(sect: Int) -> String { 128 | LunarUtil.POSITION_DESC[getPositionFu(sect: sect)]! 129 | } 130 | 131 | public var positionFu: String { 132 | getPositionFu(sect: 2) 133 | } 134 | 135 | public var positionFuDesc: String { 136 | getPositionFuDesc(sect: 2) 137 | } 138 | 139 | public var positionTaiSui: String { 140 | "\(LunarUtil.POSITION_TAI_SUI_YEAR[_zhiIndex])" 141 | } 142 | 143 | public var positionTaiSuiDesc: String { 144 | LunarUtil.POSITION_DESC[positionTaiSui]! 145 | } 146 | 147 | public init(lunarYear: Int) { 148 | _year = lunarYear 149 | _months = [LunarMonth]() 150 | let offset = lunarYear - 4 151 | var yearGanIndex = offset % 10 152 | var yearZhiIndex = offset % 12 153 | if yearGanIndex < 0 { 154 | yearGanIndex += 10 155 | } 156 | if yearZhiIndex < 0 { 157 | yearZhiIndex += 12 158 | } 159 | _ganIndex = yearGanIndex 160 | _zhiIndex = yearZhiIndex 161 | // 节气 162 | var jq = [Double]() 163 | // 合朔,即每月初一 164 | var hs = [Double]() 165 | // 每月天数,长度15 166 | var dayCounts = [Int]() 167 | var months = [Int]() 168 | 169 | let currentYear = _year 170 | 171 | var jd: Double = floor((Double(currentYear) - Double(2000)) * Double(365.2422) + Double(180)) 172 | // 355是2000.12冬至,得到较靠近jd的冬至估计值 173 | var w: Double = floor((jd - Double(355) + Double(183)) / Double(365.2422)) * Double(365.2422) + Double(355) 174 | if ShouXingUtil.calcQi(pjd: w) > jd { 175 | w -= Double(365.2422) 176 | } 177 | // 25个节气时刻(北京时间),从冬至开始到下一个冬至以后 178 | for i in 0 ..< 26 { 179 | jq.append(ShouXingUtil.calcQi(pjd: w + Double(15.2184) * Double(i))) 180 | } 181 | _jieQiJulianDays = [Double]() 182 | // 从上年的大雪到下年的立春 183 | for i in 0 ..< Lunar.JIE_QI_IN_USE.count { 184 | if i == 0 { 185 | jd = ShouXingUtil.qiAccurate2(jd: jq[0] - Double(15.2184)) 186 | } else if i <= 26 { 187 | jd = ShouXingUtil.qiAccurate2(jd: jq[i - 1]) 188 | } else { 189 | jd = ShouXingUtil.qiAccurate2(jd: jq[25] + Double(15.2184) * Double(i - 26)) 190 | } 191 | _jieQiJulianDays.append(jd + Solar.J2000) 192 | } 193 | 194 | // 冬至前的初一,今年"首朔"的日月黄经差w 195 | w = ShouXingUtil.calcShuo(pjd: jq[0]) 196 | if w > jq[0] { 197 | w -= Double(29.53) 198 | } 199 | // 递推每月初一 200 | for i in 0 ..< 16 { 201 | hs.append(ShouXingUtil.calcShuo(pjd: w + Double(29.5306) * Double(i))) 202 | } 203 | // 每月 204 | for i in 0 ..< 15 { 205 | dayCounts.append(Int(hs[i + 1] - hs[i])) 206 | months.append(i) 207 | } 208 | 209 | let prevYear: Int = currentYear - 1 210 | var leapIndex: Int = 16 211 | if LunarYear.LEAP_11.contains(currentYear) { 212 | leapIndex = 13 213 | } else if LunarYear.LEAP_12.contains(currentYear) { 214 | leapIndex = 14 215 | } else if hs[13] <= jq[24] { 216 | var i = 1 217 | while hs[i + 1] > jq[2 * i] && i < 13 { 218 | i += 1 219 | } 220 | leapIndex = i 221 | } 222 | if leapIndex < 15 { 223 | for i in leapIndex ..< 15 { 224 | months[i] -= 1 225 | } 226 | } 227 | 228 | var fm: Int = -1 229 | var index: Int = -1 230 | var y: Int = prevYear 231 | for i in 0 ..< 15 { 232 | let dm: Double = hs[i] + Solar.J2000 233 | let v2: Int = months[i] 234 | var mc: Int = LunarYear.YMC[v2 % 12] 235 | if 1724360 <= dm && dm < 1729794 { 236 | mc = LunarYear.YMC[(v2 + 1) % 12] 237 | } else if 1807724 <= dm && dm < 1808699 { 238 | mc = LunarYear.YMC[(v2 + 1) % 12] 239 | } else if dm == 1729794 || dm == 1808699 { 240 | mc = 12 241 | } 242 | if fm == -1 { 243 | fm = mc 244 | index = mc 245 | } 246 | if mc < fm { 247 | y += 1 248 | index = 1 249 | } 250 | fm = mc 251 | if i == leapIndex { 252 | mc = -mc 253 | } else if dm == 1729794 || dm == 1808699 { 254 | mc = -11 255 | } 256 | _months.append(LunarMonth(lunarYear: y, lunarMonth: mc, dayCount: dayCounts[i], firstJulianDay: hs[i] + Solar.J2000, index: index)) 257 | index += 1 258 | } 259 | } 260 | 261 | public class func fromYear(lunarYear: Int) -> LunarYear { 262 | var y: LunarYear 263 | if nil == CACHE_YEAR || CACHE_YEAR!.year != lunarYear { 264 | y = LunarYear(lunarYear: lunarYear) 265 | CACHE_YEAR = y 266 | } else { 267 | y = CACHE_YEAR! 268 | } 269 | return y 270 | } 271 | 272 | public func getMonth(lunarMonth: Int) -> LunarMonth? { 273 | for m in _months { 274 | if m.year == _year && m.month == lunarMonth { 275 | return m 276 | } 277 | } 278 | return nil 279 | } 280 | 281 | public var nineStar: NineStar { 282 | let index = LunarUtil.getJiaZiIndex(ganZhi: ganZhi) + 1 283 | let yuan = ((_year + 2696) / 60) % 3 284 | var offset = (62 + yuan * 3 - index) % 9 285 | if 0 == offset { 286 | offset = 9 287 | } 288 | return NineStar(index: offset - 1) 289 | } 290 | 291 | override public var description: String { 292 | "\(_year)" 293 | } 294 | 295 | public var fullString: String { 296 | "\(year)年" 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /Sources/LunarSwift/EightChar.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class EightChar: NSObject { 5 | public static var MONTH_ZHI: [String] = ["", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥", "子", "丑"] 6 | public static var CHANG_SHENG: [String] = ["长生", "沐浴", "冠带", "临官", "帝旺", "衰", "病", "死", "墓", "绝", "胎", "养"] 7 | public static var CHANG_SHENG_OFFSET: [String: Int] = [ 8 | "甲": 1, 9 | "丙": 10, 10 | "戊": 10, 11 | "庚": 7, 12 | "壬": 4, 13 | "乙": 6, 14 | "丁": 9, 15 | "己": 9, 16 | "辛": 0, 17 | "癸": 3 18 | ] 19 | 20 | public var sect: Int 21 | private var _lunar: Lunar 22 | 23 | public var lunar: Lunar { 24 | get { 25 | _lunar 26 | } 27 | } 28 | 29 | public init(lunar: Lunar) { 30 | sect = 2 31 | _lunar = lunar 32 | } 33 | 34 | public class func fromLunar(lunar: Lunar) -> EightChar { 35 | EightChar(lunar: lunar) 36 | } 37 | 38 | public var year: String { 39 | get { 40 | _lunar.yearInGanZhiExact 41 | } 42 | } 43 | 44 | public var yearGan: String { 45 | get { 46 | _lunar.yearGanExact 47 | } 48 | } 49 | 50 | public var yearZhi: String { 51 | get { 52 | _lunar.yearZhiExact 53 | } 54 | } 55 | 56 | public var yearHideGan: [String] { 57 | get { 58 | LunarUtil.ZHI_HIDE_GAN[yearZhi]! 59 | } 60 | } 61 | 62 | public var yearWuXing: String { 63 | get { 64 | LunarUtil.WU_XING_GAN[yearGan]! + LunarUtil.WU_XING_ZHI[yearZhi]! 65 | } 66 | } 67 | 68 | public var yearNaYin: String { 69 | get { 70 | LunarUtil.NAYIN[year]! 71 | } 72 | } 73 | 74 | public var month: String { 75 | get { 76 | _lunar.monthInGanZhiExact 77 | } 78 | } 79 | 80 | public var monthGan: String { 81 | get { 82 | _lunar.monthGanExact 83 | } 84 | } 85 | 86 | public var monthZhi: String { 87 | get { 88 | _lunar.monthZhiExact 89 | } 90 | } 91 | 92 | public var monthHideGan: [String] { 93 | get { 94 | LunarUtil.ZHI_HIDE_GAN[monthZhi]! 95 | } 96 | } 97 | 98 | public var monthWuXing: String { 99 | get { 100 | LunarUtil.WU_XING_GAN[monthGan]! + LunarUtil.WU_XING_ZHI[monthZhi]! 101 | } 102 | } 103 | 104 | public var monthNaYin: String { 105 | get { 106 | LunarUtil.NAYIN[month]! 107 | } 108 | } 109 | 110 | public var day: String { 111 | get { 112 | 2 == sect ? lunar.dayInGanZhiExact2 : lunar.dayInGanZhiExact 113 | } 114 | } 115 | 116 | public var dayGan: String { 117 | get { 118 | 2 == sect ? _lunar.dayGanExact2 : lunar.dayGanExact 119 | } 120 | } 121 | 122 | public var dayGanIndex: Int { 123 | get { 124 | 2 == sect ? _lunar.dayGanIndexExact2 : lunar.dayGanIndexExact 125 | } 126 | } 127 | 128 | public var dayZhi: String { 129 | get { 130 | 2 == sect ? _lunar.dayZhiExact2 : lunar.dayZhiExact 131 | } 132 | } 133 | 134 | public var dayZhiIndex: Int { 135 | get { 136 | 2 == sect ? _lunar.dayZhiIndexExact2 : lunar.dayZhiIndexExact 137 | } 138 | } 139 | 140 | public var dayHideGan: [String] { 141 | get { 142 | LunarUtil.ZHI_HIDE_GAN[dayZhi]! 143 | } 144 | } 145 | 146 | public var dayWuXing: String { 147 | get { 148 | LunarUtil.WU_XING_GAN[dayGan]! + LunarUtil.WU_XING_ZHI[dayZhi]! 149 | } 150 | } 151 | 152 | public var dayNaYin: String { 153 | get { 154 | LunarUtil.NAYIN[day]! 155 | } 156 | } 157 | 158 | public var time: String { 159 | get { 160 | _lunar.timeInGanZhi 161 | } 162 | } 163 | 164 | public var timeGan: String { 165 | get { 166 | _lunar.timeGan 167 | } 168 | } 169 | 170 | public var timeZhi: String { 171 | get { 172 | _lunar.timeZhi 173 | } 174 | } 175 | 176 | public var timeHideGan: [String] { 177 | get { 178 | LunarUtil.ZHI_HIDE_GAN[timeZhi]! 179 | } 180 | } 181 | 182 | public var timeWuXing: String { 183 | get { 184 | LunarUtil.WU_XING_GAN[timeGan]! + LunarUtil.WU_XING_ZHI[timeZhi]! 185 | } 186 | } 187 | 188 | public var timeNaYin: String { 189 | get { 190 | LunarUtil.NAYIN[time]! 191 | } 192 | } 193 | 194 | public func getYun(gender: Int, sect: Int = 1) -> Yun { 195 | Yun(eightChar: self, gender: gender, sect: sect) 196 | } 197 | 198 | public var yearXun: String { 199 | get { 200 | _lunar.yearXunExact 201 | } 202 | } 203 | 204 | public var yearXunKong: String { 205 | get { 206 | _lunar.yearXunKongExact 207 | } 208 | } 209 | 210 | public var monthXun: String { 211 | get { 212 | _lunar.monthXunExact 213 | } 214 | } 215 | 216 | public var monthXunKong: String { 217 | get { 218 | _lunar.monthXunKongExact 219 | } 220 | } 221 | 222 | public var dayXun: String { 223 | get { 224 | 2 == sect ? _lunar.dayXunExact2 : _lunar.dayXunExact 225 | } 226 | } 227 | 228 | public var dayXunKong: String { 229 | get { 230 | 2 == sect ? _lunar.dayXunKongExact2 : _lunar.dayXunKongExact 231 | } 232 | } 233 | 234 | public var timeXun: String { 235 | get { 236 | _lunar.timeXun 237 | } 238 | } 239 | 240 | public var timeXunKong: String { 241 | get { 242 | _lunar.timeXunKong 243 | } 244 | } 245 | 246 | private func getDiShi(zhiIndex: Int) -> String { 247 | var index = EightChar.CHANG_SHENG_OFFSET[dayGan]! + (dayGanIndex % 2 == 0 ? zhiIndex : -zhiIndex) 248 | if index >= 12 { 249 | index -= 12 250 | } 251 | if index < 0 { 252 | index += 12 253 | } 254 | return EightChar.CHANG_SHENG[index] 255 | } 256 | 257 | public var yearDiShi: String { 258 | get { 259 | getDiShi(zhiIndex: _lunar.yearZhiIndexExact) 260 | } 261 | } 262 | 263 | public var monthDiShi: String { 264 | get { 265 | getDiShi(zhiIndex: _lunar.monthZhiIndexExact) 266 | } 267 | } 268 | 269 | public var dayDiShi: String { 270 | get { 271 | getDiShi(zhiIndex: _lunar.dayZhiIndex) 272 | } 273 | } 274 | 275 | public var timeDiShi: String { 276 | get { 277 | getDiShi(zhiIndex: _lunar.timeZhiIndex) 278 | } 279 | } 280 | 281 | private func getShiShenZhi(zhi: String) -> [String] { 282 | let hideGan = LunarUtil.ZHI_HIDE_GAN[zhi]! 283 | var l = [String]() 284 | for gan in hideGan { 285 | l.append(LunarUtil.SHI_SHEN[dayGan + gan]!) 286 | } 287 | return l 288 | } 289 | 290 | public var yearShiShenGan: String { 291 | get { 292 | LunarUtil.SHI_SHEN[dayGan + yearGan]! 293 | } 294 | } 295 | 296 | public var monthShiShenGan: String { 297 | get { 298 | LunarUtil.SHI_SHEN[dayGan + monthGan]! 299 | } 300 | } 301 | 302 | public var dayShiShenGan: String { 303 | get { 304 | "日主" 305 | } 306 | } 307 | 308 | public var timeShiShenGan: String { 309 | get { 310 | LunarUtil.SHI_SHEN[dayGan + timeGan]! 311 | } 312 | } 313 | 314 | public var yearShiShenZhi: [String] { 315 | get { 316 | getShiShenZhi(zhi: yearZhi) 317 | } 318 | } 319 | 320 | public var monthShiShenZhi: [String] { 321 | get { 322 | getShiShenZhi(zhi: monthZhi) 323 | } 324 | } 325 | 326 | public var dayShiShenZhi: [String] { 327 | get { 328 | getShiShenZhi(zhi: dayZhi) 329 | } 330 | } 331 | 332 | public var timeShiShenZhi: [String] { 333 | get { 334 | getShiShenZhi(zhi: timeZhi) 335 | } 336 | } 337 | 338 | public var taiYuan: String { 339 | get { 340 | var ganIndex = _lunar.monthGanIndexExact + 1 341 | if ganIndex >= 10 { 342 | ganIndex -= 10 343 | } 344 | var zhiIndex = _lunar.monthZhiIndexExact + 3 345 | if zhiIndex >= 12 { 346 | zhiIndex -= 12 347 | } 348 | return LunarUtil.GAN[ganIndex + 1] + LunarUtil.ZHI[zhiIndex + 1] 349 | } 350 | } 351 | 352 | public var taiYuanNaYin: String { 353 | get { 354 | LunarUtil.NAYIN[taiYuan]! 355 | } 356 | } 357 | 358 | public var taiXi: String { 359 | get { 360 | let ganIndex = (2 == sect) ? lunar.dayGanIndexExact2 : lunar.dayGanIndexExact 361 | let zhiIndex = (2 == sect) ? lunar.dayZhiIndexExact2 : lunar.dayZhiIndexExact 362 | return LunarUtil.HE_GAN_5[ganIndex] + LunarUtil.HE_ZHI_6[zhiIndex] 363 | } 364 | } 365 | 366 | public var taiXiNaYin: String { 367 | get { 368 | LunarUtil.NAYIN[taiXi]! 369 | } 370 | } 371 | 372 | public var mingGong: String { 373 | get { 374 | var monthZhiIndex = 0 375 | var timeZhiIndex = 0 376 | for i in (0..= 14 ? 26 : 14) - offset 391 | var ganIndex = (_lunar.yearGanIndexExact + 1) * 2 + offset 392 | while (ganIndex > 10) { 393 | ganIndex -= 10 394 | } 395 | return LunarUtil.GAN[ganIndex] + EightChar.MONTH_ZHI[offset] 396 | } 397 | } 398 | 399 | public var mingGongNaYin: String { 400 | get { 401 | LunarUtil.NAYIN[mingGong]! 402 | } 403 | } 404 | 405 | public var shenGong: String { 406 | get { 407 | var monthZhiIndex = 0 408 | var timeZhiIndex = 0 409 | for i in (0.. 12 { 424 | offset -= 12 425 | } 426 | var ganIndex = (_lunar.yearGanIndexExact + 1) * 2 + offset 427 | while (ganIndex > 10) { 428 | ganIndex -= 10 429 | } 430 | return LunarUtil.GAN[ganIndex] + EightChar.MONTH_ZHI[offset] 431 | } 432 | } 433 | 434 | public var shenGongNaYin: String { 435 | get { 436 | LunarUtil.NAYIN[shenGong]! 437 | } 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /Sources/LunarSwift/Solar.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class Solar: NSObject { 5 | public static var J2000: Double = 2451545 6 | 7 | private var _year: Int 8 | private var _month: Int 9 | private var _day: Int 10 | private var _hour: Int 11 | private var _minute: Int 12 | private var _second: Int 13 | 14 | public var year: Int { 15 | get { 16 | _year 17 | } 18 | } 19 | 20 | public var month: Int { 21 | get { 22 | _month 23 | } 24 | } 25 | 26 | public var day: Int { 27 | get { 28 | _day 29 | } 30 | } 31 | 32 | public var hour: Int { 33 | get { 34 | _hour 35 | } 36 | } 37 | 38 | public var minute: Int { 39 | get { 40 | _minute 41 | } 42 | } 43 | 44 | public var second: Int { 45 | get { 46 | _second 47 | } 48 | } 49 | 50 | public init(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) { 51 | if 1582 == year && 10 == month { 52 | if day > 4 && day < 15 { 53 | fatalError("wrong solar year \(year) month \(month) day \(day)") 54 | } 55 | } 56 | if month < 1 || month > 12 { 57 | fatalError("wrong month \(month)") 58 | } 59 | if day < 1 || day > 31 { 60 | fatalError("wrong day \(day)") 61 | } 62 | if hour < 0 || hour > 23 { 63 | fatalError("wrong hour \(hour)") 64 | } 65 | if minute < 0 || minute > 59 { 66 | fatalError("wrong minute \(minute)") 67 | } 68 | if second < 0 || second > 59 { 69 | fatalError("wrong second \(second)") 70 | } 71 | _year = year 72 | _month = month 73 | _day = day 74 | _hour = hour 75 | _minute = minute 76 | _second = second 77 | } 78 | 79 | public convenience init(date: Date) { 80 | let calendar = Calendar.current 81 | self.init(year: calendar.component(.year, from: date), month: calendar.component(.month, from: date), day: calendar.component(.day, from: date), hour: calendar.component(.hour, from: date), minute: calendar.component(.minute, from: date), second: calendar.component(.second, from: date)) 82 | } 83 | 84 | public convenience override init() { 85 | self.init(date: Date()) 86 | } 87 | 88 | public class func fromYmdHms(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) -> Solar { 89 | Solar(year: year, month: month, day: day, hour: hour, minute: minute, second: second) 90 | } 91 | 92 | public class func fromDate(date: Date) -> Solar { 93 | Solar(date: date) 94 | } 95 | 96 | public class func fromJulianDay(julianDay: Double) -> Solar { 97 | var d = Int(julianDay + 0.5) 98 | var f = julianDay + 0.5 - Double(d) 99 | 100 | if d >= 2299161 101 | { 102 | let c = Int((Double(d) - 1867216.25) / 36524.25) 103 | d += 1 + c - Int(Double(c) / 4) 104 | } 105 | d += 1524 106 | var year = Int((Double(d) - 122.1) / 365.25) 107 | d -= Int(365.25 * Double(year)) 108 | var month = Int(Double(d) / 30.601) 109 | d -= Int(30.601 * Double(month)) 110 | var day = d 111 | if month > 13 112 | { 113 | month -= 13 114 | year -= 4715 115 | } 116 | else 117 | { 118 | month -= 1 119 | year -= 4716 120 | } 121 | f *= 24 122 | var hour = Int(f) 123 | 124 | f -= Double(hour) 125 | f *= 60 126 | var minute = Int(f) 127 | 128 | f -= Double(minute) 129 | f *= 60 130 | var second = Int(round(f)) 131 | 132 | if second > 59 133 | { 134 | second -= 60 135 | minute += 1 136 | } 137 | if minute > 59 138 | { 139 | minute -= 60 140 | hour += 1 141 | } 142 | if hour > 23 { 143 | hour -= 24 144 | day += 1 145 | } 146 | return Solar(year: year, month: month, day: day, hour: hour, minute: minute, second: second) 147 | } 148 | 149 | public class func fromBaZi(yearGanZhi: String, monthGanZhi: String, dayGanZhi: String, timeGanZhi: String, sect: Int = 2, baseYear: Int = 1900) -> [Solar] { 150 | let sec = (1 == sect) ? 1 : 2 151 | var l = [Solar]() 152 | // 月地支距寅月的偏移值 153 | var m = LunarUtil.find(name: String(monthGanZhi.suffix(1)), names: LunarUtil.ZHI, offset: -1) - 2 154 | if m < 0 { 155 | m += 12 156 | } 157 | // 月天干要一致 158 | if (((LunarUtil.find(name: String(yearGanZhi.prefix(1)), names: LunarUtil.GAN, offset: -1) + 1) * 2 + m) % 10 != LunarUtil.find(name: String(monthGanZhi.prefix(1)), names: LunarUtil.GAN, offset: -1)) { 159 | return l 160 | } 161 | // 1年的立春是辛酉,序号57 162 | var y = LunarUtil.getJiaZiIndex(ganZhi: yearGanZhi) - 57 163 | if y < 0 { 164 | y += 60 165 | } 166 | y += 1 167 | // 节令偏移值 168 | m *= 2 169 | // 时辰地支转时刻,子时按零点算 170 | let h = LunarUtil.find(name: String(timeGanZhi.suffix(1)), names: LunarUtil.ZHI, offset: -1) * 2 171 | var hours = [Int]() 172 | hours.append(h) 173 | if 0 == h && 2 == sec { 174 | hours.append(23) 175 | } 176 | let startYear = baseYear - 1 177 | 178 | // 结束年 179 | let endYear = Calendar.current.component(.year, from: Date()) 180 | 181 | while y <= endYear { 182 | if y >= startYear { 183 | // 立春为寅月的开始 184 | let jieQiTable = Lunar.fromYmdHms(lunarYear: y, lunarMonth: 1, lunarDay: 1).jieQiTable 185 | // 节令推移,年干支和月干支就都匹配上了 186 | var solarTime = jieQiTable[Lunar.JIE_QI_IN_USE[4 + m]]! 187 | if solarTime.year >= baseYear { 188 | // 日干支和节令干支的偏移值 189 | var d = LunarUtil.getJiaZiIndex(ganZhi: dayGanZhi) - LunarUtil.getJiaZiIndex(ganZhi: solarTime.lunar.dayInGanZhiExact2) 190 | if d < 0 { 191 | d += 60 192 | } 193 | if d > 0 { 194 | // 从节令推移天数 195 | solarTime = solarTime.next(days: d) 196 | } 197 | for hour in hours { 198 | var mi = 0 199 | var s = 0 200 | if d == 0 && hour == solarTime.hour { 201 | // 如果正好是节令当天,且小时和节令的小时数相等的极端情况,把分钟和秒钟带上 202 | mi = solarTime.minute 203 | s = solarTime.second 204 | } 205 | // 验证一下 206 | var solar = Solar.fromYmdHms(year: solarTime.year, month: solarTime.month, day: solarTime.day, hour: hour, minute: mi, second: s) 207 | if d == 30 { 208 | solar = solar.nextHour(hours: -1) 209 | } 210 | let lunar = solar.lunar 211 | let dgz = (2 == sect) ? lunar.dayInGanZhiExact2 : lunar.dayInGanZhiExact 212 | if lunar.yearInGanZhiExact == yearGanZhi && lunar.monthInGanZhiExact == monthGanZhi && dgz == dayGanZhi && lunar.timeInGanZhi == timeGanZhi 213 | { 214 | l.append(solar) 215 | } 216 | } 217 | } 218 | } 219 | y += 60 220 | } 221 | return l 222 | } 223 | 224 | public var leapYear: Bool { 225 | get { 226 | SolarUtil.isLeapYear(year: _year) 227 | } 228 | } 229 | 230 | public var week: Int { 231 | get { 232 | (Int(julianDay + 0.5) + 7000001) % 7 233 | } 234 | } 235 | 236 | public var weekInChinese: String { 237 | get { 238 | SolarUtil.WEEK[week] 239 | } 240 | } 241 | 242 | public var xingZuo: String { 243 | get { 244 | var index = 11 245 | let y = _month * 100 + _day 246 | if y >= 321 && y <= 419 { 247 | index = 0 248 | } else if y >= 420 && y <= 520 { 249 | index = 1 250 | } else if y >= 521 && y <= 621 { 251 | index = 2 252 | } else if y >= 622 && y <= 722 { 253 | index = 3 254 | } else if y >= 723 && y <= 822 { 255 | index = 4 256 | } else if y >= 823 && y <= 922 { 257 | index = 5 258 | } else if y >= 923 && y <= 1023 { 259 | index = 6 260 | } else if y >= 1024 && y <= 1122 { 261 | index = 7 262 | } else if y >= 1123 && y <= 1221 { 263 | index = 8 264 | } else if y >= 1222 || y <= 119 { 265 | index = 9 266 | } else if y <= 218 { 267 | index = 10 268 | } 269 | return SolarUtil.XING_ZUO[index] 270 | } 271 | } 272 | 273 | public var festivals: [String] { 274 | get { 275 | var l = [String]() 276 | //获取几月几日对应的节日 277 | var f = SolarUtil.FESTIVAL["\(_month)-\(_day)"] 278 | if nil != f { 279 | l.append(f!) 280 | } 281 | //计算几月第几个星期几对应的节日 282 | let weeks = Int(ceil(Double(_day) / 7)) 283 | let key = "\(_month)-\(weeks)-\(week)" 284 | //星期几,0代表星期天 285 | f = SolarUtil.WEEK_FESTIVAL[key] 286 | if nil != f { 287 | l.append(f!) 288 | } 289 | if _day + 7 > SolarUtil.getDaysOfMonth(year: _year, month: _month) { 290 | f = SolarUtil.WEEK_FESTIVAL["\(_month)-0-\(week)"] 291 | if nil != f { 292 | l.append(f!) 293 | } 294 | } 295 | return l 296 | } 297 | } 298 | 299 | public var otherFestivals: [String] { 300 | get { 301 | var l = [String]() 302 | //获取几月几日对应的节日 303 | let fs = SolarUtil.OTHER_FESTIVAL["\(_month)-\(_day)"] 304 | if nil != fs { 305 | for f in fs! { 306 | l.append(f) 307 | } 308 | } 309 | return l 310 | } 311 | } 312 | 313 | public var lunar: Lunar { 314 | get { 315 | Lunar.fromSolar(solar: self) 316 | } 317 | } 318 | 319 | public var julianDay: Double { 320 | get { 321 | var y: Int = _year 322 | var m: Int = _month 323 | let d: Double = Double(_day) + ((Double(_second) / Double(60) + Double(_minute)) / Double(60) + Double(_hour)) / Double(24) 324 | var n: Int = 0 325 | let g: Bool = y * 372 + m * 31 + Int(d) >= 588829 326 | if m <= 2 { 327 | m += 12 328 | y -= 1 329 | } 330 | if g { 331 | n = Int(Double(y) / Double(100)) 332 | n = 2 - n + Int(Double(n) / Double(4)) 333 | } 334 | return Double(Int(Double(365.25) * Double(y + 4716))) + Double(Int(Double(30.6001) * Double(m + 1))) + d + Double(n) - Double(1524.5) 335 | } 336 | } 337 | 338 | public func isBefore(solar: Solar) -> Bool { 339 | SolarUtil.isBefore(ay: _year, am: _month, ad: _day, ah: _hour, ai: _minute, ac: _second, by: solar.year, bm: solar.month, bd: solar.day, bh: solar.hour, bi: solar.minute, bc: solar.second) 340 | } 341 | 342 | public func isAfter(solar: Solar) -> Bool { 343 | SolarUtil.isAfter(ay: _year, am: _month, ad: _day, ah: _hour, ai: _minute, ac: _second, by: solar.year, bm: solar.month, bd: solar.day, bh: solar.hour, bi: solar.minute, bc: solar.second) 344 | } 345 | 346 | public func subtract(solar: Solar) -> Int { 347 | SolarUtil.getDaysBetween(ay: solar.year, am: solar.month, ad: solar.day, by: _year, bm: _month, bd: _day) 348 | } 349 | 350 | public func subtractMinute(solar: Solar) -> Int { 351 | var days = subtract(solar: solar) 352 | let cm = _hour * 60 + _minute 353 | let sm = solar.hour * 60 + solar.minute 354 | var m = cm - sm 355 | if m < 0 { 356 | m += 1440 357 | days -= 1 358 | } 359 | m += days * 1440 360 | return m 361 | } 362 | 363 | public func nextYear(years: Int) -> Solar { 364 | let y = _year + years 365 | var d = _day 366 | if 1582 == y && 10 == _month { 367 | if d > 4 && d < 15 { 368 | d += 10 369 | } 370 | } else if 2 == _month { 371 | if d > 28 { 372 | if !SolarUtil.isLeapYear(year: y) { 373 | d = 28 374 | } 375 | } 376 | } 377 | return Solar.fromYmdHms(year: y, month: _month, day: d, hour: _hour, minute: _minute, second: _second) 378 | } 379 | 380 | public func nextMonth(months: Int) -> Solar { 381 | let month = SolarMonth.fromYm(year: _year, month: _month).next(months: months) 382 | let y = month.year 383 | let m = month.month 384 | var d = _day 385 | if 1582 == y && 10 == m { 386 | if d > 4 && d < 15 { 387 | d += 10 388 | } 389 | } else { 390 | let maxDay = SolarUtil.getDaysOfMonth(year: y, month: m) 391 | if d > maxDay { 392 | d = maxDay 393 | } 394 | } 395 | return Solar.fromYmdHms(year: y, month: m, day: d, hour: _hour, minute: _minute, second: _second) 396 | } 397 | 398 | public func nextDay(days: Int) -> Solar { 399 | var y = _year 400 | var m = _month 401 | var d = _day 402 | if 1582 == y && 10 == m { 403 | if d > 4 { 404 | d -= 10 405 | } 406 | } 407 | if days > 0 { 408 | d += days 409 | var daysInMonth = SolarUtil.getDaysOfMonth(year: y, month: m) 410 | while d > daysInMonth { 411 | d -= daysInMonth 412 | m += 1 413 | if m > 12 { 414 | m = 1 415 | y += 1 416 | } 417 | daysInMonth = SolarUtil.getDaysOfMonth(year: y, month: m) 418 | } 419 | } else if days < 0 { 420 | while d+days <= 0 { 421 | m -= 1 422 | if m < 1 { 423 | m = 12 424 | y -= 1 425 | } 426 | d += SolarUtil.getDaysOfMonth(year: y, month: m) 427 | } 428 | d += days 429 | } 430 | if 1582 == y && 10 == m { 431 | if d > 4 { 432 | d += 10 433 | } 434 | } 435 | return Solar.fromYmdHms(year: y, month: m, day: d, hour: _hour, minute: _minute, second: _second) 436 | } 437 | 438 | public func next(days: Int, onlyWorkday: Bool = false) -> Solar { 439 | if !onlyWorkday { 440 | return nextDay(days: days) 441 | } 442 | var solar = Solar.fromYmdHms(year: _year, month: _month, day: _day, hour: _hour, minute: _minute, second: _second) 443 | if days != 0 { 444 | var rest = days 445 | var add = 1 446 | if days < 0 { 447 | rest = -days 448 | add = -1 449 | } 450 | while rest > 0 { 451 | solar = solar.nextDay(days: add) 452 | var work = true 453 | let holiday = HolidayUtil.getHolidayByYmd(year: solar.year, month: solar.month, day: solar.day) 454 | if nil == holiday { 455 | let week = solar.week 456 | if 0 == week || 6 == week { 457 | work = false 458 | } 459 | } else { 460 | work = holiday!.work 461 | } 462 | if work { 463 | rest -= 1 464 | } 465 | } 466 | } 467 | return solar 468 | } 469 | 470 | public func nextHour(hours: Int) -> Solar { 471 | let h = _hour + hours 472 | var n = 1 473 | var hour = h 474 | if h < 0 { 475 | n = -1 476 | hour = -h 477 | } 478 | var days = hour / 24 * n 479 | hour = (hour % 24) * n 480 | if hour < 0 { 481 | hour += 24 482 | days -= 1 483 | } 484 | let o = next(days: days) 485 | return Solar.fromYmdHms(year: o.year, month: o.month, day: o.day, hour: hour, minute: o.minute, second: o.second) 486 | } 487 | 488 | public func getSalaryRate() -> Int { 489 | // 元旦节 490 | if _month == 1 && _day == 1 { 491 | return 3 492 | } 493 | // 劳动节 494 | if _month == 5 && _day == 1 { 495 | return 3 496 | } 497 | // 国庆 498 | if _month == 10 && _day >= 1 && _day <= 3 { 499 | return 3 500 | } 501 | // 春节 502 | if lunar.month == 1 && lunar.day >= 1 && lunar.day <= 3 { 503 | return 3 504 | } 505 | // 端午 506 | if lunar.month == 5 && lunar.day == 5 { 507 | return 3 508 | } 509 | // 中秋 510 | if lunar.month == 8 && lunar.day == 15 { 511 | return 3 512 | } 513 | // 清明 514 | if "清明" == lunar.jieQi { 515 | return 3 516 | } 517 | let holiday = HolidayUtil.getHolidayByYmd(year: _year, month: _month, day: _day) 518 | if holiday != nil { 519 | // 法定假日非上班 520 | if !holiday!.work { 521 | return 2 522 | } 523 | } else { 524 | // 周末 525 | if week == 6 || week == 0 { 526 | return 2 527 | } 528 | } 529 | // 工作日 530 | return 1 531 | } 532 | 533 | public var ymd: String { 534 | get { 535 | String(format: "%04d-%02d-%02d", _year, _month, _day) 536 | } 537 | } 538 | 539 | public var ymdhms: String { 540 | get { 541 | String(format: "%04d-%02d-%02d %02d:%02d:%02d", _year, _month, _day, _hour, _minute, _second) 542 | } 543 | } 544 | 545 | public override var description: String { 546 | get { 547 | "\(ymd)" 548 | } 549 | } 550 | 551 | public var fullString: String { 552 | get { 553 | var s = "\(ymdhms)" 554 | if leapYear { 555 | s += " 闰年" 556 | } 557 | s += " 星期\(weekInChinese)" 558 | for i in festivals { 559 | s += " (\(i))" 560 | } 561 | for i in otherFestivals { 562 | s += " (\(i))" 563 | } 564 | s += " \(xingZuo)座" 565 | return s 566 | } 567 | } 568 | 569 | } 570 | -------------------------------------------------------------------------------- /Sources/LunarSwift/HolidayUtil.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objcMembers 4 | public class HolidayUtil { 5 | 6 | private static let SIZE: Int = 18 7 | private static let ZERO: Int = 48 8 | private static let TAG_REMOVE: String = "~" 9 | public static let NAMES: [String] = ["元旦节", "春节", "清明节", "劳动节", "端午节", "中秋节", "国庆节", "国庆中秋", "抗战胜利日"] 10 | private static let DATA: String = "200112290020020101200112300020020101200201010120020101200201020120020101200201030120020101200202091020020212200202101020020212200202121120020212200202131120020212200202141120020212200202151120020212200202161120020212200202171120020212200202181120020212200204273020020501200204283020020501200205013120020501200205023120020501200205033120020501200205043120020501200205053120020501200205063120020501200205073120020501200209286020021001200209296020021001200210016120021001200210026120021001200210036120021001200210046120021001200210056120021001200210066120021001200210076120021001200301010120030101200302011120030201200302021120030201200302031120030201200302041120030201200302051120030201200302061120030201200302071120030201200302081020030201200302091020030201200304263020030501200304273020030501200305013120030501200305023120030501200305033120030501200305043120030501200305053120030501200305063120030501200305073120030501200309276020031001200309286020031001200310016120031001200310026120031001200310036120031001200310046120031001200310056120031001200310066120031001200310076120031001200401010120040101200401171020040122200401181020040122200401221120040122200401231120040122200401241120040122200401251120040122200401261120040122200401271120040122200401281120040122200405013120040501200405023120040501200405033120040501200405043120040501200405053120040501200405063120040501200405073120040501200405083020040501200405093020040501200410016120041001200410026120041001200410036120041001200410046120041001200410056120041001200410066120041001200410076120041001200410096020041001200410106020041001200501010120050101200501020120050101200501030120050101200502051020050209200502061020050209200502091120050209200502101120050209200502111120050209200502121120050209200502131120050209200502141120050209200502151120050209200504303020050501200505013120050501200505023120050501200505033120050501200505043120050501200505053120050501200505063120050501200505073120050501200505083020050501200510016120051001200510026120051001200510036120051001200510046120051001200510056120051001200510066120051001200510076120051001200510086020051001200510096020051001200512310020060101200601010120060101200601020120060101200601030120060101200601281020060129200601291120060129200601301120060129200601311120060129200602011120060129200602021120060129200602031120060129200602041120060129200602051020060129200604293020060501200604303020060501200605013120060501200605023120060501200605033120060501200605043120060501200605053120060501200605063120060501200605073120060501200609306020061001200610016120061001200610026120061001200610036120061001200610046120061001200610056120061001200610066120061001200610076120061001200610086020061001200612300020070101200612310020070101200701010120070101200701020120070101200701030120070101200702171020070218200702181120070218200702191120070218200702201120070218200702211120070218200702221120070218200702231120070218200702241120070218200702251020070218200704283020070501200704293020070501200705013120070501200705023120070501200705033120070501200705043120070501200705053120070501200705063120070501200705073120070501200709296020071001200709306020071001200710016120071001200710026120071001200710036120071001200710046120071001200710056120071001200710066120071001200710076120071001200712290020080101200712300120080101200712310120080101200801010120080101200802021020080206200802031020080206200802061120080206200802071120080206200802081120080206200802091120080206200802101120080206200802111120080206200802121120080206200804042120080404200804052120080404200804062120080404200805013120080501200805023120080501200805033120080501200805043020080501200806074120080608200806084120080608200806094120080608200809135120080914200809145120080914200809155120080914200809276020081001200809286020081001200809296120081001200809306120081001200810016120081001200810026120081001200810036120081001200810046120081001200810056120081001200901010120090101200901020120090101200901030120090101200901040020090101200901241020090125200901251120090125200901261120090125200901271120090125200901281120090125200901291120090125200901301120090125200901311120090125200902011020090125200904042120090404200904052120090404200904062120090404200905013120090501200905023120090501200905033120090501200905284120090528200905294120090528200905304120090528200905314020090528200909276020091001200910016120091001200910026120091001200910036120091001200910046120091001200910055120091003200910065120091003200910075120091003200910085120091003200910105020091003201001010120100101201001020120100101201001030120100101201002131120100213201002141120100213201002151120100213201002161120100213201002171120100213201002181120100213201002191120100213201002201020100213201002211020100213201004032120100405201004042120100405201004052120100405201005013120100501201005023120100501201005033120100501201006124020100616201006134020100616201006144120100616201006154120100616201006164120100616201009195020100922201009225120100922201009235120100922201009245120100922201009255020100922201009266020101001201010016120101001201010026120101001201010036120101001201010046120101001201010056120101001201010066120101001201010076120101001201010096020101001201101010120110101201101020120110101201101030120110101201101301020110203201102021120110203201102031120110203201102041120110203201102051120110203201102061120110203201102071120110203201102081120110203201102121020110203201104022020110405201104032120110405201104042120110405201104052120110405201104303120110501201105013120110501201105023120110501201106044120110606201106054120110606201106064120110606201109105120110912201109115120110912201109125120110912201110016120111001201110026120111001201110036120111001201110046120111001201110056120111001201110066120111001201110076120111001201110086020111001201110096020111001201112310020120101201201010120120101201201020120120101201201030120120101201201211020120123201201221120120123201201231120120123201201241120120123201201251120120123201201261120120123201201271120120123201201281120120123201201291020120123201203312020120404201204012020120404201204022120120404201204032120120404201204042120120404201204283020120501201204293120120501201204303120120501201205013120120501201205023020120501201206224120120623201206234120120623201206244120120623201209295020120930201209305120120930201210016120121001201210026120121001201210036120121001201210046120121001201210056120121001201210066120121001201210076120121001201210086020121001201301010120130101201301020120130101201301030120130101201301050020130101201301060020130101201302091120130210201302101120130210201302111120130210201302121120130210201302131120130210201302141120130210201302151120130210201302161020130210201302171020130210201304042120130404201304052120130404201304062120130404201304273020130501201304283020130501201304293120130501201304303120130501201305013120130501201306084020130612201306094020130612201306104120130612201306114120130612201306124120130612201309195120130919201309205120130919201309215120130919201309225020130919201309296020131001201310016120131001201310026120131001201310036120131001201310046120131001201310056120131001201310066120131001201310076120131001201401010120140101201401261020140131201401311120140131201402011120140131201402021120140131201402031120140131201402041120140131201402051120140131201402061120140131201402081020140131201404052120140405201404062120140405201404072120140405201405013120140501201405023120140501201405033120140501201405043020140501201405314120140602201406014120140602201406024120140602201409065120140908201409075120140908201409085120140908201409286020141001201410016120141001201410026120141001201410036120141001201410046120141004201410056120141001201410066120141001201410076120141001201410116020141001201501010120150101201501020120150101201501030120150101201501040020150101201502151020150219201502181120150219201502191120150219201502201120150219201502211120150219201502221120150219201502231120150219201502241120150219201502281020150219201504042120150405201504052120150405201504062120150405201505013120150501201505023120150501201505033120150501201506204120150620201506214120150620201506224120150620201509038120150903201509048120150903201509058120150903201509068020150903201509265120150927201509275120150927201510016120151001201510026120151001201510036120151001201510046120151004201510056120151001201510066120151001201510076120151001201510106020151001201601010120160101201601020120160101201601030120160101201602061020160208201602071120160208201602081120160208201602091120160208201602101120160208201602111120160208201602121120160208201602131120160208201602141020160208201604022120160404201604032120160404201604042120160404201604303120160501201605013120160501201605023120160501201606094120160609201606104120160609201606114120160609201606124020160609201609155120160915201609165120160915201609175120160915201609185020160915201610016120161001201610026120161001201610036120161001201610046120161001201610056120161001201610066120161001201610076120161001201610086020161001201610096020161001201612310120170101201701010120170101201701020120170101201701221020170128201701271120170128201701281120170128201701291120170128201701301120170128201701311120170128201702011120170128201702021120170128201702041020170128201704012020170404201704022120170404201704032120170404201704042120170404201704293120170501201704303120170501201705013120170501201705274020170530201705284120170530201705294120170530201705304120170530201709306020171001201710016120171001201710026120171001201710036120171001201710045120171004201710056120171001201710066120171001201710076120171001201710086120171001201712300120180101201712310120180101201801010120180101201802111020180216201802151120180216201802161120180216201802171120180216201802181120180216201802191120180216201802201120180216201802211120180216201802241020180216201804052120180405201804062120180405201804072120180405201804082020180405201804283020180501201804293120180501201804303120180501201805013120180501201806164120180618201806174120180618201806184120180618201809225120180924201809235120180924201809245120180924201809296020181001201809306020181001201810016120181001201810026120181001201810036120181001201810046120181001201810056120181001201810066120181001201810076120181001201812290020190101201812300120190101201812310120190101201901010120190101201902021020190205201902031020190205201902041120190205201902051120190205201902061120190205201902071120190205201902081120190205201902091120190205201902101120190205201904052120190405201904062120190405201904072120190405201904283020190501201905013120190501201905023120190501201905033120190501201905043120190501201905053020190501201906074120190607201906084120190607201906094120190607201909135120190913201909145120190913201909155120190913201909296020191001201910016120191001201910026120191001201910036120191001201910046120191001201910056120191001201910066120191001201910076120191001201910126020191001202001010120200101202001191020200125202001241120200125202001251120200125202001261120200125202001271120200125202001281120200125202001291120200125202001301120200125202001311120200125202002011120200125202002021120200125202004042120200404202004052120200404202004062120200404202004263020200501202005013120200501202005023120200501202005033120200501202005043120200501202005053120200501202005093020200501202006254120200625202006264120200625202006274120200625202006284020200625202009277020201001202010017120201001202010026120201001202010036120201001202010046120201001202010056120201001202010066120201001202010076120201001202010086120201001202010106020201001202101010120210101202101020120210101202101030120210101202102071020210212202102111120210212202102121120210212202102131120210212202102141120210212202102151120210212202102161120210212202102171120210212202102201020210212202104032120210404202104042120210404202104052120210404202104253020210501202105013120210501202105023120210501202105033120210501202105043120210501202105053120210501202105083020210501202106124120210614202106134120210614202106144120210614202109185020210921202109195120210921202109205120210921202109215120210921202109266020211001202110016120211001202110026120211001202110036120211001202110046120211001202110056120211001202110066120211001202110076120211001202110096020211001202201010120220101202201020120220101202201030120220101202201291020220201202201301020220201202201311120220201202202011120220201202202021120220201202202031120220201202202041120220201202202051120220201202202061120220201202204022020220405202204032120220405202204042120220405202204052120220405202204243020220501202204303120220501202205013120220501202205023120220501202205033120220501202205043120220501202205073020220501202206034120220603202206044120220603202206054120220603202209105120220910202209115120220910202209125120220910202210016120221001202210026120221001202210036120221001202210046120221001202210056120221001202210066120221001202210076120221001202210086020221001202210096020221001202212310120230101202301010120230101202301020120230101202301211120230122202301221120230122202301231120230122202301241120230122202301251120230122202301261120230122202301271120230122202301281020230122202301291020230122202304052120230405202304233020230501202304293120230501202304303120230501202305013120230501202305023120230501202305033120230501202305063020230501202306224120230622202306234120230622202306244120230622202306254020230622202309295120230929202309306120231001202310016120231001202310026120231001202310036120231001202310046120231001202310056120231001202310066120231001202310076020231001202310086020231001202312300120240101202312310120240101202401010120240101202402041020240210202402101120240210202402111120240210202402121120240210202402131120240210202402141120240210202402151120240210202402161120240210202402171120240210202402181020240210202404042120240404202404052120240404202404062120240404202404072020240404202404283020240501202405013120240501202405023120240501202405033120240501202405043120240501202405053120240501202405113020240501202406084120240610202406094120240610202406104120240610202409145020240917202409155120240917202409165120240917202409175120240917202409296020241001202410016120241001202410026120241001202410036120241001202410046120241001202410056120241001202410066120241001202410076120241001202410126020241001202501010120250101202501261020250129202501281120250129202501291120250129202501301120250129202501311120250129202502011120250129202502021120250129202502031120250129202502041120250129202502081020250129202504042120250404202504052120250404202504062120250404202504273020250501202505013120250501202505023120250501202505033120250501202505043120250501202505053120250501202505314120250531202506014120250531202506024120250531202509287020251001202510017120251001202510027120251001202510037120251001202510047120251001202510057120251001202510067120251001202510077120251001202510087120251001202510117020251001202601010120260101202601020120260101202601030120260101202601040020260101202602141020260217202602151120260217202602161120260217202602171120260217202602181120260217202602191120260217202602201120260217202602211120260217202602221120260217202602231120260217202602281020260217202604042120260405202604052120260405202604062120260405202605013120260501202605023120260501202605033120260501202605043120260501202605053120260501202605093020260501202606194120260619202606204120260619202606214120260619202609206020261001202609255120260925202609265120260925202609275120260925202610016120261001202610026120261001202610036120261001202610046120261001202610056120261001202610066120261001202610076120261001202610106020261001" 11 | private static var NAMES_IN_USE: [String] = NAMES 12 | private static var DATA_IN_USE: String = DATA 13 | 14 | private class func buildHolidayForward(s: String) -> Holiday { 15 | let day = String(s.prefix(8)) 16 | let name = NAMES_IN_USE[Int(s[s.index(s.startIndex, offsetBy: 8)].asciiValue!) - ZERO] 17 | let work = Int(s[s.index(s.startIndex, offsetBy: 9)].asciiValue!) == ZERO 18 | let target = String(s[s.index(s.startIndex, offsetBy: 10).. Holiday { 23 | let size = s.count 24 | let day = String(s[s.index(s.startIndex, offsetBy: size - 18).. String? { 32 | let start = DATA_IN_USE.range(of: key) 33 | if nil == start 34 | { 35 | return nil 36 | } 37 | var right = DATA_IN_USE.suffix(from: start!.lowerBound) 38 | var size = right.count 39 | let n = size % SIZE 40 | if n > 0 { 41 | right = right.suffix(size - n) 42 | } 43 | size = right.count 44 | while (!right.starts(with: key)) && size >= SIZE 45 | { 46 | right = right.suffix(size - SIZE) 47 | size = right.count 48 | } 49 | return String(right) 50 | } 51 | 52 | private class func findBackward(key: String) -> String? { 53 | let start = DATA_IN_USE.range(of: key, options: .backwards) 54 | if nil == start 55 | { 56 | return nil 57 | } 58 | var left = DATA_IN_USE.prefix(upTo: start!.upperBound) 59 | var size = left.count 60 | let n = size % SIZE 61 | if n > 0 { 62 | left = left.prefix(size - n) 63 | } 64 | size = left.count 65 | while (!left.hasSuffix(key)) && size >= SIZE 66 | { 67 | left = left.prefix(size - SIZE) 68 | size = left.count 69 | } 70 | return String(left) 71 | } 72 | 73 | private class func findHolidaysForward(key: String) -> [Holiday] { 74 | var l = [Holiday]() 75 | var s = findForward(key: key) 76 | if nil != s { 77 | while s!.starts(with: key) { 78 | l.append(buildHolidayForward(s: s!)) 79 | s = String(s!.suffix(s!.count - SIZE)) 80 | } 81 | } 82 | return l 83 | } 84 | 85 | private class func findHolidaysBackward(key: String) -> [Holiday] { 86 | var l = [Holiday]() 87 | var s = findBackward(key: key) 88 | if nil != s { 89 | while s!.hasSuffix(key) { 90 | l.insert(buildHolidayBackward(s: s!), at: 0) 91 | s = String(s!.prefix(s!.count - SIZE)) 92 | } 93 | } 94 | return l 95 | } 96 | 97 | public class func getHolidayByYmd(year: Int, month: Int, day: Int) -> Holiday? { 98 | let l = findHolidaysForward(key: String(format: "%04d%02d%02d", year, month, day)) 99 | return l.isEmpty ? nil : l[0] 100 | } 101 | 102 | public class func getHoliday(ymd: String) -> Holiday? { 103 | var s = ymd 104 | if s.contains("-") { 105 | s = s.replacingOccurrences(of: "-", with: "") 106 | } 107 | let l = findHolidaysForward(key: s) 108 | return l.isEmpty ? nil : l[0] 109 | } 110 | 111 | public class func getHolidaysByYm(year: Int, month: Int) -> [Holiday] { 112 | findHolidaysForward(key: String(format: "%04d%02d", year, month)) 113 | } 114 | 115 | public class func getHolidaysByYear(year: Int) -> [Holiday] { 116 | findHolidaysForward(key: String(format: "%04d", year)) 117 | } 118 | 119 | public class func getHolidays(ymd: String) -> [Holiday] { 120 | var s = ymd 121 | if s.contains("-") { 122 | s = s.replacingOccurrences(of: "-", with: "") 123 | } 124 | return findHolidaysForward(key: s) 125 | } 126 | 127 | public class func getHolidaysByTargetYmd(year: Int, month: Int, day: Int) -> [Holiday] { 128 | findHolidaysBackward(key: String(format: "%04d%02d%02d", year, month, day)) 129 | } 130 | 131 | public class func getHolidaysByTarget(ymd: String) -> [Holiday] { 132 | var s = ymd 133 | if s.contains("-") { 134 | s = s.replacingOccurrences(of: "-", with: "") 135 | } 136 | return findHolidaysBackward(key: s) 137 | } 138 | 139 | public class func fix(names: [String]?, data: String?) { 140 | if nil != names { 141 | NAMES_IN_USE = names! 142 | } 143 | if nil == data { 144 | return 145 | } 146 | var s = data! 147 | var append = "" 148 | while s.count >= SIZE { 149 | let segment = s.prefix(SIZE) 150 | let day = String(segment.prefix(8)) 151 | let remove = String(segment[segment.index(segment.startIndex, offsetBy: 8)]) == TAG_REMOVE 152 | let holiday = getHoliday(ymd: day) 153 | if nil == holiday { 154 | if !remove { 155 | append += segment 156 | } 157 | } else { 158 | var nameIndex = -1 159 | for i in (0.. -1 { 166 | let name = Character(UnicodeScalar(nameIndex + ZERO)!) 167 | let work = holiday!.work ? "1" : "0" 168 | var target = holiday!.target 169 | target = target.replacingOccurrences(of: "-", with: "") 170 | let old = "\(day)\(name)\(work)\(target)" 171 | if DATA_IN_USE.contains(old) { 172 | DATA_IN_USE = DATA_IN_USE.replacingOccurrences(of: old, with: remove ? "" : segment) 173 | } 174 | } 175 | } 176 | s = String(s.suffix(s.count - SIZE)) 177 | } 178 | if append.count > 0 { 179 | DATA_IN_USE += append 180 | } 181 | } 182 | } 183 | --------------------------------------------------------------------------------