Skip to content

Commit

Permalink
Merge pull request #9 from mannylopez/refactor-tinymoon
Browse files Browse the repository at this point in the history
Refactor TinyMoon API
  • Loading branch information
mannylopez authored May 30, 2024
2 parents 4f57a87 + b3c180d commit 9ee3712
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 66 deletions.
99 changes: 47 additions & 52 deletions Sources/TinyMoon/TinyMoon.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Foundation

public struct Moon: Hashable {
init(moonPhase: MoonPhase, lunarDay: Double, maxLunarDay: Double, date: Date) {
self.moonPhase = moonPhase
init(date: Date) {
self.date = date
self.lunarDay = Moon.lunarDay(for: date)
self.maxLunarDay = Moon.maxLunarDayInCycle(starting: date)
self.moonPhase = Moon.moonPhase(lunarDay: Int(floor(lunarDay)))
self.name = moonPhase.rawValue
self.emoji = moonPhase.emoji
self.lunarDay = lunarDay
self.maxLunarDay = maxLunarDay
self.date = date
}

public let moonPhase: MoonPhase
Expand Down Expand Up @@ -74,51 +74,8 @@ public struct Moon: Hashable {
default: nil
}
}
}

public enum MoonPhase: String {
case newMoon = "New Moon"
case waxingCrescent = "Waxing Crescent"
case firstQuarter = "First Quarter"
case waxingGibbous = "Waxing Gibbous"
case fullMoon = "Full Moon"
case waningGibbous = "Waning Gibbous"
case lastQuarter = "Last Quarter"
case waningCrescent = "Waning Crescent"

var emoji: String {
switch self {
case .newMoon:
"\u{1F311}" // 🌑
case .waxingCrescent:
"\u{1F312}" // 🌒
case .firstQuarter:
"\u{1F313}" // 🌓
case .waxingGibbous:
"\u{1F314}" // 🌔
case .fullMoon:
"\u{1F315}" // 🌕
case .waningGibbous:
"\u{1F316}" // 🌖
case .lastQuarter:
"\u{1F317}" // 🌗
case .waningCrescent:
"\u{1F318}" // 🌘
}
}
}

public struct TinyMoon {
public init() { }
public func calculateMoonPhase(_ date: Date = Date()) -> Moon {
let lunarDay = lunarDay(for: date)
let maxLunarDay = maxLunarDayInCycle(starting: date)
let moonPhase = moonPhase(lunarDay: Int(floor(lunarDay)))
let moon = Moon(moonPhase: moonPhase, lunarDay: lunarDay, maxLunarDay: maxLunarDay, date: date)
return moon
}

internal func lunarDay(for date: Date) -> Double {
internal static func lunarDay(for date: Date) -> Double {
let synodicMonth = 29.53058770576
let calendar = Calendar.current
let components = calendar.dateComponents([.day, .month, .year], from: date)
Expand All @@ -138,7 +95,7 @@ public struct TinyMoon {
return lunarDay
}

internal func maxLunarDayInCycle(starting date: Date) -> Double {
internal static func maxLunarDayInCycle(starting date: Date) -> Double {
let maxLunarDay = lunarDay(for: date)
let calendar = Calendar.current
if let tomorrow = calendar.date(byAdding: .day, value: 1, to: date) {
Expand All @@ -151,7 +108,7 @@ public struct TinyMoon {
return maxLunarDay
}

internal func moonPhase(lunarDay: Int) -> MoonPhase {
internal static func moonPhase(lunarDay: Int) -> MoonPhase {
if lunarDay < 1 {
return .newMoon
} else if lunarDay < 6 {
Expand All @@ -176,7 +133,7 @@ public struct TinyMoon {
/// The Julian Day Count is a uniform count of days from a remote epoch in the past and is used for calculating the days between two events.
/// The Julian day is calculated by combining the contributions from the years, months, and day, taking into account constant offsets and rounding down the result.
/// https://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
private func julianDay(year: Int, month: Int, day: Int) -> Double {
private static func julianDay(year: Int, month: Int, day: Int) -> Double {
var newYear = year
var newMonth = month
if month <= 2 {
Expand All @@ -191,3 +148,41 @@ public struct TinyMoon {
return Double(c + day + e + f) - 1524.5
}
}

public enum MoonPhase: String {
case newMoon = "New Moon"
case waxingCrescent = "Waxing Crescent"
case firstQuarter = "First Quarter"
case waxingGibbous = "Waxing Gibbous"
case fullMoon = "Full Moon"
case waningGibbous = "Waning Gibbous"
case lastQuarter = "Last Quarter"
case waningCrescent = "Waning Crescent"

var emoji: String {
switch self {
case .newMoon:
"\u{1F311}" // 🌑
case .waxingCrescent:
"\u{1F312}" // 🌒
case .firstQuarter:
"\u{1F313}" // 🌓
case .waxingGibbous:
"\u{1F314}" // 🌔
case .fullMoon:
"\u{1F315}" // 🌕
case .waningGibbous:
"\u{1F316}" // 🌖
case .lastQuarter:
"\u{1F317}" // 🌗
case .waningCrescent:
"\u{1F318}" // 🌘
}
}
}

public enum TinyMoon {
public static func calculateMoonPhase(_ date: Date = Date()) -> Moon {
Moon(date: date)
}
}
11 changes: 5 additions & 6 deletions Tests/TinyMoonTests/TestHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Foundation
@testable import TinyMoon

struct TestHelper {
let tinyMoon = TinyMoon()
static var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm"
Expand All @@ -19,14 +18,14 @@ struct TestHelper {
}

/// Helper function to return a moon object for a given Date
func moonDay(year: Int, month: Int, day: Int) -> Moon {
let fullMoonDate = TestHelper.dateFormatter.date(from: "\(year)/\(month)/\(day) 00:00")
let moon = tinyMoon.calculateMoonPhase(fullMoonDate!)
static func moonDay(year: Int, month: Int, day: Int) -> Moon {
let date = TestHelper.formatDate(year: year, month: month, day: day)
let moon = TinyMoon.calculateMoonPhase(date)
return moon
}

/// Helper function to return an array of moon objects for a given range of Dates
func moonRange(year: Int, month: Int, days: ClosedRange<Int>) -> [Moon] {
static func moonRange(year: Int, month: Int, days: ClosedRange<Int>) -> [Moon] {
var moons: [Moon] = []

moons = days.map({ day in
Expand All @@ -37,7 +36,7 @@ struct TestHelper {
}

/// Helper function to return a full month's moon objects
func moonMonth(month: Helper.Month) -> [Moon] {
static func moonMonth(month: Helper.Month) -> [Moon] {
var moons: [Moon] = []

Helper.months2024[month]?.forEach({ day in
Expand Down
14 changes: 6 additions & 8 deletions Tests/TinyMoonTests/TinyMoonTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import XCTest
final class TinyMoonTests: XCTestCase {
func test_tinyMoon_calculateMoonPhase_returnsFullMoon() throws {
let date = TestHelper.formatDate(year: 2024, month: 04, day: 23)
let moon = TestHelper().tinyMoon.calculateMoonPhase(date)
let moon = TinyMoon.calculateMoonPhase(date)

XCTAssertTrue(moon.isFullMoon())
XCTAssertEqual(moon.fullMoonName, "Pink Moon")
Expand All @@ -14,7 +14,7 @@ final class TinyMoonTests: XCTestCase {

func test_moon_daysTillFullMoon_returnsCorrectDays() throws {
let date = TestHelper.formatDate(year: 2024, month: 04, day: 22)
let moon = TestHelper().tinyMoon.calculateMoonPhase(date)
let moon = TinyMoon.calculateMoonPhase(date)

XCTAssertFalse(moon.isFullMoon())
XCTAssertNil(moon.fullMoonName)
Expand All @@ -24,23 +24,21 @@ final class TinyMoonTests: XCTestCase {

func test_tinyMoon_calculateMoonPhase_returnsNewMoon() throws {
var date = TestHelper.formatDate(year: 2024, month: 11, day: 01)
let testHelper = TestHelper()
var moon = testHelper.tinyMoon.calculateMoonPhase(date)
var moon = TinyMoon.calculateMoonPhase(date)
XCTAssertEqual(moon.emoji, "\u{1F311}") // 🌑
XCTAssertEqual(moon.daysTillNewMoon, 0)

date = TestHelper.formatDate(year: 2024, month: 12, day: 1)
moon = testHelper.tinyMoon.calculateMoonPhase(date)
moon = TinyMoon.calculateMoonPhase(date)
XCTAssertEqual(moon.emoji, "\u{1F311}") // 🌑
XCTAssertEqual(moon.daysTillNewMoon, 0)
}

func test_moon_uniquePhases() {
let testHelper = TestHelper()
var months: [Helper.Month] = [.january, .february, .april, .may, .june, .july, .august, .september, .october, .november]

months.forEach { month in
let moons = testHelper.moonMonth(month: month)
let moons = TestHelper.moonMonth(month: month)
let emojis = moons.compactMap { moon in
switch moon.moonPhase {
case .newMoon, .firstQuarter, .fullMoon, .lastQuarter:
Expand All @@ -56,7 +54,7 @@ final class TinyMoonTests: XCTestCase {

months = [.march, .december]
months.forEach { month in
let moons = testHelper.moonMonth(month: month)
let moons = TestHelper.moonMonth(month: month)
let emojis = moons.compactMap { moon in
switch moon.moonPhase {
case .newMoon, .firstQuarter, .fullMoon, .lastQuarter:
Expand Down

0 comments on commit 9ee3712

Please sign in to comment.