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