const std = @import("std"); const c = @cImport({ @cInclude("time.h"); @cInclude("astro.h"); }); pub const Phase = struct { phase: f64, // 0.0 to 1.0 (0=new, 0.25=first quarter, 0.5=full, 0.75=last quarter) illuminated: f64, // 0.0 to 1.0 age_days: f64, // Age in days distance_km: f64, angular_diameter: f64, pub fn emoji(self: Phase) []const u8 { const p = self.phase; if (p < 0.0625) return "🌑"; // New moon if (p < 0.1875) return "🌒"; // Waxing crescent if (p < 0.3125) return "🌓"; // First quarter if (p < 0.4375) return "🌔"; // Waxing gibbous if (p < 0.5625) return "🌕"; // Full moon if (p < 0.6875) return "🌖"; // Waning gibbous if (p < 0.8125) return "🌗"; // Last quarter if (p < 0.9375) return "🌘"; // Waning crescent return "🌑"; // New moon } pub fn day(self: Phase) u8 { return @intFromFloat(@round(self.age_days)); } }; pub fn getPhase(timestamp: i64) Phase { const julian = c.unix_to_julian(@intCast(timestamp)); var illuminated: f64 = 0; var age_days: f64 = 0; var distance: f64 = 0; var angular_diameter: f64 = 0; var sun_distance: f64 = 0; var sun_angular_diameter: f64 = 0; const phase_angle = c.phase( julian, &illuminated, &age_days, &distance, &angular_diameter, &sun_distance, &sun_angular_diameter, ); return .{ .phase = phase_angle, .illuminated = illuminated, .age_days = age_days, .distance_km = distance, .angular_diameter = angular_diameter, }; } test "moon phase calculation" { // Test a known date: 2024-01-01 00:00:00 UTC const timestamp: i64 = 1704067200; const moon = getPhase(timestamp); try std.testing.expect(moon.phase >= 0.0 and moon.phase <= 1.0); try std.testing.expect(moon.illuminated >= 0.0 and moon.illuminated <= 1.0); try std.testing.expect(moon.age_days >= 0.0 and moon.age_days <= 29.53); try std.testing.expect(moon.distance_km > 0); }