1 #include "moment.h"
2 #include "dt_core.h"
3 #include "dt_accessor.h"
4 #include "dt_arithmetic.h"
5 #include "dt_util.h"
6 #include "dt_length.h"
7 #include "dt_easter.h"
8 
9 static const int32_t kPow10[10] = {
10     1,
11     10,
12     100,
13     1000,
14     10000,
15     100000,
16     1000000,
17     10000000,
18     100000000,
19     1000000000,
20 };
21 
22 static void
THX_moment_check_self(pTHX_ const moment_t * mt)23 THX_moment_check_self(pTHX_ const moment_t *mt) {
24     if (mt->sec < MIN_RANGE || mt->sec > MAX_RANGE)
25         croak("Time::Moment is out of range");
26 }
27 
28 static moment_t
THX_moment_from_local(pTHX_ int64_t sec,IV nsec,IV offset)29 THX_moment_from_local(pTHX_ int64_t sec, IV nsec, IV offset) {
30     moment_t r;
31 
32     r.sec    = sec;
33     r.nsec   = (int32_t)nsec;
34     r.offset = (int32_t)offset;
35     THX_moment_check_self(aTHX_ &r);
36     return r;
37 }
38 
39 static moment_t
THX_moment_from_instant(pTHX_ int64_t sec,IV nsec,IV offset)40 THX_moment_from_instant(pTHX_ int64_t sec, IV nsec, IV offset) {
41     moment_t r;
42 
43     r.sec    = sec + offset * 60;
44     r.nsec   = (int32_t)nsec;
45     r.offset = (int32_t)offset;
46     THX_moment_check_self(aTHX_ &r);
47     return r;
48 }
49 
50 int64_t
moment_instant_rd_seconds(const moment_t * mt)51 moment_instant_rd_seconds(const moment_t *mt) {
52     return mt->sec - mt->offset * 60;
53 }
54 
55 int
moment_instant_rd(const moment_t * mt)56 moment_instant_rd(const moment_t *mt) {
57     return (int)(moment_instant_rd_seconds(mt) / SECS_PER_DAY);
58 }
59 
60 void
moment_to_instant_rd_values(const moment_t * mt,IV * rdn,IV * sod,IV * nos)61 moment_to_instant_rd_values(const moment_t *mt, IV *rdn, IV *sod, IV *nos) {
62     const int64_t sec = moment_instant_rd_seconds(mt);
63     *rdn = (IV)(sec / SECS_PER_DAY);
64     *sod = (IV)(sec % SECS_PER_DAY);
65     *nos = (IV)mt->nsec;
66 }
67 
68 int64_t
moment_local_rd_seconds(const moment_t * mt)69 moment_local_rd_seconds(const moment_t *mt) {
70     return mt->sec;
71 }
72 
73 int
moment_local_rd(const moment_t * mt)74 moment_local_rd(const moment_t *mt) {
75     return (int)(moment_local_rd_seconds(mt) / SECS_PER_DAY);
76 }
77 
78 dt_t
moment_local_dt(const moment_t * mt)79 moment_local_dt(const moment_t *mt) {
80     return dt_from_rdn(moment_local_rd(mt));
81 }
82 
83 void
moment_to_local_rd_values(const moment_t * mt,IV * rdn,IV * sod,IV * nos)84 moment_to_local_rd_values(const moment_t *mt, IV *rdn, IV *sod, IV *nos) {
85     const int64_t sec = moment_local_rd_seconds(mt);
86     *rdn = (IV)(sec / SECS_PER_DAY);
87     *sod = (IV)(sec % SECS_PER_DAY);
88     *nos = (IV)mt->nsec;
89 }
90 
91 static void
THX_check_year(pTHX_ int64_t v)92 THX_check_year(pTHX_ int64_t v) {
93     if (v < 1 || v > 9999)
94         croak("Parameter 'year' is out of the range [1, 9999]");
95 }
96 
97 static void
THX_check_quarter(pTHX_ int64_t v)98 THX_check_quarter(pTHX_ int64_t v) {
99     if (v < 1 || v > 4)
100         croak("Parameter 'quarter' is out of the range [1, 4]");
101 }
102 
103 static void
THX_check_month(pTHX_ int64_t v)104 THX_check_month(pTHX_ int64_t v) {
105     if (v < 1 || v > 12)
106         croak("Parameter 'month' is out of the range [1, 12]");
107 }
108 
109 static void
THX_check_week(pTHX_ int64_t v)110 THX_check_week(pTHX_ int64_t v) {
111     if (v < 1 || v > 53)
112         croak("Parameter 'week' is out of the range [1, 53]");
113 }
114 
115 static void
THX_check_day_of_year(pTHX_ int64_t v)116 THX_check_day_of_year(pTHX_ int64_t v) {
117     if (v < 1 || v > 366)
118         croak("Parameter 'day' is out of the range [1, 366]");
119 }
120 
121 static void
THX_check_day_of_quarter(pTHX_ int64_t v)122 THX_check_day_of_quarter(pTHX_ int64_t v) {
123     if (v < 1 || v > 92)
124         croak("Parameter 'day' is out of the range [1, 92]");
125 }
126 
127 static void
THX_check_day_of_month(pTHX_ int64_t v)128 THX_check_day_of_month(pTHX_ int64_t v) {
129     if (v < 1 || v > 31)
130         croak("Parameter 'day' is out of the range [1, 31]");
131 }
132 
133 static void
THX_check_day_of_week(pTHX_ int64_t v)134 THX_check_day_of_week(pTHX_ int64_t v) {
135     if (v < 1 || v > 7)
136         croak("Parameter 'day' is out of the range [1, 7]");
137 }
138 
139 static void
THX_check_hour(pTHX_ int64_t v)140 THX_check_hour(pTHX_ int64_t v) {
141     if (v < 0 || v > 23)
142         croak("Parameter 'hour' is out of the range [1, 23]");
143 }
144 
145 static void
THX_check_minute(pTHX_ int64_t v)146 THX_check_minute(pTHX_ int64_t v) {
147     if (v < 0 || v > 59)
148         croak("Parameter 'minute' is out of the range [1, 59]");
149 }
150 
151 static void
THX_check_minute_of_day(pTHX_ int64_t v)152 THX_check_minute_of_day(pTHX_ int64_t v) {
153     if (v < 0 || v > 1439)
154         croak("Parameter 'minute' is out of the range [1, 1439]");
155 }
156 
157 static void
THX_check_second(pTHX_ int64_t v)158 THX_check_second(pTHX_ int64_t v) {
159     if (v < 0 || v > 59)
160         croak("Parameter 'second' is out of the range [1, 59]");
161 }
162 
163 static void
THX_check_second_of_day(pTHX_ int64_t v)164 THX_check_second_of_day(pTHX_ int64_t v) {
165     if (v < 0 || v > 86399)
166         croak("Parameter 'second' is out of the range [0, 86_399]");
167 }
168 
169 static void
THX_check_millisecond(pTHX_ int64_t v)170 THX_check_millisecond(pTHX_ int64_t v) {
171     if (v < 0 || v > 999)
172         croak("Parameter 'millisecond' is out of the range [0, 999]");
173 }
174 
175 static void
THX_check_microsecond(pTHX_ int64_t v)176 THX_check_microsecond(pTHX_ int64_t v) {
177     if (v < 0 || v > 999999)
178         croak("Parameter 'microsecond' is out of the range [0, 999_999]");
179 }
180 
181 static void
THX_check_nanosecond(pTHX_ int64_t v)182 THX_check_nanosecond(pTHX_ int64_t v) {
183     if (v < 0 || v > 999999999)
184         croak("Parameter 'nanosecond' is out of the range [0, 999_999_999]");
185 }
186 
187 static void
THX_check_offset(pTHX_ int64_t v)188 THX_check_offset(pTHX_ int64_t v) {
189     if (v < -1080 || v > 1080)
190         croak("Parameter 'offset' is out of the range [-1080, 1080]");
191 }
192 
193 static void
THX_check_epoch_seconds(pTHX_ int64_t v)194 THX_check_epoch_seconds(pTHX_ int64_t v) {
195     if (!VALID_EPOCH_SEC(v))
196         croak("Parameter 'seconds' is out of range");
197 }
198 
199 static void
THX_check_rata_die_day(pTHX_ int64_t v)200 THX_check_rata_die_day(pTHX_ int64_t v) {
201     if (v < MIN_RATA_DIE_DAY || v > MAX_RATA_DIE_DAY)
202         croak("Parameter 'rdn' is out of range");
203 }
204 
205 static void
THX_check_unit_years(pTHX_ int64_t v)206 THX_check_unit_years(pTHX_ int64_t v) {
207     if (v < MIN_UNIT_YEARS || v > MAX_UNIT_YEARS)
208         croak("Parameter 'years' is out of range");
209 }
210 
211 static void
THX_check_unit_months(pTHX_ int64_t v)212 THX_check_unit_months(pTHX_ int64_t v) {
213     if (v < MIN_UNIT_MONTHS || v > MAX_UNIT_MONTHS)
214         croak("Parameter 'months' is out of range");
215 }
216 
217 static void
THX_check_unit_weeks(pTHX_ int64_t v)218 THX_check_unit_weeks(pTHX_ int64_t v) {
219     if (v < MIN_UNIT_WEEKS || v > MAX_UNIT_WEEKS)
220         croak("Parameter 'weeks' is out of range");
221 }
222 
223 static void
THX_check_unit_days(pTHX_ int64_t v)224 THX_check_unit_days(pTHX_ int64_t v) {
225     if (v < MIN_UNIT_DAYS || v > MAX_UNIT_DAYS)
226         croak("Parameter 'days' is out of range");
227 }
228 
229 static void
THX_check_unit_hours(pTHX_ int64_t v)230 THX_check_unit_hours(pTHX_ int64_t v) {
231     if (v < MIN_UNIT_HOURS || v > MAX_UNIT_HOURS)
232         croak("Parameter 'hours' is out of range");
233 }
234 
235 static void
THX_check_unit_minutes(pTHX_ int64_t v)236 THX_check_unit_minutes(pTHX_ int64_t v) {
237     if (v < MIN_UNIT_MINUTES || v > MAX_UNIT_MINUTES)
238         croak("Parameter 'minutes' is out of range");
239 }
240 
241 static void
THX_check_unit_seconds(pTHX_ int64_t v)242 THX_check_unit_seconds(pTHX_ int64_t v) {
243     if (v < MIN_UNIT_SECONDS || v > MAX_UNIT_SECONDS)
244         croak("Parameter 'seconds' is out of range");
245 }
246 
247 static void
THX_check_unit_milliseconds(pTHX_ int64_t v)248 THX_check_unit_milliseconds(pTHX_ int64_t v) {
249     if (v < MIN_UNIT_MILLIS || v > MAX_UNIT_MILLIS)
250         croak("Parameter 'milliseconds' is out of range");
251 }
252 
253 static void
THX_check_unit_microseconds(pTHX_ int64_t v)254 THX_check_unit_microseconds(pTHX_ int64_t v) {
255     if (v < MIN_UNIT_MICROS || v > MAX_UNIT_MICROS)
256         croak("Parameter 'microseconds' is out of range");
257 }
258 
259 moment_t
THX_moment_from_epoch(pTHX_ int64_t sec,IV nsec,IV offset)260 THX_moment_from_epoch(pTHX_ int64_t sec, IV nsec, IV offset) {
261 
262     THX_check_epoch_seconds(aTHX_ sec);
263     THX_check_nanosecond(aTHX_ nsec);
264     THX_check_offset(aTHX_ offset);
265 
266     sec += UNIX_EPOCH;
267     return THX_moment_from_instant(aTHX_ sec, nsec, offset);
268 }
269 
270 moment_t
THX_moment_from_epoch_nv(pTHX_ NV sec,IV precision)271 THX_moment_from_epoch_nv(pTHX_ NV sec, IV precision) {
272     static const NV SEC_MIN = -62135596801.0; /*  0000-12-31T23:59:59Z */
273     static const NV SEC_MAX = 253402300800.0; /* 10000-01-01T00:00:00Z */
274     NV s, f, n, denom;
275 
276     if (precision < 0 || precision > 9)
277         croak("Parameter 'precision' is out of the range [0, 9]");
278 
279     if (!(sec > SEC_MIN && sec < SEC_MAX))
280         croak("Parameter 'seconds' is out of range");
281 
282     f = n = Perl_fmod(sec, 1.0);
283     s = Perl_floor(sec - f);
284     if (n < 0)
285         n += 1.0;
286     s = s + Perl_floor(f - n);
287     denom = Perl_pow(10.0, (NV)precision);
288     n = (Perl_floor(n * denom + 0.5) / denom) * 1E9;
289     return THX_moment_from_epoch(aTHX_ (int64_t)s, (IV)(n + 0.5), 0);
290 }
291 
292 static int
THX_moment_from_sd(pTHX_ NV sd,NV epoch,IV precision,int64_t * sec,int32_t * nsec)293 THX_moment_from_sd(pTHX_ NV sd, NV epoch, IV precision, int64_t *sec, int32_t *nsec) {
294     static const NV SD_MIN = -146097 * 50;
295     static const NV SD_MAX =  146097 * 50;
296     NV d1, d2, f1, f2, f, d, s, denom;
297 
298     if (precision < 0 || precision > 9)
299         croak("Parameter 'precision' is out of the range [0, 9]");
300 
301     if (!(sd > SD_MIN && sd < SD_MAX))
302         return -1;
303 
304     if (!(epoch > SD_MIN && epoch < SD_MAX))
305         croak("Parameter 'epoch' is out of range");
306 
307     if (sd >= epoch) {
308         d1 = sd;
309         d2 = epoch;
310     }
311     else {
312         d1 = epoch;
313         d2 = sd;
314     }
315 
316     f1 = Perl_fmod(d1, 1.0);
317     f2 = Perl_fmod(d2, 1.0);
318     d1 = Perl_floor(d1 - f1);
319     d2 = Perl_floor(d2 - f2);
320 
321     f = Perl_fmod(f1 + f2, 1.0);
322     if (f < 0.0)
323         f += 1.0;
324 
325     d = d1 + d2 + Perl_floor(f1 + f2 - f);
326     f *= 86400;
327     s = Perl_floor(f);
328 
329     if (d < 1 || d > 3652059)
330         return -2;
331 
332     denom = Perl_pow(10.0, (NV)precision);
333     f = (Perl_floor((f - s) * denom + 0.5) / denom) * 1E9;
334 
335     *sec = (int64_t)d * 86400 + (int32_t)s;
336     *nsec = (int32_t)(f + 0.5);
337 
338     if (*nsec >= NANOS_PER_SEC) {
339         *nsec -= NANOS_PER_SEC;
340         *sec += 1;
341     }
342     return 0;
343 }
344 
345 moment_t
THX_moment_from_rd(pTHX_ NV jd,NV epoch,IV precision,IV offset)346 THX_moment_from_rd(pTHX_ NV jd, NV epoch, IV precision, IV offset) {
347     int64_t sec;
348     int32_t nsec;
349     int r;
350 
351     THX_check_offset(aTHX_ offset);
352 
353     r = THX_moment_from_sd(aTHX_ jd, epoch, precision, &sec, &nsec);
354     if (r < 0) {
355         if (r == -1)
356             croak("Parameter 'rd' is out of range");
357         else
358             croak("Rata Die is out of range");
359     }
360     return THX_moment_from_local(aTHX_ sec, nsec, offset);
361 }
362 
363 moment_t
THX_moment_from_jd(pTHX_ NV jd,NV epoch,IV precision)364 THX_moment_from_jd(pTHX_ NV jd, NV epoch, IV precision) {
365     int64_t sec;
366     int32_t nsec;
367     int r;
368 
369     r = THX_moment_from_sd(aTHX_ jd, epoch, precision, &sec, &nsec);
370     if (r < 0) {
371         if (r == -1)
372             croak("Parameter 'jd' is out of range");
373         else
374             croak("Julian date is out of range");
375     }
376 
377     return THX_moment_from_instant(aTHX_ sec, nsec, 0);
378 }
379 
380 moment_t
THX_moment_from_mjd(pTHX_ NV jd,NV epoch,IV precision)381 THX_moment_from_mjd(pTHX_ NV jd, NV epoch, IV precision) {
382     int64_t sec;
383     int32_t nsec;
384     int r;
385 
386     r = THX_moment_from_sd(aTHX_ jd, epoch, precision, &sec, &nsec);
387     if (r < 0) {
388         if (r == -1)
389             croak("Parameter 'mjd' is out of range");
390         else
391             croak("Modified Julian date is out of range");
392     }
393 
394     return THX_moment_from_instant(aTHX_ sec, nsec, 0);
395 }
396 
397 moment_t
THX_moment_new(pTHX_ IV Y,IV M,IV D,IV h,IV m,IV s,IV nsec,IV offset)398 THX_moment_new(pTHX_ IV Y, IV M, IV D, IV h, IV m, IV s, IV nsec, IV offset) {
399     int64_t rdn, sec;
400 
401     THX_check_year(aTHX_ Y);
402     THX_check_month(aTHX_ M);
403     THX_check_day_of_month(aTHX_ D);
404     if (D > 28) {
405         int dim = dt_days_in_month((int)Y, (int)M);
406         if (D > dim)
407             croak("Parameter 'day' is out of the range [1, %d]", dim);
408     }
409     THX_check_hour(aTHX_ h);
410     THX_check_minute(aTHX_ m);
411     THX_check_second(aTHX_ s);
412     THX_check_nanosecond(aTHX_ nsec);
413     THX_check_offset(aTHX_ offset);
414 
415     rdn = dt_rdn(dt_from_ymd((int)Y, (int)M, (int)D));
416     sec = ((rdn * 24 + h) * 60 + m) * 60 + s;
417     return THX_moment_from_local(aTHX_ sec, nsec, offset);
418 }
419 
420 static moment_t
THX_moment_with_local_dt(pTHX_ const moment_t * mt,const dt_t dt)421 THX_moment_with_local_dt(pTHX_ const moment_t *mt, const dt_t dt) {
422     int64_t sec;
423 
424     sec = (int64_t)dt_rdn(dt) * 86400 + moment_second_of_day(mt);
425     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
426 }
427 
428 static moment_t
THX_moment_with_ymd(pTHX_ const moment_t * mt,int y,int m,int d)429 THX_moment_with_ymd(pTHX_ const moment_t *mt, int y, int m, int d) {
430 
431     if (d > 28) {
432         int dim = dt_days_in_month(y, m);
433         if (d > dim)
434             d = dim;
435     }
436     return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, m, d));
437 }
438 
439 static moment_t
THX_moment_with_year(pTHX_ const moment_t * mt,int64_t v)440 THX_moment_with_year(pTHX_ const moment_t *mt, int64_t v) {
441     int m, d;
442 
443     THX_check_year(aTHX_ v);
444     dt_to_ymd(moment_local_dt(mt), NULL, &m, &d);
445     return THX_moment_with_ymd(aTHX_ mt, (int)v, m, d);
446 }
447 
448 static moment_t
THX_moment_with_quarter(pTHX_ const moment_t * mt,int64_t v)449 THX_moment_with_quarter(pTHX_ const moment_t *mt, int64_t v) {
450     int y, m, d;
451 
452     THX_check_quarter(aTHX_ v);
453     dt_to_ymd(moment_local_dt(mt), &y, &m, &d);
454     m = 1 + 3 * ((int)v - 1) + (m - 1) % 3;
455     return THX_moment_with_ymd(aTHX_ mt, y, m, d);
456 }
457 
458 static moment_t
THX_moment_with_month(pTHX_ const moment_t * mt,int64_t v)459 THX_moment_with_month(pTHX_ const moment_t *mt, int64_t v) {
460     int y, d;
461 
462     THX_check_month(aTHX_ v);
463     dt_to_ymd(moment_local_dt(mt), &y, NULL, &d);
464     return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, (int)v, d));
465 }
466 
467 static moment_t
THX_moment_with_week(pTHX_ const moment_t * mt,int64_t v)468 THX_moment_with_week(pTHX_ const moment_t *mt, int64_t v) {
469     int y, w, d;
470 
471     THX_check_week(aTHX_ v);
472     dt_to_ywd(moment_local_dt(mt), &y, NULL, &d);
473     w = (int)v;
474     if (w > 52) {
475         int wiy = dt_weeks_in_year(y);
476         if (w > wiy)
477             croak("Parameter 'week' is out of the range [1, %d]", wiy);
478     }
479     return THX_moment_with_local_dt(aTHX_ mt, dt_from_ywd(y, w, d));
480 }
481 
482 static moment_t
THX_moment_with_day_of_month(pTHX_ const moment_t * mt,int64_t v)483 THX_moment_with_day_of_month(pTHX_ const moment_t *mt, int64_t v) {
484     int y, m, d;
485 
486     THX_check_day_of_month(aTHX_ v);
487     dt_to_ymd(moment_local_dt(mt), &y, &m, NULL);
488     d = (int)v;
489     if (d > 28) {
490         int dim = dt_days_in_month(y, m);
491         if (d > dim)
492             croak("Parameter 'day' is out of the range [1, %d]", dim);
493     }
494     return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, m, d));
495 }
496 
497 static moment_t
THX_moment_with_day_of_quarter(pTHX_ const moment_t * mt,int64_t v)498 THX_moment_with_day_of_quarter(pTHX_ const moment_t *mt, int64_t v) {
499     int y, q, d;
500 
501     THX_check_day_of_quarter(aTHX_ v);
502     dt_to_yqd(moment_local_dt(mt), &y, &q, NULL);
503     d = (int)v;
504     if (d > 90) {
505         int diq = dt_days_in_quarter(y, q);
506         if (d > diq)
507             croak("Parameter 'day' is out of the range [1, %d]", diq);
508     }
509     return THX_moment_with_local_dt(aTHX_ mt, dt_from_yqd(y, q, d));
510 }
511 
512 static moment_t
THX_moment_with_day_of_year(pTHX_ const moment_t * mt,int64_t v)513 THX_moment_with_day_of_year(pTHX_ const moment_t *mt, int64_t v) {
514     int y, d;
515 
516     THX_check_day_of_year(aTHX_ v);
517     dt_to_yd(moment_local_dt(mt), &y, NULL);
518     d = (int)v;
519     if (d > 365) {
520         int diy = dt_days_in_year(y);
521         if (v > diy)
522             croak("Parameter 'day' is out of the range [1, %d]", diy);
523     }
524     return THX_moment_with_local_dt(aTHX_ mt, dt_from_yd(y, d));
525 }
526 
527 static moment_t
THX_moment_with_day_of_week(pTHX_ const moment_t * mt,int64_t v)528 THX_moment_with_day_of_week(pTHX_ const moment_t *mt, int64_t v) {
529     dt_t dt;
530 
531     THX_check_day_of_week(aTHX_ v);
532     dt = moment_local_dt(mt);
533     return THX_moment_with_local_dt(aTHX_ mt, dt - (dt_dow(dt) - v));
534 }
535 
536 static moment_t
THX_moment_with_rata_die_day(pTHX_ const moment_t * mt,int64_t v)537 THX_moment_with_rata_die_day(pTHX_ const moment_t *mt, int64_t v) {
538     dt_t dt;
539 
540     THX_check_rata_die_day(aTHX_ v);
541     dt = dt_from_rdn((int)v);
542     return THX_moment_with_local_dt(aTHX_ mt, dt);
543 }
544 
545 static moment_t
THX_moment_with_hour(pTHX_ const moment_t * mt,int64_t v)546 THX_moment_with_hour(pTHX_ const moment_t *mt, int64_t v) {
547     int64_t sec;
548 
549     THX_check_hour(aTHX_ v);
550     sec = moment_local_rd_seconds(mt) + (v - moment_hour(mt)) * 3600;
551     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
552 }
553 
554 static moment_t
THX_moment_with_minute(pTHX_ const moment_t * mt,int64_t v)555 THX_moment_with_minute(pTHX_ const moment_t *mt, int64_t v) {
556     int64_t sec;
557 
558     THX_check_minute(aTHX_ v);
559     sec = moment_local_rd_seconds(mt) + (v - moment_minute(mt)) * 60;
560     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
561 }
562 
563 static moment_t
THX_moment_with_minute_of_day(pTHX_ const moment_t * mt,int64_t v)564 THX_moment_with_minute_of_day(pTHX_ const moment_t *mt, int64_t v) {
565     int64_t sec;
566 
567     THX_check_minute_of_day(aTHX_ v);
568     sec = moment_local_rd_seconds(mt) + (v - moment_minute_of_day(mt)) * 60;
569     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
570 }
571 
572 static moment_t
THX_moment_with_second(pTHX_ const moment_t * mt,int64_t v)573 THX_moment_with_second(pTHX_ const moment_t *mt, int64_t v) {
574     int64_t sec;
575 
576     THX_check_second(aTHX_ v);
577     sec = moment_local_rd_seconds(mt) + (v - moment_second(mt));
578     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
579 }
580 
581 static moment_t
THX_moment_with_second_of_day(pTHX_ const moment_t * mt,int64_t v)582 THX_moment_with_second_of_day(pTHX_ const moment_t *mt, int64_t v) {
583     int64_t sec;
584 
585     THX_check_second_of_day(aTHX_ v);
586     sec = moment_local_rd_seconds(mt) + (v - moment_second_of_day(mt));
587     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
588 }
589 
590 static moment_t
THX_moment_with_millisecond(pTHX_ const moment_t * mt,int64_t v)591 THX_moment_with_millisecond(pTHX_ const moment_t *mt, int64_t v) {
592     int64_t sec;
593 
594     THX_check_millisecond(aTHX_ v);
595     sec = moment_local_rd_seconds(mt);
596     return THX_moment_from_local(aTHX_ sec, v * 1000000, mt->offset);
597 }
598 
599 static moment_t
THX_moment_with_microsecond(pTHX_ const moment_t * mt,int64_t v)600 THX_moment_with_microsecond(pTHX_ const moment_t *mt, int64_t v) {
601     int64_t sec;
602 
603     THX_check_microsecond(aTHX_ v);
604     sec = moment_local_rd_seconds(mt);
605     return THX_moment_from_local(aTHX_ sec, v * 1000, mt->offset);
606 }
607 
608 static moment_t
THX_moment_with_nanosecond(pTHX_ const moment_t * mt,int64_t v)609 THX_moment_with_nanosecond(pTHX_ const moment_t *mt, int64_t v) {
610     int64_t sec;
611 
612     THX_check_nanosecond(aTHX_ v);
613     sec = moment_local_rd_seconds(mt);
614     return THX_moment_from_local(aTHX_ sec, v, mt->offset);
615 }
616 
617 static moment_t
THX_moment_with_nanosecond_of_day(pTHX_ const moment_t * mt,int64_t v)618 THX_moment_with_nanosecond_of_day(pTHX_ const moment_t *mt, int64_t v) {
619     int64_t sec;
620     int32_t nsec;
621 
622     if (v < 0 || v > INT64_C(86400000000000))
623         croak("Paramteter 'nanosecond' is out of the range [0, 86_400_000_000_000]");
624 
625     sec = moment_local_rd_seconds(mt) + v / NANOS_PER_SEC - moment_second_of_day(mt);
626     nsec = v % NANOS_PER_SEC;
627     return THX_moment_from_local(aTHX_ sec, nsec, mt->offset);
628 }
629 
630 static moment_t
THX_moment_with_microsecond_of_day(pTHX_ const moment_t * mt,int64_t v)631 THX_moment_with_microsecond_of_day(pTHX_ const moment_t *mt, int64_t v) {
632     if (v < 0 || v > INT64_C(86400000000))
633         croak("Paramteter 'microsecond' is out of the range [0, 86_400_000_000]");
634     return THX_moment_with_nanosecond_of_day(aTHX_ mt, v * 1000);
635 }
636 
637 static moment_t
THX_moment_with_millisecond_of_day(pTHX_ const moment_t * mt,int64_t v)638 THX_moment_with_millisecond_of_day(pTHX_ const moment_t *mt, int64_t v) {
639     if (v < 0 || v > INT64_C(86400000))
640         croak("Paramteter 'millisecond' is out of the range [0, 86_400_000]");
641     return THX_moment_with_nanosecond_of_day(aTHX_ mt, v * 1000000);
642 }
643 
644 moment_t
THX_moment_with_field(pTHX_ const moment_t * mt,moment_component_t c,int64_t v)645 THX_moment_with_field(pTHX_ const moment_t *mt, moment_component_t c, int64_t v) {
646     switch (c) {
647         case MOMENT_FIELD_YEAR:
648             return THX_moment_with_year(aTHX_ mt, v);
649         case MOMENT_FIELD_QUARTER_OF_YEAR:
650             return THX_moment_with_quarter(aTHX_ mt, v);
651         case MOMENT_FIELD_MONTH_OF_YEAR:
652             return THX_moment_with_month(aTHX_ mt, v);
653         case MOMENT_FIELD_WEEK_OF_YEAR:
654             return THX_moment_with_week(aTHX_ mt, v);
655         case MOMENT_FIELD_DAY_OF_MONTH:
656             return THX_moment_with_day_of_month(aTHX_ mt, v);
657         case MOMENT_FIELD_DAY_OF_QUARTER:
658             return THX_moment_with_day_of_quarter(aTHX_ mt, v);
659         case MOMENT_FIELD_DAY_OF_YEAR:
660             return THX_moment_with_day_of_year(aTHX_ mt, v);
661         case MOMENT_FIELD_DAY_OF_WEEK:
662             return THX_moment_with_day_of_week(aTHX_ mt, v);
663         case MOMENT_FIELD_HOUR_OF_DAY:
664             return THX_moment_with_hour(aTHX_ mt, v);
665         case MOMENT_FIELD_MINUTE_OF_HOUR:
666             return THX_moment_with_minute(aTHX_ mt, v);
667         case MOMENT_FIELD_MINUTE_OF_DAY:
668             return THX_moment_with_minute_of_day(aTHX_ mt, v);
669         case MOMENT_FIELD_SECOND_OF_MINUTE:
670             return THX_moment_with_second(aTHX_ mt, v);
671         case MOMENT_FIELD_SECOND_OF_DAY:
672             return THX_moment_with_second_of_day(aTHX_ mt, v);
673         case MOMENT_FIELD_MILLI_OF_SECOND:
674             return THX_moment_with_millisecond(aTHX_ mt, v);
675         case MOMENT_FIELD_MILLI_OF_DAY:
676             return THX_moment_with_millisecond_of_day(aTHX_ mt, v);
677         case MOMENT_FIELD_MICRO_OF_SECOND:
678             return THX_moment_with_microsecond(aTHX_ mt, v);
679         case MOMENT_FIELD_MICRO_OF_DAY:
680             return THX_moment_with_microsecond_of_day(aTHX_ mt, v);
681         case MOMENT_FIELD_NANO_OF_SECOND:
682             return THX_moment_with_nanosecond(aTHX_ mt, v);
683         case MOMENT_FIELD_NANO_OF_DAY:
684             return THX_moment_with_nanosecond_of_day(aTHX_ mt, v);
685         case MOMENT_FIELD_PRECISION:
686             return THX_moment_with_precision(aTHX_ mt, v);
687         case MOMENT_FIELD_RATA_DIE_DAY:
688             return THX_moment_with_rata_die_day(aTHX_ mt, v);
689     }
690     croak("panic: THX_moment_with_component() called with unknown component (%d)", (int)c);
691 }
692 
693 static moment_t
THX_moment_plus_months(pTHX_ const moment_t * mt,int64_t v)694 THX_moment_plus_months(pTHX_ const moment_t *mt, int64_t v) {
695     dt_t dt;
696 
697     THX_check_unit_months(aTHX_ v);
698     dt = dt_add_months(moment_local_dt(mt), (int)v, DT_LIMIT);
699     return THX_moment_with_local_dt(aTHX_ mt, dt);
700 }
701 
702 static moment_t
THX_moment_plus_days(pTHX_ const moment_t * mt,int64_t v)703 THX_moment_plus_days(pTHX_ const moment_t *mt, int64_t v) {
704     int64_t sec;
705 
706     THX_check_unit_days(aTHX_ v);
707     sec = moment_local_rd_seconds(mt) + v * 86400;
708     return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset);
709 }
710 
711 static moment_t
THX_moment_plus_seconds(pTHX_ const moment_t * mt,int64_t v)712 THX_moment_plus_seconds(pTHX_ const moment_t *mt, int64_t v) {
713     int64_t sec;
714 
715     THX_check_unit_seconds(aTHX_ v);
716     sec = moment_instant_rd_seconds(mt) + v;
717     return THX_moment_from_instant(aTHX_ sec, mt->nsec, mt->offset);
718 }
719 
720 static moment_t
THX_moment_plus_time(pTHX_ const moment_t * mt,int64_t sec,int64_t nsec,int sign)721 THX_moment_plus_time(pTHX_ const moment_t *mt, int64_t sec, int64_t nsec, int sign) {
722 
723     sec  = sec + (nsec / NANOS_PER_SEC);
724     nsec = nsec % NANOS_PER_SEC;
725 
726     sec  = moment_instant_rd_seconds(mt) + sec * sign;
727     nsec = mt->nsec + nsec * sign;
728 
729     if (nsec < 0) {
730         nsec += NANOS_PER_SEC;
731         sec--;
732     }
733     else if (nsec >= NANOS_PER_SEC) {
734         nsec -= NANOS_PER_SEC;
735         sec++;
736     }
737     return THX_moment_from_instant(aTHX_ sec, (IV)nsec, mt->offset);
738 }
739 
740 moment_t
THX_moment_plus_unit(pTHX_ const moment_t * mt,moment_unit_t u,int64_t v)741 THX_moment_plus_unit(pTHX_ const moment_t *mt, moment_unit_t u, int64_t v) {
742     switch (u) {
743         case MOMENT_UNIT_YEARS:
744             THX_check_unit_years(aTHX_ v);
745             return THX_moment_plus_months(aTHX_ mt, v * 12);
746         case MOMENT_UNIT_MONTHS:
747             THX_check_unit_months(aTHX_ v);
748             return THX_moment_plus_months(aTHX_ mt, v);
749         case MOMENT_UNIT_WEEKS:
750             THX_check_unit_weeks(aTHX_ v);
751             return THX_moment_plus_days(aTHX_ mt, v * 7);
752         case MOMENT_UNIT_DAYS:
753             THX_check_unit_days(aTHX_ v);
754             return THX_moment_plus_days(aTHX_ mt, v);
755         case MOMENT_UNIT_HOURS:
756             THX_check_unit_hours(aTHX_ v);
757             return THX_moment_plus_seconds(aTHX_ mt, v * 3600);
758         case MOMENT_UNIT_MINUTES:
759             THX_check_unit_minutes(aTHX_ v);
760             return THX_moment_plus_seconds(aTHX_ mt, v * 60);
761         case MOMENT_UNIT_SECONDS:
762             THX_check_unit_seconds(aTHX_ v);
763             return THX_moment_plus_seconds(aTHX_ mt, v);
764         case MOMENT_UNIT_MILLIS:
765             THX_check_unit_milliseconds(aTHX_ v);
766             return THX_moment_plus_time(aTHX_ mt, v / 1000, (v % 1000) * 1000000, 1);
767         case MOMENT_UNIT_MICROS:
768             THX_check_unit_microseconds(aTHX_ v);
769             return THX_moment_plus_time(aTHX_ mt, v / 1000000, (v % 1000000) * 1000, 1);
770         case MOMENT_UNIT_NANOS:
771             return THX_moment_plus_time(aTHX_ mt, 0, v, 1);
772     }
773     croak("panic: THX_moment_plus_unit() called with unknown unit (%d)", (int)u);
774 }
775 
776 moment_t
THX_moment_minus_unit(pTHX_ const moment_t * mt,moment_unit_t u,int64_t v)777 THX_moment_minus_unit(pTHX_ const moment_t *mt, moment_unit_t u, int64_t v) {
778     switch (u) {
779         case MOMENT_UNIT_YEARS:
780             THX_check_unit_years(aTHX_ v);
781             return THX_moment_plus_months(aTHX_ mt, -v * 12);
782         case MOMENT_UNIT_MONTHS:
783             THX_check_unit_months(aTHX_ v);
784             return THX_moment_plus_months(aTHX_ mt, -v);
785         case MOMENT_UNIT_WEEKS:
786             THX_check_unit_weeks(aTHX_ v);
787             return THX_moment_plus_days(aTHX_ mt, -v * 7);
788         case MOMENT_UNIT_DAYS:
789             THX_check_unit_days(aTHX_ v);
790             return THX_moment_plus_days(aTHX_ mt, -v);
791         case MOMENT_UNIT_HOURS:
792             THX_check_unit_hours(aTHX_ v);
793             return THX_moment_plus_seconds(aTHX_ mt, -v * 3600);
794         case MOMENT_UNIT_MINUTES:
795             THX_check_unit_minutes(aTHX_ v);
796             return THX_moment_plus_seconds(aTHX_ mt, -v * 60);
797         case MOMENT_UNIT_SECONDS:
798             THX_check_unit_seconds(aTHX_ v);
799             return THX_moment_plus_seconds(aTHX_ mt, -v);
800         case MOMENT_UNIT_MILLIS:
801             THX_check_unit_milliseconds(aTHX_ v);
802             return THX_moment_plus_time(aTHX_ mt, v / 1000, (v % 1000) * 1000000, -1);
803         case MOMENT_UNIT_MICROS:
804             THX_check_unit_microseconds(aTHX_ v);
805             return THX_moment_plus_time(aTHX_ mt, v / 1000000, (v % 1000000) * 1000, -1);
806         case MOMENT_UNIT_NANOS:
807             return THX_moment_plus_time(aTHX_ mt, 0, v, -1);
808     }
809     croak("panic: THX_moment_minus_unit() called with unknown unit (%d)", (int)u);
810 }
811 
812 moment_t
THX_moment_with_offset_same_instant(pTHX_ const moment_t * mt,IV offset)813 THX_moment_with_offset_same_instant(pTHX_ const moment_t *mt, IV offset) {
814     int64_t sec;
815 
816     THX_check_offset(aTHX_ offset);
817     sec = moment_instant_rd_seconds(mt);
818     return THX_moment_from_instant(aTHX_ sec, mt->nsec, offset);
819 }
820 
821 moment_t
THX_moment_with_offset_same_local(pTHX_ const moment_t * mt,IV offset)822 THX_moment_with_offset_same_local(pTHX_ const moment_t *mt, IV offset) {
823     int64_t sec;
824 
825     THX_check_offset(aTHX_ offset);
826     sec = moment_local_rd_seconds(mt);
827     return THX_moment_from_local(aTHX_ sec, mt->nsec, offset);
828 }
829 
830 moment_t
THX_moment_with_precision(pTHX_ const moment_t * mt,int64_t precision)831 THX_moment_with_precision(pTHX_ const moment_t *mt, int64_t precision) {
832     int64_t sec;
833     int32_t nsec;
834 
835     if (precision < -3 || precision > 9)
836         croak("Parameter 'precision' is out of the range [-3, 9]");
837 
838     sec = moment_local_rd_seconds(mt);
839     nsec = mt->nsec;
840     if (precision <= 0) {
841         nsec = 0;
842         switch (precision) {
843             case -1: sec -= sec % 60;       break;
844             case -2: sec -= sec % 3600;     break;
845             case -3: sec -= sec % 86400;    break;
846         }
847     }
848     else {
849         nsec -= nsec % kPow10[9 - precision];
850     }
851     return THX_moment_from_local(aTHX_ sec, nsec, mt->offset);
852 }
853 
854 moment_duration_t
moment_subtract_moment(const moment_t * mt1,const moment_t * mt2)855 moment_subtract_moment(const moment_t *mt1, const moment_t *mt2) {
856     const int64_t s1 = moment_instant_rd_seconds(mt1);
857     const int64_t s2 = moment_instant_rd_seconds(mt2);
858     moment_duration_t d;
859 
860     d.sec = s2 - s1;
861     d.nsec = mt2->nsec - mt1->nsec;
862     if (d.nsec < 0) {
863         d.sec -= 1;
864         d.nsec += NANOS_PER_SEC;
865     }
866     return d;
867 }
868 
869 static int
moment_delta_days(const moment_t * mt1,const moment_t * mt2)870 moment_delta_days(const moment_t *mt1, const moment_t *mt2) {
871     const dt_t dt1 = moment_local_dt(mt1);
872     const dt_t dt2 = moment_local_dt(mt2);
873     return dt2 - dt1;
874 }
875 
876 static int
moment_delta_weeks(const moment_t * mt1,const moment_t * mt2)877 moment_delta_weeks(const moment_t *mt1, const moment_t *mt2) {
878     return moment_delta_days(mt1, mt2) / 7;
879 }
880 
881 static int
moment_delta_months(const moment_t * mt1,const moment_t * mt2)882 moment_delta_months(const moment_t *mt1, const moment_t *mt2) {
883     const dt_t dt1 = moment_local_dt(mt1);
884     const dt_t dt2 = moment_local_dt(mt2);
885     return dt_delta_months(dt1, dt2, true);
886 }
887 
888 static int
moment_delta_years(const moment_t * mt1,const moment_t * mt2)889 moment_delta_years(const moment_t *mt1, const moment_t *mt2) {
890     return moment_delta_months(mt1, mt2) / 12;
891 }
892 
893 static int64_t
THX_moment_delta_hours(pTHX_ const moment_t * mt1,const moment_t * mt2)894 THX_moment_delta_hours(pTHX_ const moment_t *mt1, const moment_t *mt2) {
895     moment_duration_t d;
896     d = moment_subtract_moment(mt1, mt2);
897     return (d.sec / 3600);
898 }
899 
900 static int64_t
THX_moment_delta_minutes(pTHX_ const moment_t * mt1,const moment_t * mt2)901 THX_moment_delta_minutes(pTHX_ const moment_t *mt1, const moment_t *mt2) {
902     moment_duration_t d;
903     d = moment_subtract_moment(mt1, mt2);
904     return (d.sec / 60);
905 }
906 
907 static int64_t
THX_moment_delta_seconds(pTHX_ const moment_t * mt1,const moment_t * mt2)908 THX_moment_delta_seconds(pTHX_ const moment_t *mt1, const moment_t *mt2) {
909     moment_duration_t d;
910     d = moment_subtract_moment(mt1, mt2);
911     return d.sec;
912 }
913 
914 static int64_t
THX_moment_delta_milliseconds(pTHX_ const moment_t * mt1,const moment_t * mt2)915 THX_moment_delta_milliseconds(pTHX_ const moment_t *mt1, const moment_t *mt2) {
916     moment_duration_t d;
917     d = moment_subtract_moment(mt1, mt2);
918     return d.sec * 1000 + (d.nsec / 1000000);
919 }
920 
921 static int64_t
THX_moment_delta_microseconds(pTHX_ const moment_t * mt1,const moment_t * mt2)922 THX_moment_delta_microseconds(pTHX_ const moment_t *mt1, const moment_t *mt2) {
923     moment_duration_t d;
924     d = moment_subtract_moment(mt1, mt2);
925     return d.sec * 1000000 + (d.nsec / 1000);
926 }
927 
928 static int64_t
THX_moment_delta_nanoseconds(pTHX_ const moment_t * mt1,const moment_t * mt2)929 THX_moment_delta_nanoseconds(pTHX_ const moment_t *mt1, const moment_t *mt2) {
930     static const int64_t kMaxSec = INT64_C(9223372035);
931     moment_duration_t d;
932 
933     d = moment_subtract_moment(mt1, mt2);
934     if (d.sec > kMaxSec || d.sec < -kMaxSec)
935         croak("Nanosecond duration is too large to be represented in a 64-bit integer");
936     return d.sec * 1000000000 + d.nsec;
937 }
938 
939 int64_t
THX_moment_delta_unit(pTHX_ const moment_t * mt1,const moment_t * mt2,moment_unit_t u)940 THX_moment_delta_unit(pTHX_ const moment_t *mt1, const moment_t *mt2, moment_unit_t u) {
941     switch (u) {
942         case MOMENT_UNIT_YEARS:
943             return moment_delta_years(mt1, mt2);
944         case MOMENT_UNIT_MONTHS:
945             return moment_delta_months(mt1, mt2);
946         case MOMENT_UNIT_WEEKS:
947             return moment_delta_weeks(mt1, mt2);
948         case MOMENT_UNIT_DAYS:
949             return moment_delta_days(mt1, mt2);
950         case MOMENT_UNIT_HOURS:
951             return THX_moment_delta_hours(aTHX_ mt1, mt2);
952         case MOMENT_UNIT_MINUTES:
953             return THX_moment_delta_minutes(aTHX_ mt1, mt2);
954         case MOMENT_UNIT_SECONDS:
955             return THX_moment_delta_seconds(aTHX_ mt1, mt2);
956         case MOMENT_UNIT_MILLIS:
957             return THX_moment_delta_milliseconds(aTHX_ mt1, mt2);
958         case MOMENT_UNIT_MICROS:
959             return THX_moment_delta_microseconds(aTHX_ mt1, mt2);
960         case MOMENT_UNIT_NANOS:
961             return THX_moment_delta_nanoseconds(aTHX_ mt1, mt2);
962         default:
963             croak("panic: THX_moment_delta_unit() called with unknown unit (%d)", (int)u);
964     }
965 }
966 
967 moment_t
THX_moment_at_utc(pTHX_ const moment_t * mt)968 THX_moment_at_utc(pTHX_ const moment_t *mt) {
969     return THX_moment_with_offset_same_instant(aTHX_ mt, 0);
970 }
971 
972 moment_t
THX_moment_at_midnight(pTHX_ const moment_t * mt)973 THX_moment_at_midnight(pTHX_ const moment_t *mt) {
974     return THX_moment_with_millisecond_of_day(aTHX_ mt, 0);
975 }
976 
977 moment_t
THX_moment_at_noon(pTHX_ const moment_t * mt)978 THX_moment_at_noon(pTHX_ const moment_t *mt) {
979     return THX_moment_with_millisecond_of_day(aTHX_ mt, 12*60*60*1000);
980 }
981 
982 moment_t
THX_moment_at_last_day_of_year(pTHX_ const moment_t * mt)983 THX_moment_at_last_day_of_year(pTHX_ const moment_t *mt) {
984     int y;
985 
986     dt_to_yd(moment_local_dt(mt), &y, NULL);
987     return THX_moment_with_local_dt(aTHX_ mt, dt_from_yd(y + 1, 0));
988 }
989 
990 moment_t
THX_moment_at_last_day_of_quarter(pTHX_ const moment_t * mt)991 THX_moment_at_last_day_of_quarter(pTHX_ const moment_t *mt) {
992     int y, q;
993 
994     dt_to_yqd(moment_local_dt(mt), &y, &q, NULL);
995     return THX_moment_with_local_dt(aTHX_ mt, dt_from_yqd(y, q + 1, 0));
996 }
997 
998 moment_t
THX_moment_at_last_day_of_month(pTHX_ const moment_t * mt)999 THX_moment_at_last_day_of_month(pTHX_ const moment_t *mt) {
1000     int y, m;
1001 
1002     dt_to_ymd(moment_local_dt(mt), &y, &m, NULL);
1003     return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, m + 1, 0));
1004 }
1005 
1006 int
moment_compare_instant(const moment_t * m1,const moment_t * m2)1007 moment_compare_instant(const moment_t *m1, const moment_t *m2) {
1008     const int64_t s1 = moment_instant_rd_seconds(m1);
1009     const int64_t s2 = moment_instant_rd_seconds(m2);
1010     int r;
1011 
1012     r = (s1 > s2) - (s1 < s2);
1013     if (r == 0)
1014         r = (m1->nsec > m2->nsec) - (m1->nsec < m2->nsec);
1015     return r;
1016 }
1017 
1018 int
moment_compare_local(const moment_t * m1,const moment_t * m2)1019 moment_compare_local(const moment_t *m1, const moment_t *m2) {
1020     const int64_t s1 = moment_local_rd_seconds(m1);
1021     const int64_t s2 = moment_local_rd_seconds(m2);
1022     int r;
1023 
1024     r = (s1 > s2) - (s1 < s2);
1025     if (r == 0)
1026         r = (m1->nsec > m2->nsec) - (m1->nsec < m2->nsec);
1027     return r;
1028 }
1029 
1030 int
THX_moment_compare_precision(pTHX_ const moment_t * m1,const moment_t * m2,IV precision)1031 THX_moment_compare_precision(pTHX_ const moment_t *m1, const moment_t *m2, IV precision) {
1032     int64_t n1, n2;
1033     int r;
1034 
1035     if (precision < -3 || precision > 9)
1036         croak("Parameter 'precision' is out of the range [-3, 9]");
1037 
1038     if (precision < 0) {
1039         int32_t n;
1040 
1041         n = 0;
1042         switch (precision) {
1043             case -1: n = 60;    break;
1044             case -2: n = 3600;  break;
1045             case -3: n = 86400; break;
1046         }
1047         n1 = moment_local_rd_seconds(m1);
1048         n2 = moment_local_rd_seconds(m2);
1049         n1 -= n1 % n;
1050         n2 -= n2 % n;
1051         n1 -= m1->offset * 60;
1052         n2 -= m2->offset * 60;
1053         r = (n1 > n2) - (n1 < n2);
1054     }
1055     else {
1056         n1 = moment_instant_rd_seconds(m1);
1057         n2 = moment_instant_rd_seconds(m2);
1058         r = (n1 > n2) - (n1 < n2);
1059         if (r == 0 && precision != 0) {
1060             n1 = m1->nsec - m1->nsec % kPow10[9 - precision];
1061             n2 = m2->nsec - m2->nsec % kPow10[9 - precision];
1062             r = (n1 > n2) - (n1 < n2);
1063         }
1064     }
1065     return r;
1066 }
1067 
1068 bool
moment_equals(const moment_t * m1,const moment_t * m2)1069 moment_equals(const moment_t *m1, const moment_t *m2) {
1070     return memcmp(m1, m2, sizeof(moment_t)) == 0;
1071 }
1072 
1073 int64_t
moment_epoch(const moment_t * mt)1074 moment_epoch(const moment_t *mt) {
1075     return (moment_instant_rd_seconds(mt) - UNIX_EPOCH);
1076 }
1077 
1078 int
moment_year(const moment_t * mt)1079 moment_year(const moment_t *mt) {
1080     return dt_year(moment_local_dt(mt));
1081 }
1082 
1083 int
moment_month(const moment_t * mt)1084 moment_month(const moment_t *mt) {
1085     return dt_month(moment_local_dt(mt));
1086 }
1087 
1088 int
moment_quarter(const moment_t * mt)1089 moment_quarter(const moment_t *mt) {
1090     return dt_quarter(moment_local_dt(mt));
1091 }
1092 
1093 int
moment_week(const moment_t * mt)1094 moment_week(const moment_t *mt) {
1095     return dt_woy(moment_local_dt(mt));
1096 }
1097 
1098 int
moment_day_of_year(const moment_t * mt)1099 moment_day_of_year(const moment_t *mt) {
1100     return dt_doy(moment_local_dt(mt));
1101 }
1102 
1103 int
moment_day_of_quarter(const moment_t * mt)1104 moment_day_of_quarter(const moment_t *mt) {
1105     return dt_doq(moment_local_dt(mt));
1106 }
1107 
1108 int
moment_day_of_month(const moment_t * mt)1109 moment_day_of_month(const moment_t *mt) {
1110     return dt_dom(moment_local_dt(mt));
1111 }
1112 
1113 int
moment_day_of_week(const moment_t * mt)1114 moment_day_of_week(const moment_t *mt) {
1115     return dt_dow(moment_local_dt(mt));
1116 }
1117 
1118 int
moment_hour(const moment_t * mt)1119 moment_hour(const moment_t *mt) {
1120     return (int)((moment_local_rd_seconds(mt) / 3600) % 24);
1121 }
1122 
1123 int
moment_minute(const moment_t * mt)1124 moment_minute(const moment_t *mt) {
1125     return (int)((moment_local_rd_seconds(mt) / 60) % 60);
1126 }
1127 
1128 int
moment_minute_of_day(const moment_t * mt)1129 moment_minute_of_day(const moment_t *mt) {
1130     return (int)((moment_local_rd_seconds(mt) / 60) % 1440);
1131 }
1132 
1133 int
moment_second(const moment_t * mt)1134 moment_second(const moment_t *mt) {
1135     return (int)(moment_local_rd_seconds(mt) % 60);
1136 }
1137 
1138 int
moment_second_of_day(const moment_t * mt)1139 moment_second_of_day(const moment_t *mt) {
1140     return (int)(moment_local_rd_seconds(mt) % 86400);
1141 }
1142 
1143 int
moment_millisecond(const moment_t * mt)1144 moment_millisecond(const moment_t *mt) {
1145     return (mt->nsec / 1000000);
1146 }
1147 
1148 int
moment_millisecond_of_day(const moment_t * mt)1149 moment_millisecond_of_day(const moment_t *mt) {
1150     return moment_second_of_day(mt) * 1000 + moment_millisecond(mt);
1151 }
1152 
1153 int
moment_microsecond(const moment_t * mt)1154 moment_microsecond(const moment_t *mt) {
1155     return (mt->nsec / 1000);
1156 }
1157 
1158 int64_t
moment_microsecond_of_day(const moment_t * mt)1159 moment_microsecond_of_day(const moment_t *mt) {
1160     const int64_t sod = moment_local_rd_seconds(mt) % 86400;
1161     return sod * 1000000 + (mt->nsec / 1000);
1162 }
1163 
1164 int
moment_nanosecond(const moment_t * mt)1165 moment_nanosecond(const moment_t *mt) {
1166     return mt->nsec;
1167 }
1168 
1169 int64_t
moment_nanosecond_of_day(const moment_t * mt)1170 moment_nanosecond_of_day(const moment_t *mt) {
1171     const int64_t sod = moment_local_rd_seconds(mt) % 86400;
1172     return sod * 1000000000 + mt->nsec;
1173 }
1174 
1175 NV
moment_jd(const moment_t * mt)1176 moment_jd(const moment_t *mt) {
1177     return moment_mjd(mt) + 2400000.5;
1178 }
1179 
1180 NV
moment_mjd(const moment_t * mt)1181 moment_mjd(const moment_t *mt) {
1182     const int64_t s = moment_instant_rd_seconds(mt);
1183     const int64_t d = (s / SECS_PER_DAY) - 678576;
1184     const int64_t n = (s % SECS_PER_DAY) * NANOS_PER_SEC + mt->nsec;
1185     return (NV)d + (NV)n * (1E-9/60/60/24);
1186 }
1187 
1188 NV
moment_rd(const moment_t * mt)1189 moment_rd(const moment_t *mt) {
1190     const int64_t s = moment_local_rd_seconds(mt);
1191     const int64_t d = (s / SECS_PER_DAY);
1192     const int64_t n = (s % SECS_PER_DAY) * NANOS_PER_SEC + mt->nsec;
1193     return (NV)d + (NV)n * (1E-9/60/60/24);
1194 }
1195 
1196 int
moment_rata_die_day(const moment_t * mt)1197 moment_rata_die_day(const moment_t *mt) {
1198     return dt_rdn(moment_local_dt(mt));
1199 }
1200 
1201 int
moment_offset(const moment_t * mt)1202 moment_offset(const moment_t *mt) {
1203     return mt->offset;
1204 }
1205 
1206 int
moment_precision(const moment_t * mt)1207 moment_precision(const moment_t *mt) {
1208     int v, i;
1209 
1210     v = mt->nsec;
1211     if (v != 0) {
1212         for (i = 8; i > 0; i--) {
1213             if ((v % kPow10[i]) == 0)
1214                 break;
1215         }
1216         return 9 - i;
1217     }
1218     v = moment_second_of_day(mt);
1219     if (v != 0) {
1220         if      ((v % 3600) == 0) return -2;
1221         else if ((v %   60) == 0) return -1;
1222         else                      return 0;
1223     }
1224     return -3;
1225 }
1226 
1227 int
moment_length_of_year(const moment_t * mt)1228 moment_length_of_year(const moment_t *mt) {
1229     return dt_length_of_year(moment_local_dt(mt));
1230 }
1231 
1232 int
moment_length_of_quarter(const moment_t * mt)1233 moment_length_of_quarter(const moment_t *mt) {
1234     return dt_length_of_quarter(moment_local_dt(mt));
1235 }
1236 
1237 int
moment_length_of_month(const moment_t * mt)1238 moment_length_of_month(const moment_t *mt) {
1239     return dt_length_of_month(moment_local_dt(mt));
1240 }
1241 
1242 int
moment_length_of_week_year(const moment_t * mt)1243 moment_length_of_week_year(const moment_t *mt) {
1244     return dt_length_of_week_year(moment_local_dt(mt));
1245 }
1246 
1247 bool
moment_is_leap_year(const moment_t * mt)1248 moment_is_leap_year(const moment_t *mt) {
1249     return dt_leap_year(moment_year(mt));
1250 }
1251 
1252 int
THX_moment_internal_western_easter(pTHX_ int64_t y)1253 THX_moment_internal_western_easter(pTHX_ int64_t y) {
1254     THX_check_year(aTHX_ y);
1255     return dt_rdn(dt_from_easter((int)y, DT_WESTERN));
1256 }
1257 
1258 int
THX_moment_internal_orthodox_easter(pTHX_ int64_t y)1259 THX_moment_internal_orthodox_easter(pTHX_ int64_t y) {
1260     THX_check_year(aTHX_ y);
1261     return dt_rdn(dt_from_easter((int)y, DT_ORTHODOX));
1262 }
1263 
1264