Files
TaroTime/app/astro-calcs.js

164 lines
5.1 KiB
JavaScript
Raw Permalink Normal View History

2026-03-07 01:09:00 -08:00
(function () {
const DAY_IN_MS = 24 * 60 * 60 * 1000;
const START = ["sol", "luna", "mars", "mercury", "jupiter", "venus", "saturn"];
const CHALDEAN = ["saturn", "jupiter", "mars", "sol", "venus", "mercury", "luna"];
function toTitleCase(value) {
if (!value) return "";
return value.charAt(0).toUpperCase() + value.slice(1);
}
function getDateKey(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
function getCenteredWeekStartDay(date) {
return (date.getDay() + 4) % 7;
}
function minutesBetween(a, b) {
return (a.getTime() - b.getTime()) / 60000;
}
function getMoonPhaseName(phase) {
if (phase < 0.03 || phase > 0.97) return "New Moon";
if (phase < 0.22) return "Waxing Crescent";
if (phase < 0.28) return "First Quarter";
if (phase < 0.47) return "Waxing Gibbous";
if (phase < 0.53) return "Full Moon";
if (phase < 0.72) return "Waning Gibbous";
if (phase < 0.78) return "Last Quarter";
return "Waning Crescent";
}
function parseMonthDay(monthDay) {
const [month, day] = monthDay.split("-").map(Number);
return { month, day };
}
function isDateInSign(date, sign) {
const { month: startMonth, day: startDay } = parseMonthDay(sign.start);
const { month: endMonth, day: endDay } = parseMonthDay(sign.end);
const month = date.getMonth() + 1;
const day = date.getDate();
const wrapsYear = startMonth > endMonth;
if (!wrapsYear) {
const afterStart = month > startMonth || (month === startMonth && day >= startDay);
const beforeEnd = month < endMonth || (month === endMonth && day <= endDay);
return afterStart && beforeEnd;
}
const afterStart = month > startMonth || (month === startMonth && day >= startDay);
const beforeEnd = month < endMonth || (month === endMonth && day <= endDay);
return afterStart || beforeEnd;
}
function getSignStartDate(date, sign) {
const { month: startMonth, day: startDay } = parseMonthDay(sign.start);
const month = date.getMonth() + 1;
const day = date.getDate();
const wrapsYear = startMonth > parseMonthDay(sign.end).month;
let year = date.getFullYear();
if (wrapsYear && (month < startMonth || (month === startMonth && day < startDay))) {
year -= 1;
}
return new Date(year, startMonth - 1, startDay);
}
function getSignForDate(date, signs) {
return signs.find((sign) => isDateInSign(date, sign)) || null;
}
function groupDecansBySign(decans) {
const map = {};
for (const decan of decans) {
if (!map[decan.signId]) {
map[decan.signId] = [];
}
map[decan.signId].push(decan);
}
for (const signId of Object.keys(map)) {
map[signId].sort((a, b) => a.index - b.index);
}
return map;
}
function getDecanForDate(date, signs, decansBySign) {
const sign = getSignForDate(date, signs);
if (!sign) return null;
const signDecans = decansBySign[sign.id] || [];
if (!signDecans.length) {
return { sign, decan: null };
}
const signStartDate = getSignStartDate(date, sign);
const daysSinceSignStart = Math.floor((date.getTime() - signStartDate.getTime()) / DAY_IN_MS);
let index = Math.floor(daysSinceSignStart / 10) + 1;
if (index < 1) index = 1;
if (index > 3) index = 3;
const decan = signDecans.find((entry) => entry.index === index) || signDecans[0];
return { sign, decan };
}
function calcPlanetaryHoursForDayAndLocation(date, geo) {
const sunCalc = window.SunCalc;
if (!sunCalc) {
throw new Error("SunCalc library is not loaded.");
}
const solar = sunCalc.getTimes(date, geo.latitude, geo.longitude);
const nextDay = new Date(date.getTime() + DAY_IN_MS);
const solarNext = sunCalc.getTimes(nextDay, geo.latitude, geo.longitude);
const dayOfWeek = date.getDay();
const chaldeanStartPos = CHALDEAN.indexOf(START[dayOfWeek]);
const dayHourInMinutes = minutesBetween(solar.sunset, solar.sunrise) / 12;
const nightHourInMinutes = minutesBetween(solarNext.sunrise, solar.sunset) / 12;
const hours = [];
for (let hour = 0; hour < 12; hour += 1) {
const start = new Date(solar.sunrise.getTime() + dayHourInMinutes * hour * 60_000);
const end = new Date(start.getTime() + dayHourInMinutes * 60_000);
hours.push({
start,
end,
planetId: CHALDEAN[(chaldeanStartPos + hour) % 7],
isDaylight: true
});
}
for (let hour = 12; hour < 24; hour += 1) {
const start = new Date(solar.sunset.getTime() + nightHourInMinutes * (hour - 12) * 60_000);
const end = new Date(start.getTime() + nightHourInMinutes * 60_000);
hours.push({
start,
end,
planetId: CHALDEAN[(chaldeanStartPos + hour) % 7],
isDaylight: false
});
}
return hours;
}
window.TarotCalc = {
DAY_IN_MS,
toTitleCase,
getDateKey,
getCenteredWeekStartDay,
getMoonPhaseName,
groupDecansBySign,
getDecanForDate,
calcPlanetaryHoursForDayAndLocation
};
})();