jasonsturges/lunarphase-js

consider adding multiple language, and next new moon/full moon? Here is my code

talpx0 opened this issue · 1 comments

export function calculateMoonPhase(currentDate: Date): number {
  const newMoonDate = new Date(Date.UTC(2019, 0, 6, 1, 28, 0)); // Reference New Moon date
  const lunarCycleLength = 29.53; // Average length of the lunar cycle in days
  const diffInDays = (currentDate.getTime() - newMoonDate.getTime()) / (1000 * 3600 * 24);
  const moonPhase = (diffInDays % lunarCycleLength) / lunarCycleLength;
  return moonPhase;
}


export function estimateNextMoonPhases(currentDate: Date): { nextNewMoon: string, nextFullMoon: string} {
  const currentMoonPhase = calculateMoonPhase(currentDate);
  const lunarCycleLength = 29.53; // Average length of the lunar cycle in days

  let daysUntilNextNewMoon = lunarCycleLength * (1 - currentMoonPhase);
  if (currentMoonPhase >= 0 && currentMoonPhase < 0.5) {
      daysUntilNextNewMoon = lunarCycleLength * (0.5 - currentMoonPhase);
  }

  let daysUntilNextFullMoon = lunarCycleLength * (0.5 - currentMoonPhase);

  if (currentMoonPhase >= 0.5) {
      daysUntilNextFullMoon = lunarCycleLength * (1 - currentMoonPhase) + (lunarCycleLength / 2);
  }

  const nextNewMoon = formatMMDDDate(new Date(currentDate.getTime() + daysUntilNextNewMoon * 24 * 3600 * 1000));
  const nextFullMoon = formatMMDDDate(new Date(currentDate.getTime() + daysUntilNextFullMoon * 24 * 3600 * 1000));
  return { nextNewMoon, nextFullMoon };
}


export function getMoonPhaseName(moonPhase: number): string {
  if(moonPhase < 0.03 || moonPhase > 0.97) {
      return "NewMoon";
  } else if(moonPhase < 0.22) {
      return "WaxingCrescent";
  } else if(moonPhase < 0.28) {
      return "FirstQuarter";
  } else if(moonPhase < 0.47) {
      return "WaxingGibbous";
  } else if(moonPhase < 0.53) {
      return "FullMoon";
  } else if(moonPhase < 0.72) {
      return "WaningGibbous";
  } else if(moonPhase < 0.78) {
      return "LastQuarter";
  } else {
      return "WaningCrescent";
  }
}


export function getMoonPhasesForPeriod(currentDate: Date = new Date()): MoonPhase[] {
    const todayMoonPhaseNumber = calculateMoonPhase(currentDate);
    const todayMoonPhaseName = getMoonPhaseName(todayMoonPhaseNumber);
    const { nextNewMoon, nextFullMoon } = estimateNextMoonPhases(currentDate);
  
    const today: MoonPhase = {
      date: formatMMDDDate(currentDate),
      moonPhaseIcon: MoonPhaseIcons[todayMoonPhaseName],
      name: todayMoonPhaseName,
    };
  
    const nextNewMoonPhase: MoonPhase = {
      date: nextNewMoon,
      moonPhaseIcon: MoonPhaseIcons["NewMoon"],
      name: "NewMoon",
    };
  
    const nextFullMoonPhase: MoonPhase = {
      date: nextFullMoon,
      moonPhaseIcon: MoonPhaseIcons["FullMoon"],
      name: "FullMoon",
    };
  
    // Determine the sequence based on current phase
    let phases: MoonPhase[];
    if (todayMoonPhaseNumber < 0.5) {
      // Today -> Next Full Moon -> Next New Moon
      phases = [today, nextFullMoonPhase, nextNewMoonPhase];
    } else {
      // Today -> Next New Moon -> Next Full Moon
      phases = [today, nextNewMoonPhase, nextFullMoonPhase];
    }
  
    return phases;
}

Appreciate sharing the ideas and implementation.

Internationalization support was something I had considered based on the lunar phase enumeration, but adds a hefty amount of complexity.

Any i18n implementation and corresponding translations would be local to that project.

Similar requests for next cycles have been made, in #30, and pull request in #17.

My formula has a small margin of error that I never had time to resolve. There are some other aspects in regards to how this library handles time windows versus events.