├── README.md ├── .gitignore ├── Tests ├── LinuxMain.swift └── ACNHEventsTests │ ├── XCTestManifests.swift │ └── ACNHEventsTests.swift ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Package.swift └── Sources └── ACNHEvents └── Date+ACNHEvents.swift /README.md: -------------------------------------------------------------------------------- 1 | # ACNHEvents 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import ACNHEventsTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ACNHEventsTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/ACNHEventsTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(ACNHEventsTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 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: "ACNHEvents", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "ACNHEvents", 12 | targets: ["ACNHEvents"]), 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 which this package depends on. 21 | .target( 22 | name: "ACNHEvents", 23 | dependencies: []), 24 | .testTarget( 25 | name: "ACNHEventsTests", 26 | dependencies: ["ACNHEvents"]), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /Sources/ACNHEvents/Date+ACNHEvents.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Hemisphere { 4 | case north, south 5 | } 6 | 7 | public enum Event { 8 | // 1.0.0 - 1.1.4 9 | case bunnyDay, fishingTourney, earthDay, newYearsDay, newYearsEve, cherryBlossumSeason, bugOff 10 | // 1.2.0 11 | case natureDay, mayDay, museumDay, weddingSeason 12 | } 13 | 14 | public extension Date { 15 | func events(for hemisphere: Hemisphere) -> [Event] { 16 | var events: [Event] = [] 17 | 18 | // v1.0.0 - 1.1.4 19 | if isBunnyDay { events.append(.bunnyDay) } 20 | if isFishingTourneyDay { events.append(.fishingTourney) } 21 | if isEarthDay { events.append(.earthDay) } 22 | if isNewYearsDay { events.append(.newYearsDay) } 23 | if isNewYearsEve { events.append(.newYearsEve) } 24 | if isCherryBlossomSeason(hemisphere) { events.append(.cherryBlossumSeason) } 25 | if isBugOffDay(hemisphere) { events.append(.bugOff) } 26 | 27 | // v1.2.0 28 | if isNatureDay { events.append(.natureDay) } 29 | if isMayDay { events.append(.mayDay) } 30 | if isMuseumDay { events.append(.museumDay) } 31 | if isWeddingSeason { events.append(.weddingSeason) } 32 | 33 | return events 34 | } 35 | 36 | var isBunnyDay: Bool { 37 | let components = Calendar.current.dateComponents([.day, .month], from: self) 38 | 39 | guard let month = components.month, let day = components.day else { 40 | return false 41 | } 42 | 43 | return month == 4 && day >= 1 && day <= 12 44 | } 45 | 46 | var isNatureDay: Bool { 47 | let components = Calendar.current.dateComponents([.day, .month], from: self) 48 | 49 | guard let month = components.month, let day = components.day else { 50 | return false 51 | } 52 | 53 | return (month == 4 && day >= 23 && day <= 31) || (month == 5 && day <= 4) 54 | } 55 | 56 | var isMayDay: Bool { 57 | let components = Calendar.current.dateComponents([.day, .month], from: self) 58 | 59 | guard let month = components.month, let day = components.day else { 60 | return false 61 | } 62 | 63 | return month == 5 && day >= 1 && day <= 7 64 | } 65 | 66 | var isMuseumDay: Bool { 67 | let components = Calendar.current.dateComponents([.day, .month], from: self) 68 | 69 | guard let month = components.month, let day = components.day else { 70 | return false 71 | } 72 | 73 | return month == 5 && day >= 18 && day <= 31 74 | } 75 | 76 | var isWeddingSeason: Bool { 77 | let components = Calendar.current.dateComponents([.day, .month], from: self) 78 | 79 | guard let month = components.month, let day = components.day else { 80 | return false 81 | } 82 | 83 | return month == 6 && day >= 1 && day <= 30 84 | } 85 | 86 | var isFishingTourneyDay: Bool { 87 | let components = Calendar.current.dateComponents([.day, .month, .weekday], from: self) 88 | 89 | guard let month = components.month, let day = components.day else { 90 | return false 91 | } 92 | 93 | switch month { 94 | case 1, 4, 7, 10: 95 | return components.weekday == 7 && (day - 1) / 7 == 1 96 | default: 97 | return false 98 | } 99 | } 100 | 101 | var isEarthDay: Bool { 102 | let components = Calendar.current.dateComponents([.day, .month], from: self) 103 | 104 | guard let month = components.month, let day = components.day else { 105 | return false 106 | } 107 | 108 | return month == 4 && day == 22 109 | } 110 | 111 | var isNewYearsDay: Bool { 112 | let components = Calendar.current.dateComponents([.day, .month], from: self) 113 | 114 | guard let month = components.month, let day = components.day else { 115 | return false 116 | } 117 | 118 | return month == 1 && day == 1 119 | } 120 | 121 | var isNewYearsEve: Bool { 122 | let components = Calendar.current.dateComponents([.day, .month], from: self) 123 | 124 | guard let month = components.month, let day = components.day else { 125 | return false 126 | } 127 | 128 | return month == 12 && day == 31 129 | } 130 | 131 | func isCherryBlossomSeason(_ hemisphere: Hemisphere) -> Bool { 132 | let components = Calendar.current.dateComponents([.day, .month], from: self) 133 | 134 | guard let month = components.month, let day = components.day else { 135 | return false 136 | } 137 | 138 | let inDayRange = (day >= 1 && day <= 10) 139 | 140 | switch hemisphere { 141 | case .north: 142 | return month == 4 && inDayRange 143 | case .south: 144 | return month == 10 && inDayRange 145 | } 146 | } 147 | 148 | func isBugOffDay(_ hemisphere: Hemisphere) -> Bool { 149 | let components = Calendar.current.dateComponents([.day, .month, .weekday], from: self) 150 | 151 | guard let month = components.month, let day = components.day else { 152 | return false 153 | } 154 | 155 | switch hemisphere { 156 | case .north: 157 | switch month { 158 | case 6, 7, 8, 9: 159 | return components.weekday == 7 && (day - 1) / 7 == 3 160 | default: 161 | return false 162 | } 163 | case .south: 164 | switch month { 165 | case 1, 2, 11, 12: 166 | return components.weekday == 7 && (day - 1) / 7 == 2 167 | default: 168 | return false 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /Tests/ACNHEventsTests/ACNHEventsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import ACNHEvents 3 | 4 | final class ACNHEventsTests: XCTestCase { 5 | 6 | func testEarthDay() { 7 | let c = DateComponents(calendar: .current, month: 4, day: 22) 8 | let d = Calendar.current.date(from: c) 9 | XCTAssertEqual(d?.isEarthDay, true) 10 | } 11 | 12 | func testNewYearDay() { 13 | let c = DateComponents(calendar: .current, month: 1, day: 1) 14 | let d = Calendar.current.date(from: c) 15 | XCTAssertEqual(d?.isNewYearsDay, true) 16 | } 17 | 18 | func testNewYearEve() { 19 | let c = DateComponents(calendar: .current, month: 12, day: 31) 20 | let d = Calendar.current.date(from: c) 21 | XCTAssertEqual(d?.isNewYearsEve, true) 22 | } 23 | 24 | func testSpringFishTourney() { 25 | let c = DateComponents(calendar: .current, year: 2020, month: 4, day: 11) 26 | let d = Calendar.current.date(from: c) 27 | XCTAssertEqual(d?.isFishingTourneyDay, true) 28 | } 29 | 30 | func testSummerFishTourney() { 31 | let c = DateComponents(calendar: .current, year: 2020, month: 7, day: 11) 32 | let d = Calendar.current.date(from: c) 33 | XCTAssertEqual(d?.isFishingTourneyDay, true) 34 | } 35 | 36 | func testFallFishTourney() { 37 | let c = DateComponents(calendar: .current, year: 2020, month: 10, day: 10) 38 | let d = Calendar.current.date(from: c) 39 | XCTAssertEqual(d?.isFishingTourneyDay, true) 40 | } 41 | 42 | func testWinterFishTourney() { 43 | let c = DateComponents(calendar: .current, year: 2020, month: 1, day: 11) 44 | let d = Calendar.current.date(from: c) 45 | XCTAssertEqual(d?.isFishingTourneyDay, true) 46 | } 47 | 48 | func testNorthCherryBlossomSeason() { 49 | let c = DateComponents(calendar: .current, year: 2020, month: 4, day: 1) 50 | let d = Calendar.current.date(from: c) 51 | XCTAssertEqual(d?.isCherryBlossomSeason(.north), true) 52 | 53 | let c1 = DateComponents(calendar: .current, year: 2020, month: 4, day: 10) 54 | let d1 = Calendar.current.date(from: c1) 55 | XCTAssertEqual(d1?.isCherryBlossomSeason(.north), true) 56 | 57 | let c2 = DateComponents(calendar: .current, year: 2020, month: 4, day: 11) 58 | let d2 = Calendar.current.date(from: c2) 59 | XCTAssertEqual(d2?.isCherryBlossomSeason(.north), false) 60 | } 61 | 62 | func testSouthCherryBlossomSeason() { 63 | let c = DateComponents(calendar: .current, year: 2020, month: 10, day: 1) 64 | let d = Calendar.current.date(from: c) 65 | XCTAssertEqual(d?.isCherryBlossomSeason(.south), true) 66 | 67 | let c1 = DateComponents(calendar: .current, year: 2020, month: 10, day: 10) 68 | let d1 = Calendar.current.date(from: c1) 69 | XCTAssertEqual(d1?.isCherryBlossomSeason(.south), true) 70 | 71 | let c2 = DateComponents(calendar: .current, year: 2020, month: 10, day: 11) 72 | let d2 = Calendar.current.date(from: c2) 73 | XCTAssertEqual(d2?.isCherryBlossomSeason(.south), false) 74 | } 75 | 76 | func testBunnyDay() { 77 | let c = DateComponents(calendar: .current, year: 2020, month: 4, day: 1) 78 | let d = Calendar.current.date(from: c) 79 | XCTAssertEqual(d?.isBunnyDay, true) 80 | 81 | let c1 = DateComponents(calendar: .current, year: 2020, month: 4, day: 12) 82 | let d1 = Calendar.current.date(from: c1) 83 | XCTAssertEqual(d1?.isBunnyDay, true) 84 | 85 | let c2 = DateComponents(calendar: .current, year: 2020, month: 4, day: 13) 86 | let d2 = Calendar.current.date(from: c2) 87 | XCTAssertEqual(d2?.isBunnyDay, false) 88 | } 89 | 90 | func testNorthBugDayOffs() { 91 | let c = DateComponents(calendar: .current, year: 2020, month: 6, day: 27) 92 | let d = Calendar.current.date(from: c) 93 | XCTAssertEqual(d?.isBugOffDay(.north), true) 94 | 95 | let c1 = DateComponents(calendar: .current, year: 2020, month: 7, day: 25) 96 | let d1 = Calendar.current.date(from: c1) 97 | XCTAssertEqual(d1?.isBugOffDay(.north), true) 98 | 99 | let c2 = DateComponents(calendar: .current, year: 2020, month: 8, day: 22) 100 | let d2 = Calendar.current.date(from: c2) 101 | XCTAssertEqual(d2?.isBugOffDay(.north), true) 102 | 103 | let c3 = DateComponents(calendar: .current, year: 2020, month: 9, day: 26) 104 | let d3 = Calendar.current.date(from: c3) 105 | XCTAssertEqual(d3?.isBugOffDay(.north), true) 106 | } 107 | 108 | func testSouthBugDayOffs() { 109 | let c = DateComponents(calendar: .current, year: 2020, month: 11, day: 21) 110 | let d = Calendar.current.date(from: c) 111 | XCTAssertEqual(d?.isBugOffDay(.south), true) 112 | 113 | let c1 = DateComponents(calendar: .current, year: 2020, month: 12, day: 19) 114 | let d1 = Calendar.current.date(from: c1) 115 | XCTAssertEqual(d1?.isBugOffDay(.south), true) 116 | 117 | let c2 = DateComponents(calendar: .current, year: 2020, month: 1, day: 18) 118 | let d2 = Calendar.current.date(from: c2) 119 | XCTAssertEqual(d2?.isBugOffDay(.south), true) 120 | 121 | let c3 = DateComponents(calendar: .current, year: 2020, month: 2, day: 15) 122 | let d3 = Calendar.current.date(from: c3) 123 | XCTAssertEqual(d3?.isBugOffDay(.south), true) 124 | } 125 | 126 | static var allTests = [ 127 | ("testEarthDay", testEarthDay), 128 | ("testNewYearDay", testNewYearDay), 129 | ("testNewYearEve", testNewYearEve), 130 | ("testSpringFishTourney", testSpringFishTourney), 131 | ("testSummerFishTourney", testSummerFishTourney), 132 | ("testFallFishTourney", testFallFishTourney), 133 | ("testWinterFishTourney", testWinterFishTourney), 134 | ("testNorthCherryBlossomSeason", testNorthCherryBlossomSeason), 135 | ("testSouthCherryBlossomSeason", testSouthCherryBlossomSeason), 136 | ("testBunnyDay", testBunnyDay), 137 | ("testNorthBugDayOffs", testNorthBugDayOffs), 138 | ("testSouthBugDayOffs", testSouthBugDayOffs), 139 | ] 140 | } 141 | --------------------------------------------------------------------------------