@verifyhash/moon-phase 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/index.js +223 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 <OWNER-NAME PLACEHOLDER>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# moon-phase
|
|
2
|
+
|
|
3
|
+
<!-- publish-prep -->
|
|
4
|
+
> **TODO (owner): pick the final npm name/scope before publishing.**
|
|
5
|
+
> The npm package name is **not** finalized by this project. Convention used here:
|
|
6
|
+
> `package.json` keeps the working slug `moon-phase` as a placeholder — replace it
|
|
7
|
+
> (and the `npm install` line below) with the real published name/scope before
|
|
8
|
+
> running `npm publish`.
|
|
9
|
+
|
|
10
|
+
A **zero-dependency, pure-function** Node library that turns any instant into the
|
|
11
|
+
Moon's phase: its phase angle, the fraction of the disk that is lit, the synodic
|
|
12
|
+
age in days, the canonical phase name, and the Brown lunation number.
|
|
13
|
+
|
|
14
|
+
No network, no external dependencies, no servers, no I/O. Every export is a pure
|
|
15
|
+
function of a single `Date` and never mutates its argument. `require()` it and go.
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const moon = require('./index.js');
|
|
19
|
+
|
|
20
|
+
const d = new Date('2024-01-25T17:54:00Z'); // a full moon
|
|
21
|
+
moon.phaseName(d); // 'Full'
|
|
22
|
+
moon.illuminatedFraction(d); // ~1.0
|
|
23
|
+
moon.moonAge(d); // ~14.75 (days since the last new moon)
|
|
24
|
+
moon.moonPhaseAngle(d); // ~179.85 (Sun-Moon elongation, degrees)
|
|
25
|
+
moon.lunation(d); // 1250 (Brown Lunation Number)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Who it's for
|
|
29
|
+
|
|
30
|
+
Developers who need the Moon's phase without pulling in a heavyweight ephemeris:
|
|
31
|
+
|
|
32
|
+
- **Weather / almanac apps** — show tonight's phase and % illumination next to
|
|
33
|
+
the forecast.
|
|
34
|
+
- **Astronomy & astrophotography planners** — a dark-sky (new moon) or
|
|
35
|
+
full-moon window finder, or a "how washed-out will the sky be" estimate.
|
|
36
|
+
- **Tide and fishing tools** — spring/neap tides track the new/full syzygy, and
|
|
37
|
+
the phase angle is a cheap proxy for it.
|
|
38
|
+
- **Calendar / UI widgets** — a correct moon glyph for any date.
|
|
39
|
+
|
|
40
|
+
It is a *geocentric phase* model: it answers "what phase is the Moon in?", not
|
|
41
|
+
"when does the Moon rise here?" or "where is it in my sky?". For observer-local
|
|
42
|
+
rise/set/altitude you want a different tool (e.g. a full ephemeris).
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
All functions take a JS `Date` interpreted as a **UTC instant** (date *and*
|
|
47
|
+
time). The time of day matters: the Moon moves ~12°/day, so one hour shifts the
|
|
48
|
+
elongation by ~0.5°. A date-only value like `new Date('2024-01-11')` is treated
|
|
49
|
+
as `00:00 UTC` that day.
|
|
50
|
+
|
|
51
|
+
| Function | Returns |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `moonPhaseAngle(date)` | Sun-Moon elongation in degrees, `[0, 360)`. 0° = New, 90° = First Quarter, 180° = Full, 270° = Last Quarter. |
|
|
54
|
+
| `illuminatedFraction(date)` | Fraction of the disk lit, `[0, 1]`. 0 at new, 1 at full, ~0.5 at the quarters. |
|
|
55
|
+
| `moonAge(date)` | Days since the last new moon (synodic age), `[0, ~29.53)`. |
|
|
56
|
+
| `phaseName(date)` | One of the 8 canonical phases: `New`, `Waxing Crescent`, `First Quarter`, `Waxing Gibbous`, `Full`, `Waning Gibbous`, `Last Quarter`, `Waning Crescent`. |
|
|
57
|
+
| `lunation(date)` | Brown Lunation Number (integer) of the lunation in progress. |
|
|
58
|
+
|
|
59
|
+
`phaseName` bins the elongation into eight 45°-wide sectors, each centred on its
|
|
60
|
+
defining angle — so `New` covers 337.5°–360°–22.5°, `First Quarter` covers
|
|
61
|
+
67.5°–112.5°, and so on. The narrow "instant" phases (New, the two quarters,
|
|
62
|
+
Full) are the sectors straddling their exact angles.
|
|
63
|
+
|
|
64
|
+
## Algorithm & citation
|
|
65
|
+
|
|
66
|
+
The geometry follows **Jean Meeus, *Astronomical Algorithms* (2nd ed., 1998)**:
|
|
67
|
+
|
|
68
|
+
1. Evaluate the Moon's mean elongation `D`, the Sun's mean anomaly `M`, and the
|
|
69
|
+
Moon's mean anomaly `M′` from the standard polynomials in Julian centuries
|
|
70
|
+
`T` from J2000.0 (Meeus ch. 47).
|
|
71
|
+
2. Form the true Sun-Moon elongation by adding the leading periodic terms of the
|
|
72
|
+
phase-angle series (Meeus ch. 48, eq. 48.4 — the published "low accuracy"
|
|
73
|
+
method), giving the phase angle `i` and elongation `180° − i`.
|
|
74
|
+
3. Illuminated fraction `k = (1 + cos i) / 2` (Meeus eq. 48.1).
|
|
75
|
+
|
|
76
|
+
The synodic month is taken as `29.530588853` days (Meeus ch. 47). The Brown
|
|
77
|
+
lunation number is anchored to the new moon of **2000-01-06 18:14 UTC**
|
|
78
|
+
(Meeus lunation index `k = 0`), whose Brown Lunation Number is 953; Brown
|
|
79
|
+
lunation 1 was the new moon of 1923-01-17.
|
|
80
|
+
|
|
81
|
+
## Accuracy — honest limits
|
|
82
|
+
|
|
83
|
+
This is a **simplified** model. It keeps only the largest periodic terms and no
|
|
84
|
+
planetary perturbations, so:
|
|
85
|
+
|
|
86
|
+
- **Phase angle / elongation:** within about **0.5°** of the full Meeus solution
|
|
87
|
+
for dates within a couple of centuries of J2000.
|
|
88
|
+
- **Illuminated fraction:** within about **1%** — a few percent near a thin
|
|
89
|
+
crescent, where a 0.5° angle error has the most leverage.
|
|
90
|
+
- **Synodic age (`moonAge`):** derived from the true elongation via the *mean*
|
|
91
|
+
synodic month, so it can drift up to roughly **0.5 day** from a true almanac
|
|
92
|
+
age, because the Moon's angular speed varies over its ~27.5-day anomalistic
|
|
93
|
+
(perigee/apogee) cycle. Treat it as good to a *fraction of a day*, not to the
|
|
94
|
+
minute.
|
|
95
|
+
- **No topocentric / parallax correction:** this is the geocentric phase, which
|
|
96
|
+
is what "the phase of the Moon" almost always means.
|
|
97
|
+
|
|
98
|
+
If you need to-the-minute event times (exact new/full moon instants) or a
|
|
99
|
+
sky-position ephemeris, use a full-precision library instead. This one is for
|
|
100
|
+
"what phase, how lit, how old, what's it called" — fast, dependency-free, and
|
|
101
|
+
accurate enough for UI, planning, and forecasting overlays.
|
|
102
|
+
|
|
103
|
+
## Tests
|
|
104
|
+
|
|
105
|
+
Run the full suite (a tiny built-in assert harness — no framework, no deps):
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
node test/moon.test.js
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
It exits non-zero on any failure. Fixtures use published new/full/quarter-moon
|
|
112
|
+
instants (NASA GSFC phase tables / timeanddate.com), and the suite also checks
|
|
113
|
+
that illumination increases monotonically across the waxing half-lunation, that
|
|
114
|
+
the phase-name bins are ordered and complete over a lunation, that inputs are not
|
|
115
|
+
mutated, and that invalid input throws. Stated tolerances: 1.5° on the phase
|
|
116
|
+
angle at a documented event, 0.02 on the illuminated fraction at the quarters,
|
|
117
|
+
and 0.15 day on the synodic age.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT.
|
|
122
|
+
|
|
123
|
+
## Install
|
|
124
|
+
|
|
125
|
+
> Placeholder name: the `moon-phase` below is the working slug, not a finalized
|
|
126
|
+
> npm name — see the owner TODO near the top of this README.
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
npm install moon-phase
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
const moon = require('moon-phase');
|
|
134
|
+
|
|
135
|
+
const d = new Date('2024-01-25T17:54:00Z'); // a full moon
|
|
136
|
+
moon.phaseName(d); // 'Full'
|
|
137
|
+
moon.illuminatedFraction(d); // ~1.0
|
|
138
|
+
moon.lunation(d); // 1250 (Brown Lunation Number)
|
|
139
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* moon-phase — zero-dependency lunar phase & illumination math.
|
|
5
|
+
*
|
|
6
|
+
* Every export is a PURE function: no I/O, no network, no globals mutated, no
|
|
7
|
+
* argument mutation, no dependencies. It turns any instant (a UTC JS Date) into
|
|
8
|
+
* the Moon's phase angle, illuminated fraction, synodic age, canonical phase
|
|
9
|
+
* name, and Brown lunation number.
|
|
10
|
+
*
|
|
11
|
+
* ALGORITHM
|
|
12
|
+
* The geometry follows Jean Meeus, "Astronomical Algorithms" (2nd ed., 1998),
|
|
13
|
+
* chapter 49 (the mean elongation / mean-anomaly series) and chapter 48's
|
|
14
|
+
* low-accuracy phase-angle formula (48.4). We evaluate the Moon's mean
|
|
15
|
+
* elongation D, the Sun's mean anomaly M, and the Moon's mean anomaly M' from
|
|
16
|
+
* the standard polynomials in Julian centuries T, then add the leading
|
|
17
|
+
* periodic terms to obtain the true Sun-Moon elongation and phase angle. The
|
|
18
|
+
* illuminated fraction is k = (1 + cos i) / 2 where i is the phase angle
|
|
19
|
+
* (Meeus 48.1). This is the same "low accuracy" method NASA and many almanac
|
|
20
|
+
* tools quote as good to a few tenths of a degree in phase angle.
|
|
21
|
+
*
|
|
22
|
+
* ACCURACY (honest limits — this is a SIMPLIFIED model)
|
|
23
|
+
* - Phase angle / elongation: within ~0.5° of the full Meeus solution for
|
|
24
|
+
* dates within a couple of centuries of J2000; it omits the smaller
|
|
25
|
+
* periodic terms and all planetary perturbations.
|
|
26
|
+
* - Illuminated fraction: within ~1% (a few percent near the thin crescent,
|
|
27
|
+
* where a 0.5° angle error matters most).
|
|
28
|
+
* - Synodic age (moonAge): derived from the true elongation via the MEAN
|
|
29
|
+
* synodic month, so it can drift up to ~0.5 day from a true almanac age
|
|
30
|
+
* because the Moon's angular speed varies with its ~27.5-day anomalistic
|
|
31
|
+
* cycle (perigee/apogee). Treat it as good to a fraction of a day, not to
|
|
32
|
+
* the minute.
|
|
33
|
+
* - No topocentric / parallax correction: this is the GEOCENTRIC phase, which
|
|
34
|
+
* is what "the phase of the Moon" almost always means. It is not a rise/set
|
|
35
|
+
* or an observer-altitude calculation.
|
|
36
|
+
*
|
|
37
|
+
* INPUT
|
|
38
|
+
* Every function takes a JS Date interpreted as a UTC INSTANT (date + time).
|
|
39
|
+
* Unlike a daily sunrise calc, the time of day matters here: the Moon moves
|
|
40
|
+
* ~12° per day, so an hour changes the elongation by ~0.5°. Passing a
|
|
41
|
+
* date-only value like new Date('2024-01-11') is treated as 00:00 UTC that
|
|
42
|
+
* day. Inputs are never mutated.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Constants
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
// Mean length of the synodic month (new moon to new moon), in days. Meeus 47.
|
|
50
|
+
const SYNODIC_MONTH = 29.530588853;
|
|
51
|
+
|
|
52
|
+
// Julian Day of the Unix epoch 1970-01-01T00:00:00Z.
|
|
53
|
+
const JULIAN_UNIX_EPOCH = 2440587.5;
|
|
54
|
+
const MS_PER_DAY = 86400000;
|
|
55
|
+
|
|
56
|
+
// Reference new moon used for the Brown lunation number: the new moon of
|
|
57
|
+
// 2000-01-06 at 18:14 UTC has Julian Day ~2451550.1 and is Meeus lunation
|
|
58
|
+
// index k = 0 (Meeus 49.1). The Brown Lunation Number of that new moon is 953,
|
|
59
|
+
// so BLN = floor(k) + 953. (Brown lunation 1 = the new moon of 1923-01-17.)
|
|
60
|
+
const NEWMOON_2000_JD = 2451550.1;
|
|
61
|
+
const BROWN_LUNATION_OFFSET = 953;
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Helpers
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
const rad = (d) => (d * Math.PI) / 180;
|
|
68
|
+
const deg = (r) => (r * 180) / Math.PI;
|
|
69
|
+
|
|
70
|
+
// Normalise degrees into [0, 360).
|
|
71
|
+
function norm360(x) {
|
|
72
|
+
let r = x % 360;
|
|
73
|
+
if (r < 0) r += 360;
|
|
74
|
+
return r;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Julian Day (including fractional day) for a JS Date instant. Does NOT mutate.
|
|
78
|
+
function julianDay(date) {
|
|
79
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
80
|
+
throw new TypeError('moon-phase: expected a valid JS Date (UTC instant)');
|
|
81
|
+
}
|
|
82
|
+
return date.getTime() / MS_PER_DAY + JULIAN_UNIX_EPOCH;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Meeus fundamental arguments (degrees) at Julian Day jd.
|
|
86
|
+
// Returns { D, M, Mp } — Moon's mean elongation, Sun's mean anomaly, Moon's
|
|
87
|
+
// mean anomaly. Polynomials from Meeus "Astronomical Algorithms" ch. 47.
|
|
88
|
+
function fundamentalArguments(jd) {
|
|
89
|
+
const T = (jd - 2451545.0) / 36525; // Julian centuries from J2000.0
|
|
90
|
+
|
|
91
|
+
const D =
|
|
92
|
+
297.8501921 +
|
|
93
|
+
445267.1114034 * T -
|
|
94
|
+
0.0018819 * T * T +
|
|
95
|
+
(T * T * T) / 545868 -
|
|
96
|
+
(T * T * T * T) / 113065000;
|
|
97
|
+
|
|
98
|
+
const M =
|
|
99
|
+
357.5291092 +
|
|
100
|
+
35999.0502909 * T -
|
|
101
|
+
0.0001536 * T * T +
|
|
102
|
+
(T * T * T) / 24490000;
|
|
103
|
+
|
|
104
|
+
const Mp =
|
|
105
|
+
134.9633964 +
|
|
106
|
+
477198.8675055 * T +
|
|
107
|
+
0.0087414 * T * T +
|
|
108
|
+
(T * T * T) / 69699 -
|
|
109
|
+
(T * T * T * T) / 14712000;
|
|
110
|
+
|
|
111
|
+
return { D: norm360(D), M: norm360(M), Mp: norm360(Mp) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// True Sun-Moon elongation (degrees) at Julian Day jd. This is the geocentric
|
|
115
|
+
// angular distance Moon-from-Sun measured in the direction of the Moon's
|
|
116
|
+
// motion: 0° = new, 90° = first quarter, 180° = full, 270° = last quarter.
|
|
117
|
+
// It is 180° minus the Meeus phase angle (48.4).
|
|
118
|
+
function elongationAtJD(jd) {
|
|
119
|
+
const { D, M, Mp } = fundamentalArguments(jd);
|
|
120
|
+
// Meeus 48.4 gives the phase angle i; elongation = 180 - i, so the sign of
|
|
121
|
+
// every periodic term flips relative to the i-formula.
|
|
122
|
+
const elong =
|
|
123
|
+
D +
|
|
124
|
+
6.289 * Math.sin(rad(Mp)) -
|
|
125
|
+
2.100 * Math.sin(rad(M)) +
|
|
126
|
+
1.274 * Math.sin(rad(2 * D - Mp)) +
|
|
127
|
+
0.658 * Math.sin(rad(2 * D)) +
|
|
128
|
+
0.214 * Math.sin(rad(2 * Mp)) +
|
|
129
|
+
0.110 * Math.sin(rad(D));
|
|
130
|
+
return norm360(elong);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Public API
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* moonPhaseAngle(date) → the Sun-Moon elongation in degrees, in [0, 360).
|
|
139
|
+
* 0° = New, 90° = First Quarter, 180° = Full, 270° = Last Quarter. This is the
|
|
140
|
+
* "age angle" that drives phaseName() and moonAge().
|
|
141
|
+
*/
|
|
142
|
+
function moonPhaseAngle(date) {
|
|
143
|
+
return elongationAtJD(julianDay(date));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* illuminatedFraction(date) → fraction of the Moon's disk that is lit, [0, 1].
|
|
148
|
+
* 0 at new moon, 1 at full moon, ~0.5 at the quarters. Uses k = (1 + cos i)/2
|
|
149
|
+
* with i the phase angle = 180° - elongation (Meeus 48.1).
|
|
150
|
+
*/
|
|
151
|
+
function illuminatedFraction(date) {
|
|
152
|
+
const elong = elongationAtJD(julianDay(date));
|
|
153
|
+
const i = 180 - elong; // phase angle in degrees
|
|
154
|
+
const k = (1 + Math.cos(rad(i))) / 2;
|
|
155
|
+
// Guard against tiny floating-point excursions outside [0, 1].
|
|
156
|
+
if (k < 0) return 0;
|
|
157
|
+
if (k > 1) return 1;
|
|
158
|
+
return k;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* moonAge(date) → days since the last new moon (synodic age), in [0, ~29.53).
|
|
163
|
+
* Derived from the true elongation via the mean synodic month, so it is good to
|
|
164
|
+
* a fraction of a day (see ACCURACY note above), not to the minute.
|
|
165
|
+
*/
|
|
166
|
+
function moonAge(date) {
|
|
167
|
+
const elong = elongationAtJD(julianDay(date));
|
|
168
|
+
return (elong / 360) * SYNODIC_MONTH;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// The 8 canonical phases, in elongation order starting at New. Each occupies a
|
|
172
|
+
// 45°-wide bin centred on its defining angle (New at 0°, First Quarter at 90°,
|
|
173
|
+
// Full at 180°, Last Quarter at 270°). The narrow "instant" phases (New, the
|
|
174
|
+
// quarters, Full) are the bins that straddle those exact angles.
|
|
175
|
+
const PHASE_NAMES = [
|
|
176
|
+
'New',
|
|
177
|
+
'Waxing Crescent',
|
|
178
|
+
'First Quarter',
|
|
179
|
+
'Waxing Gibbous',
|
|
180
|
+
'Full',
|
|
181
|
+
'Waning Gibbous',
|
|
182
|
+
'Last Quarter',
|
|
183
|
+
'Waning Crescent',
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* phaseName(date) → one of the 8 canonical phase names (see PHASE_NAMES).
|
|
188
|
+
* Binned by elongation into 45° sectors, so the boundaries between, e.g.,
|
|
189
|
+
* "Waxing Crescent" and "First Quarter" fall at 67.5°, 112.5°, etc.
|
|
190
|
+
*/
|
|
191
|
+
function phaseName(date) {
|
|
192
|
+
const elong = elongationAtJD(julianDay(date));
|
|
193
|
+
// Shift by 22.5° so the New bin is centred on 0° (i.e. 337.5°..360°..22.5°).
|
|
194
|
+
const index = Math.floor(norm360(elong + 22.5) / 45) % 8;
|
|
195
|
+
return PHASE_NAMES[index];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* lunation(date) → the Brown Lunation Number of the lunation in progress on the
|
|
200
|
+
* given date (integer). Brown lunation 1 began at the new moon of 1923-01-17;
|
|
201
|
+
* the new moon of 2000-01-06 is number 953. Accurate to the correct lunation
|
|
202
|
+
* for any date within a few centuries of J2000.
|
|
203
|
+
*/
|
|
204
|
+
function lunation(date) {
|
|
205
|
+
const jd = julianDay(date);
|
|
206
|
+
// Whole synodic months elapsed since the 2000-01-06 new moon. floor() gives
|
|
207
|
+
// the lunation currently in progress (the count increments at each new moon).
|
|
208
|
+
const k = Math.floor((jd - NEWMOON_2000_JD) / SYNODIC_MONTH);
|
|
209
|
+
return k + BROWN_LUNATION_OFFSET;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
moonPhaseAngle,
|
|
214
|
+
illuminatedFraction,
|
|
215
|
+
moonAge,
|
|
216
|
+
phaseName,
|
|
217
|
+
lunation,
|
|
218
|
+
// constants exposed for advanced use / testing
|
|
219
|
+
SYNODIC_MONTH,
|
|
220
|
+
// internals exposed for testing
|
|
221
|
+
_julianDay: julianDay,
|
|
222
|
+
_elongationAtJD: elongationAtJD,
|
|
223
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@verifyhash/moon-phase",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-dependency lunar phase & illumination math (simplified Meeus method): phase angle, illuminated fraction, synodic age, canonical phase name, and Brown lunation number for any UTC instant.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node test/moon.test.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"moon",
|
|
12
|
+
"lunar",
|
|
13
|
+
"moon-phase",
|
|
14
|
+
"illuminated-fraction",
|
|
15
|
+
"moon-age",
|
|
16
|
+
"synodic",
|
|
17
|
+
"lunation",
|
|
18
|
+
"meeus",
|
|
19
|
+
"astronomy",
|
|
20
|
+
"zero-dependency"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"files": [
|
|
24
|
+
"index.js",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/verifyhash/libs.git",
|
|
33
|
+
"directory": "moon-phase"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/verifyhash/libs/tree/main/moon-phase#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/verifyhash/libs/issues"
|
|
38
|
+
}
|
|
39
|
+
}
|