File Coverage

src/moment.c
Criterion Covered Total %
statement 518 660 78.4
branch 118 238 49.5
condition n/a
subroutine n/a
pod n/a
total 636 898 70.8


line stmt bran cond sub pod time code
1            
2            
3            

perltidy

4            
 
5             #include "moment.h" #include "dt_core.h" #include "dt_accessor.h" #include "dt_arithmetic.h" #include "dt_util.h" #include "dt_length.h" #include "dt_easter.h" static const int32_t kPow10[10] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, }; static void THX_moment_check_self(pTHX_ const moment_t *mt) { if (mt->sec < MIN_RANGE || mt->sec > MAX_RANGE) croak("Time::Moment is out of range"); } static moment_t THX_moment_from_local(pTHX_ int64_t sec, IV nsec, IV offset) { moment_t r; r.sec = sec; r.nsec = (int32_t)nsec; r.offset = (int32_t)offset; THX_moment_check_self(aTHX_ &r); return r; } static moment_t THX_moment_from_instant(pTHX_ int64_t sec, IV nsec, IV offset) { moment_t r; r.sec = sec + offset * 60; r.nsec = (int32_t)nsec; r.offset = (int32_t)offset; THX_moment_check_self(aTHX_ &r); return r; } int64_t moment_instant_rd_seconds(const moment_t *mt) { return mt->sec - mt->offset * 60; } int moment_instant_rd(const moment_t *mt) { return (int)(moment_instant_rd_seconds(mt) / SECS_PER_DAY); } void moment_to_instant_rd_values(const moment_t *mt, IV *rdn, IV *sod, IV *nos) { const int64_t sec = moment_instant_rd_seconds(mt); *rdn = (IV)(sec / SECS_PER_DAY); *sod = (IV)(sec % SECS_PER_DAY); *nos = (IV)mt->nsec; } int64_t moment_local_rd_seconds(const moment_t *mt) { return mt->sec; } int moment_local_rd(const moment_t *mt) { return (int)(moment_local_rd_seconds(mt) / SECS_PER_DAY); } dt_t moment_local_dt(const moment_t *mt) { return dt_from_rdn(moment_local_rd(mt)); } void moment_to_local_rd_values(const moment_t *mt, IV *rdn, IV *sod, IV *nos) { const int64_t sec = moment_local_rd_seconds(mt); *rdn = (IV)(sec / SECS_PER_DAY); *sod = (IV)(sec % SECS_PER_DAY); *nos = (IV)mt->nsec; } static void THX_check_year(pTHX_ int64_t v) { if (v < 1 || v > 9999) croak("Parameter 'year' is out of the range [1, 9999]"); } static void THX_check_quarter(pTHX_ int64_t v) { if (v < 1 || v > 4) croak("Parameter 'quarter' is out of the range [1, 4]"); } static void THX_check_month(pTHX_ int64_t v) { if (v < 1 || v > 12) croak("Parameter 'month' is out of the range [1, 12]"); } static void THX_check_week(pTHX_ int64_t v) { if (v < 1 || v > 53) croak("Parameter 'week' is out of the range [1, 53]"); } static void THX_check_day_of_year(pTHX_ int64_t v) { if (v < 1 || v > 366) croak("Parameter 'day' is out of the range [1, 366]"); } static void THX_check_day_of_quarter(pTHX_ int64_t v) { if (v < 1 || v > 92) croak("Parameter 'day' is out of the range [1, 92]"); } static void THX_check_day_of_month(pTHX_ int64_t v) { if (v < 1 || v > 31) croak("Parameter 'day' is out of the range [1, 31]"); } static void THX_check_day_of_week(pTHX_ int64_t v) { if (v < 1 || v > 7) croak("Parameter 'day' is out of the range [1, 7]"); } static void THX_check_hour(pTHX_ int64_t v) { if (v < 0 || v > 23) croak("Parameter 'hour' is out of the range [1, 23]"); } static void THX_check_minute(pTHX_ int64_t v) { if (v < 0 || v > 59) croak("Parameter 'minute' is out of the range [1, 59]"); } static void THX_check_minute_of_day(pTHX_ int64_t v) { if (v < 0 || v > 1439) croak("Parameter 'minute' is out of the range [1, 1439]"); } static void THX_check_second(pTHX_ int64_t v) { if (v < 0 || v > 59) croak("Parameter 'second' is out of the range [1, 59]"); } static void THX_check_second_of_day(pTHX_ int64_t v) { if (v < 0 || v > 86399) croak("Parameter 'second' is out of the range [0, 86_399]"); } static void THX_check_millisecond(pTHX_ int64_t v) { if (v < 0 || v > 999) croak("Parameter 'millisecond' is out of the range [0, 999]"); } static void THX_check_microsecond(pTHX_ int64_t v) { if (v < 0 || v > 999999) croak("Parameter 'microsecond' is out of the range [0, 999_999]"); } static void THX_check_nanosecond(pTHX_ int64_t v) { if (v < 0 || v > 999999999) croak("Parameter 'nanosecond' is out of the range [0, 999_999_999]"); } static void THX_check_offset(pTHX_ int64_t v) { if (v < -1080 || v > 1080) croak("Parameter 'offset' is out of the range [-1080, 1080]"); } static void THX_check_epoch_seconds(pTHX_ int64_t v) { if (!VALID_EPOCH_SEC(v)) croak("Parameter 'seconds' is out of range"); } static void THX_check_rata_die_day(pTHX_ int64_t v) { if (v < MIN_RATA_DIE_DAY || v > MAX_RATA_DIE_DAY) croak("Parameter 'rdn' is out of range"); } static void THX_check_unit_years(pTHX_ int64_t v) { if (v < MIN_UNIT_YEARS || v > MAX_UNIT_YEARS) croak("Parameter 'years' is out of range"); } static void THX_check_unit_months(pTHX_ int64_t v) { if (v < MIN_UNIT_MONTHS || v > MAX_UNIT_MONTHS) croak("Parameter 'months' is out of range"); } static void THX_check_unit_weeks(pTHX_ int64_t v) { if (v < MIN_UNIT_WEEKS || v > MAX_UNIT_WEEKS) croak("Parameter 'weeks' is out of range"); } static void THX_check_unit_days(pTHX_ int64_t v) { if (v < MIN_UNIT_DAYS || v > MAX_UNIT_DAYS) croak("Parameter 'days' is out of range"); } static void THX_check_unit_hours(pTHX_ int64_t v) { if (v < MIN_UNIT_HOURS || v > MAX_UNIT_HOURS) croak("Parameter 'hours' is out of range"); } static void THX_check_unit_minutes(pTHX_ int64_t v) { if (v < MIN_UNIT_MINUTES || v > MAX_UNIT_MINUTES) croak("Parameter 'minutes' is out of range"); } static void THX_check_unit_seconds(pTHX_ int64_t v) { if (v < MIN_UNIT_SECONDS || v > MAX_UNIT_SECONDS) croak("Parameter 'seconds' is out of range"); } static void THX_check_unit_milliseconds(pTHX_ int64_t v) { if (v < MIN_UNIT_MILLIS || v > MAX_UNIT_MILLIS) croak("Parameter 'milliseconds' is out of range"); } static void THX_check_unit_microseconds(pTHX_ int64_t v) { if (v < MIN_UNIT_MICROS || v > MAX_UNIT_MICROS) croak("Parameter 'microseconds' is out of range"); } moment_t THX_moment_from_epoch(pTHX_ int64_t sec, IV nsec, IV offset) { THX_check_epoch_seconds(aTHX_ sec); THX_check_nanosecond(aTHX_ nsec); THX_check_offset(aTHX_ offset); sec += UNIX_EPOCH; return THX_moment_from_instant(aTHX_ sec, nsec, offset); } moment_t THX_moment_from_epoch_nv(pTHX_ NV sec, IV precision) { static const NV SEC_MIN = -62135596801.0; /* 0000-12-31T23:59:59Z */ static const NV SEC_MAX = 253402300800.0; /* 10000-01-01T00:00:00Z */ NV s, f, n, denom; if (precision < 0 || precision > 9) croak("Parameter 'precision' is out of the range [0, 9]"); if (!(sec > SEC_MIN && sec < SEC_MAX)) croak("Parameter 'seconds' is out of range"); f = n = Perl_fmod(sec, 1.0); s = Perl_floor(sec - f); if (n < 0) n += 1.0; s = s + Perl_floor(f - n); denom = Perl_pow(10.0, (NV)precision); n = (Perl_floor(n * denom + 0.5) / denom) * 1E9; return THX_moment_from_epoch(aTHX_ (int64_t)s, (IV)(n + 0.5), 0); } static int THX_moment_from_sd(pTHX_ NV sd, NV epoch, IV precision, int64_t *sec, int32_t *nsec) { static const NV SD_MIN = -146097 * 50; static const NV SD_MAX = 146097 * 50; NV d1, d2, f1, f2, f, d, s, denom; if (precision < 0 || precision > 9) croak("Parameter 'precision' is out of the range [0, 9]"); if (!(sd > SD_MIN && sd < SD_MAX)) return -1; if (!(epoch > SD_MIN && epoch < SD_MAX)) croak("Parameter 'epoch' is out of range"); if (sd >= epoch) { d1 = sd; d2 = epoch; } else { d1 = epoch; d2 = sd; } f1 = Perl_fmod(d1, 1.0); f2 = Perl_fmod(d2, 1.0); d1 = Perl_floor(d1 - f1); d2 = Perl_floor(d2 - f2); f = Perl_fmod(f1 + f2, 1.0); if (f < 0.0) f += 1.0; d = d1 + d2 + Perl_floor(f1 + f2 - f); f *= 86400; s = Perl_floor(f); if (d < 1 || d > 3652059) return -2; denom = Perl_pow(10.0, (NV)precision); f = (Perl_floor((f - s) * denom + 0.5) / denom) * 1E9; *sec = (int64_t)d * 86400 + (int32_t)s; *nsec = (int32_t)(f + 0.5); if (*nsec >= NANOS_PER_SEC) { *nsec -= NANOS_PER_SEC; *sec += 1; } return 0; } moment_t THX_moment_from_rd(pTHX_ NV jd, NV epoch, IV precision, IV offset) { int64_t sec; int32_t nsec; int r; THX_check_offset(aTHX_ offset); r = THX_moment_from_sd(aTHX_ jd, epoch, precision, &sec, &nsec); if (r < 0) { if (r == -1) croak("Parameter 'rd' is out of range"); else croak("Rata Die is out of range"); } return THX_moment_from_local(aTHX_ sec, nsec, offset); } moment_t THX_moment_from_jd(pTHX_ NV jd, NV epoch, IV precision) { int64_t sec; int32_t nsec; int r; r = THX_moment_from_sd(aTHX_ jd, epoch, precision, &sec, &nsec); if (r < 0) { if (r == -1) croak("Parameter 'jd' is out of range"); else croak("Julian date is out of range"); } return THX_moment_from_instant(aTHX_ sec, nsec, 0); } moment_t THX_moment_from_mjd(pTHX_ NV jd, NV epoch, IV precision) { int64_t sec; int32_t nsec; int r; r = THX_moment_from_sd(aTHX_ jd, epoch, precision, &sec, &nsec); if (r < 0) { if (r == -1) croak("Parameter 'mjd' is out of range"); else croak("Modified Julian date is out of range"); } return THX_moment_from_instant(aTHX_ sec, nsec, 0); } moment_t THX_moment_new(pTHX_ IV Y, IV M, IV D, IV h, IV m, IV s, IV nsec, IV offset) { int64_t rdn, sec; THX_check_year(aTHX_ Y); THX_check_month(aTHX_ M); THX_check_day_of_month(aTHX_ D); if (D > 28) { int dim = dt_days_in_month((int)Y, (int)M); if (D > dim) croak("Parameter 'day' is out of the range [1, %d]", dim); } THX_check_hour(aTHX_ h); THX_check_minute(aTHX_ m); THX_check_second(aTHX_ s); THX_check_nanosecond(aTHX_ nsec); THX_check_offset(aTHX_ offset); rdn = dt_rdn(dt_from_ymd((int)Y, (int)M, (int)D)); sec = ((rdn * 24 + h) * 60 + m) * 60 + s; return THX_moment_from_local(aTHX_ sec, nsec, offset); } static moment_t THX_moment_with_local_dt(pTHX_ const moment_t *mt, const dt_t dt) { int64_t sec; sec = (int64_t)dt_rdn(dt) * 86400 + moment_second_of_day(mt); return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_with_ymd(pTHX_ const moment_t *mt, int y, int m, int d) { if (d > 28) { int dim = dt_days_in_month(y, m); if (d > dim) d = dim; } return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, m, d)); } static moment_t THX_moment_with_year(pTHX_ const moment_t *mt, int64_t v) { int m, d; THX_check_year(aTHX_ v); dt_to_ymd(moment_local_dt(mt), NULL, &m, &d); return THX_moment_with_ymd(aTHX_ mt, (int)v, m, d); } static moment_t THX_moment_with_quarter(pTHX_ const moment_t *mt, int64_t v) { int y, m, d; THX_check_quarter(aTHX_ v); dt_to_ymd(moment_local_dt(mt), &y, &m, &d); m = 1 + 3 * ((int)v - 1) + (m - 1) % 3; return THX_moment_with_ymd(aTHX_ mt, y, m, d); } static moment_t THX_moment_with_month(pTHX_ const moment_t *mt, int64_t v) { int y, d; THX_check_month(aTHX_ v); dt_to_ymd(moment_local_dt(mt), &y, NULL, &d); return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, (int)v, d)); } static moment_t THX_moment_with_week(pTHX_ const moment_t *mt, int64_t v) { int y, w, d; THX_check_week(aTHX_ v); dt_to_ywd(moment_local_dt(mt), &y, NULL, &d); w = (int)v; if (w > 52) { int wiy = dt_weeks_in_year(y); if (w > wiy) croak("Parameter 'week' is out of the range [1, %d]", wiy); } return THX_moment_with_local_dt(aTHX_ mt, dt_from_ywd(y, w, d)); } static moment_t THX_moment_with_day_of_month(pTHX_ const moment_t *mt, int64_t v) { int y, m, d; THX_check_day_of_month(aTHX_ v); dt_to_ymd(moment_local_dt(mt), &y, &m, NULL); d = (int)v; if (d > 28) { int dim = dt_days_in_month(y, m); if (d > dim) croak("Parameter 'day' is out of the range [1, %d]", dim); } return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, m, d)); } static moment_t THX_moment_with_day_of_quarter(pTHX_ const moment_t *mt, int64_t v) { int y, q, d; THX_check_day_of_quarter(aTHX_ v); dt_to_yqd(moment_local_dt(mt), &y, &q, NULL); d = (int)v; if (d > 90) { int diq = dt_days_in_quarter(y, q); if (d > diq) croak("Parameter 'day' is out of the range [1, %d]", diq); } return THX_moment_with_local_dt(aTHX_ mt, dt_from_yqd(y, q, d)); } static moment_t THX_moment_with_day_of_year(pTHX_ const moment_t *mt, int64_t v) { int y, d; THX_check_day_of_year(aTHX_ v); dt_to_yd(moment_local_dt(mt), &y, NULL); d = (int)v; if (d > 365) { int diy = dt_days_in_year(y); if (v > diy) croak("Parameter 'day' is out of the range [1, %d]", diy); } return THX_moment_with_local_dt(aTHX_ mt, dt_from_yd(y, d)); } static moment_t THX_moment_with_day_of_week(pTHX_ const moment_t *mt, int64_t v) { dt_t dt; THX_check_day_of_week(aTHX_ v); dt = moment_local_dt(mt); return THX_moment_with_local_dt(aTHX_ mt, dt - (dt_dow(dt) - v)); } static moment_t THX_moment_with_rata_die_day(pTHX_ const moment_t *mt, int64_t v) { dt_t dt; THX_check_rata_die_day(aTHX_ v); dt = dt_from_rdn((int)v); return THX_moment_with_local_dt(aTHX_ mt, dt); } static moment_t THX_moment_with_hour(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_hour(aTHX_ v); sec = moment_local_rd_seconds(mt) + (v - moment_hour(mt)) * 3600; return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_with_minute(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_minute(aTHX_ v); sec = moment_local_rd_seconds(mt) + (v - moment_minute(mt)) * 60; return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_with_minute_of_day(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_minute_of_day(aTHX_ v); sec = moment_local_rd_seconds(mt) + (v - moment_minute_of_day(mt)) * 60; return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_with_second(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_second(aTHX_ v); sec = moment_local_rd_seconds(mt) + (v - moment_second(mt)); return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_with_second_of_day(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_second_of_day(aTHX_ v); sec = moment_local_rd_seconds(mt) + (v - moment_second_of_day(mt)); return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_with_millisecond(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_millisecond(aTHX_ v); sec = moment_local_rd_seconds(mt); return THX_moment_from_local(aTHX_ sec, v * 1000000, mt->offset); } static moment_t THX_moment_with_microsecond(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_microsecond(aTHX_ v); sec = moment_local_rd_seconds(mt); return THX_moment_from_local(aTHX_ sec, v * 1000, mt->offset); } static moment_t THX_moment_with_nanosecond(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_nanosecond(aTHX_ v); sec = moment_local_rd_seconds(mt); return THX_moment_from_local(aTHX_ sec, v, mt->offset); } static moment_t THX_moment_with_nanosecond_of_day(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; int32_t nsec; if (v < 0 || v > INT64_C(86400000000000)) croak("Paramteter 'nanosecond' is out of the range [0, 86_400_000_000_000]"); sec = moment_local_rd_seconds(mt) + v / NANOS_PER_SEC - moment_second_of_day(mt); nsec = v % NANOS_PER_SEC; return THX_moment_from_local(aTHX_ sec, nsec, mt->offset); } static moment_t THX_moment_with_microsecond_of_day(pTHX_ const moment_t *mt, int64_t v) { if (v < 0 || v > INT64_C(86400000000)) croak("Paramteter 'microsecond' is out of the range [0, 86_400_000_000]"); return THX_moment_with_nanosecond_of_day(aTHX_ mt, v * 1000); } static moment_t THX_moment_with_millisecond_of_day(pTHX_ const moment_t *mt, int64_t v) { if (v < 0 || v > INT64_C(86400000)) croak("Paramteter 'millisecond' is out of the range [0, 86_400_000]"); return THX_moment_with_nanosecond_of_day(aTHX_ mt, v * 1000000); } moment_t THX_moment_with_field(pTHX_ const moment_t *mt, moment_component_t c, int64_t v) { switch (c) { case MOMENT_FIELD_YEAR: return THX_moment_with_year(aTHX_ mt, v); case MOMENT_FIELD_QUARTER_OF_YEAR: return THX_moment_with_quarter(aTHX_ mt, v); case MOMENT_FIELD_MONTH_OF_YEAR: return THX_moment_with_month(aTHX_ mt, v); case MOMENT_FIELD_WEEK_OF_YEAR: return THX_moment_with_week(aTHX_ mt, v); case MOMENT_FIELD_DAY_OF_MONTH: return THX_moment_with_day_of_month(aTHX_ mt, v); case MOMENT_FIELD_DAY_OF_QUARTER: return THX_moment_with_day_of_quarter(aTHX_ mt, v); case MOMENT_FIELD_DAY_OF_YEAR: return THX_moment_with_day_of_year(aTHX_ mt, v); case MOMENT_FIELD_DAY_OF_WEEK: return THX_moment_with_day_of_week(aTHX_ mt, v); case MOMENT_FIELD_HOUR_OF_DAY: return THX_moment_with_hour(aTHX_ mt, v); case MOMENT_FIELD_MINUTE_OF_HOUR: return THX_moment_with_minute(aTHX_ mt, v); case MOMENT_FIELD_MINUTE_OF_DAY: return THX_moment_with_minute_of_day(aTHX_ mt, v); case MOMENT_FIELD_SECOND_OF_MINUTE: return THX_moment_with_second(aTHX_ mt, v); case MOMENT_FIELD_SECOND_OF_DAY: return THX_moment_with_second_of_day(aTHX_ mt, v); case MOMENT_FIELD_MILLI_OF_SECOND: return THX_moment_with_millisecond(aTHX_ mt, v); case MOMENT_FIELD_MILLI_OF_DAY: return THX_moment_with_millisecond_of_day(aTHX_ mt, v); case MOMENT_FIELD_MICRO_OF_SECOND: return THX_moment_with_microsecond(aTHX_ mt, v); case MOMENT_FIELD_MICRO_OF_DAY: return THX_moment_with_microsecond_of_day(aTHX_ mt, v); case MOMENT_FIELD_NANO_OF_SECOND: return THX_moment_with_nanosecond(aTHX_ mt, v); case MOMENT_FIELD_NANO_OF_DAY: return THX_moment_with_nanosecond_of_day(aTHX_ mt, v); case MOMENT_FIELD_PRECISION: return THX_moment_with_precision(aTHX_ mt, v); case MOMENT_FIELD_RATA_DIE_DAY: return THX_moment_with_rata_die_day(aTHX_ mt, v); } croak("panic: THX_moment_with_component() called with unknown component (%d)", (int)c); } static moment_t THX_moment_plus_months(pTHX_ const moment_t *mt, int64_t v) { dt_t dt; THX_check_unit_months(aTHX_ v); dt = dt_add_months(moment_local_dt(mt), (int)v, DT_LIMIT); return THX_moment_with_local_dt(aTHX_ mt, dt); } static moment_t THX_moment_plus_days(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_unit_days(aTHX_ v); sec = moment_local_rd_seconds(mt) + v * 86400; return THX_moment_from_local(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_plus_seconds(pTHX_ const moment_t *mt, int64_t v) { int64_t sec; THX_check_unit_seconds(aTHX_ v); sec = moment_instant_rd_seconds(mt) + v; return THX_moment_from_instant(aTHX_ sec, mt->nsec, mt->offset); } static moment_t THX_moment_plus_time(pTHX_ const moment_t *mt, int64_t sec, int64_t nsec, int sign) { sec = sec + (nsec / NANOS_PER_SEC); nsec = nsec % NANOS_PER_SEC; sec = moment_instant_rd_seconds(mt) + sec * sign; nsec = mt->nsec + nsec * sign; if (nsec < 0) { nsec += NANOS_PER_SEC; sec--; } else if (nsec >= NANOS_PER_SEC) { nsec -= NANOS_PER_SEC; sec++; } return THX_moment_from_instant(aTHX_ sec, (IV)nsec, mt->offset); } moment_t THX_moment_plus_unit(pTHX_ const moment_t *mt, moment_unit_t u, int64_t v) { switch (u) { case MOMENT_UNIT_YEARS: THX_check_unit_years(aTHX_ v); return THX_moment_plus_months(aTHX_ mt, v * 12); case MOMENT_UNIT_MONTHS: THX_check_unit_months(aTHX_ v); return THX_moment_plus_months(aTHX_ mt, v); case MOMENT_UNIT_WEEKS: THX_check_unit_weeks(aTHX_ v); return THX_moment_plus_days(aTHX_ mt, v * 7); case MOMENT_UNIT_DAYS: THX_check_unit_days(aTHX_ v); return THX_moment_plus_days(aTHX_ mt, v); case MOMENT_UNIT_HOURS: THX_check_unit_hours(aTHX_ v); return THX_moment_plus_seconds(aTHX_ mt, v * 3600); case MOMENT_UNIT_MINUTES: THX_check_unit_minutes(aTHX_ v); return THX_moment_plus_seconds(aTHX_ mt, v * 60); case MOMENT_UNIT_SECONDS: THX_check_unit_seconds(aTHX_ v); return THX_moment_plus_seconds(aTHX_ mt, v); case MOMENT_UNIT_MILLIS: THX_check_unit_milliseconds(aTHX_ v); return THX_moment_plus_time(aTHX_ mt, v / 1000, (v % 1000) * 1000000, 1); case MOMENT_UNIT_MICROS: THX_check_unit_microseconds(aTHX_ v); return THX_moment_plus_time(aTHX_ mt, v / 1000000, (v % 1000000) * 1000, 1); case MOMENT_UNIT_NANOS: return THX_moment_plus_time(aTHX_ mt, 0, v, 1); } croak("panic: THX_moment_plus_unit() called with unknown unit (%d)", (int)u); } moment_t THX_moment_minus_unit(pTHX_ const moment_t *mt, moment_unit_t u, int64_t v) { switch (u) { case MOMENT_UNIT_YEARS: THX_check_unit_years(aTHX_ v); return THX_moment_plus_months(aTHX_ mt, -v * 12); case MOMENT_UNIT_MONTHS: THX_check_unit_months(aTHX_ v); return THX_moment_plus_months(aTHX_ mt, -v); case MOMENT_UNIT_WEEKS: THX_check_unit_weeks(aTHX_ v); return THX_moment_plus_days(aTHX_ mt, -v * 7); case MOMENT_UNIT_DAYS: THX_check_unit_days(aTHX_ v); return THX_moment_plus_days(aTHX_ mt, -v); case MOMENT_UNIT_HOURS: THX_check_unit_hours(aTHX_ v); return THX_moment_plus_seconds(aTHX_ mt, -v * 3600); case MOMENT_UNIT_MINUTES: THX_check_unit_minutes(aTHX_ v); return THX_moment_plus_seconds(aTHX_ mt, -v * 60); case MOMENT_UNIT_SECONDS: THX_check_unit_seconds(aTHX_ v); return THX_moment_plus_seconds(aTHX_ mt, -v); case MOMENT_UNIT_MILLIS: THX_check_unit_milliseconds(aTHX_ v); return THX_moment_plus_time(aTHX_ mt, v / 1000, (v % 1000) * 1000000, -1); case MOMENT_UNIT_MICROS: THX_check_unit_microseconds(aTHX_ v); return THX_moment_plus_time(aTHX_ mt, v / 1000000, (v % 1000000) * 1000, -1); case MOMENT_UNIT_NANOS: return THX_moment_plus_time(aTHX_ mt, 0, v, -1); } croak("panic: THX_moment_minus_unit() called with unknown unit (%d)", (int)u); } moment_t THX_moment_with_offset_same_instant(pTHX_ const moment_t *mt, IV offset) { int64_t sec; THX_check_offset(aTHX_ offset); sec = moment_instant_rd_seconds(mt); return THX_moment_from_instant(aTHX_ sec, mt->nsec, offset); } moment_t THX_moment_with_offset_same_local(pTHX_ const moment_t *mt, IV offset) { int64_t sec; THX_check_offset(aTHX_ offset); sec = moment_local_rd_seconds(mt); return THX_moment_from_local(aTHX_ sec, mt->nsec, offset); } moment_t THX_moment_with_precision(pTHX_ const moment_t *mt, int64_t precision) { int64_t sec; int32_t nsec; if (precision < -3 || precision > 9) croak("Parameter 'precision' is out of the range [-3, 9]"); sec = moment_local_rd_seconds(mt); nsec = mt->nsec; if (precision <= 0) { nsec = 0; switch (precision) { case -1: sec -= sec % 60; break; case -2: sec -= sec % 3600; break; case -3: sec -= sec % 86400; break; } } else { nsec -= nsec % kPow10[9 - precision]; } return THX_moment_from_local(aTHX_ sec, nsec, mt->offset); } moment_duration_t moment_subtract_moment(const moment_t *mt1, const moment_t *mt2) { const int64_t s1 = moment_instant_rd_seconds(mt1); const int64_t s2 = moment_instant_rd_seconds(mt2); moment_duration_t d; d.sec = s2 - s1; d.nsec = mt2->nsec - mt1->nsec; if (d.nsec < 0) { d.sec -= 1; d.nsec += NANOS_PER_SEC; } return d; } static int moment_delta_days(const moment_t *mt1, const moment_t *mt2) { const dt_t dt1 = moment_local_dt(mt1); const dt_t dt2 = moment_local_dt(mt2); return dt2 - dt1; } static int moment_delta_weeks(const moment_t *mt1, const moment_t *mt2) { return moment_delta_days(mt1, mt2) / 7; } static int moment_delta_months(const moment_t *mt1, const moment_t *mt2) { const dt_t dt1 = moment_local_dt(mt1); const dt_t dt2 = moment_local_dt(mt2); return dt_delta_months(dt1, dt2, true); } static int moment_delta_years(const moment_t *mt1, const moment_t *mt2) { return moment_delta_months(mt1, mt2) / 12; } static int64_t THX_moment_delta_hours(pTHX_ const moment_t *mt1, const moment_t *mt2) { moment_duration_t d; d = moment_subtract_moment(mt1, mt2); return (d.sec / 3600); } static int64_t THX_moment_delta_minutes(pTHX_ const moment_t *mt1, const moment_t *mt2) { moment_duration_t d; d = moment_subtract_moment(mt1, mt2); return (d.sec / 60); } static int64_t THX_moment_delta_seconds(pTHX_ const moment_t *mt1, const moment_t *mt2) { moment_duration_t d; d = moment_subtract_moment(mt1, mt2); return d.sec; } static int64_t THX_moment_delta_milliseconds(pTHX_ const moment_t *mt1, const moment_t *mt2) { moment_duration_t d; d = moment_subtract_moment(mt1, mt2); return d.sec * 1000 + (d.nsec / 1000000); } static int64_t THX_moment_delta_microseconds(pTHX_ const moment_t *mt1, const moment_t *mt2) { moment_duration_t d; d = moment_subtract_moment(mt1, mt2); return d.sec * 1000000 + (d.nsec / 1000); } static int64_t THX_moment_delta_nanoseconds(pTHX_ const moment_t *mt1, const moment_t *mt2) { static const int64_t kMaxSec = INT64_C(9223372035); moment_duration_t d; d = moment_subtract_moment(mt1, mt2); if (d.sec > kMaxSec || d.sec < -kMaxSec) croak("Nanosecond duration is too large to be represented in a 64-bit integer"); return d.sec * 1000000000 + d.nsec; } int64_t THX_moment_delta_unit(pTHX_ const moment_t *mt1, const moment_t *mt2, moment_unit_t u) { switch (u) { case MOMENT_UNIT_YEARS: return moment_delta_years(mt1, mt2); case MOMENT_UNIT_MONTHS: return moment_delta_months(mt1, mt2); case MOMENT_UNIT_WEEKS: return moment_delta_weeks(mt1, mt2); case MOMENT_UNIT_DAYS: return moment_delta_days(mt1, mt2); case MOMENT_UNIT_HOURS: return THX_moment_delta_hours(aTHX_ mt1, mt2); case MOMENT_UNIT_MINUTES: return THX_moment_delta_minutes(aTHX_ mt1, mt2); case MOMENT_UNIT_SECONDS: return THX_moment_delta_seconds(aTHX_ mt1, mt2); case MOMENT_UNIT_MILLIS: return THX_moment_delta_milliseconds(aTHX_ mt1, mt2); case MOMENT_UNIT_MICROS: return THX_moment_delta_microseconds(aTHX_ mt1, mt2); case MOMENT_UNIT_NANOS: return THX_moment_delta_nanoseconds(aTHX_ mt1, mt2); default: croak("panic: THX_moment_delta_unit() called with unknown unit (%d)", (int)u); } } moment_t THX_moment_at_utc(pTHX_ const moment_t *mt) { return THX_moment_with_offset_same_instant(aTHX_ mt, 0); } moment_t THX_moment_at_midnight(pTHX_ const moment_t *mt) { return THX_moment_with_millisecond_of_day(aTHX_ mt, 0); } moment_t THX_moment_at_noon(pTHX_ const moment_t *mt) { return THX_moment_with_millisecond_of_day(aTHX_ mt, 12*60*60*1000); } moment_t THX_moment_at_last_day_of_year(pTHX_ const moment_t *mt) { int y; dt_to_yd(moment_local_dt(mt), &y, NULL); return THX_moment_with_local_dt(aTHX_ mt, dt_from_yd(y + 1, 0)); } moment_t THX_moment_at_last_day_of_quarter(pTHX_ const moment_t *mt) { int y, q; dt_to_yqd(moment_local_dt(mt), &y, &q, NULL); return THX_moment_with_local_dt(aTHX_ mt, dt_from_yqd(y, q + 1, 0)); } moment_t THX_moment_at_last_day_of_month(pTHX_ const moment_t *mt) { int y, m; dt_to_ymd(moment_local_dt(mt), &y, &m, NULL); return THX_moment_with_local_dt(aTHX_ mt, dt_from_ymd(y, m + 1, 0)); } int moment_compare_instant(const moment_t *m1, const moment_t *m2) { const int64_t s1 = moment_instant_rd_seconds(m1); const int64_t s2 = moment_instant_rd_seconds(m2); int r; r = (s1 > s2) - (s1 < s2); if (r == 0) r = (m1->nsec > m2->nsec) - (m1->nsec < m2->nsec); return r; } int moment_compare_local(const moment_t *m1, const moment_t *m2) { const int64_t s1 = moment_local_rd_seconds(m1); const int64_t s2 = moment_local_rd_seconds(m2); int r; r = (s1 > s2) - (s1 < s2); if (r == 0) r = (m1->nsec > m2->nsec) - (m1->nsec < m2->nsec); return r; } int THX_moment_compare_precision(pTHX_ const moment_t *m1, const moment_t *m2, IV precision) { int64_t n1, n2; int r; if (precision < -3 || precision > 9) croak("Parameter 'precision' is out of the range [-3, 9]"); if (precision < 0) { int32_t n; n = 0; switch (precision) { case -1: n = 60; break; case -2: n = 3600; break; case -3: n = 86400; break; } n1 = moment_local_rd_seconds(m1); n2 = moment_local_rd_seconds(m2); n1 -= n1 % n; n2 -= n2 % n; n1 -= m1->offset * 60; n2 -= m2->offset * 60; r = (n1 > n2) - (n1 < n2); } else { n1 = moment_instant_rd_seconds(m1); n2 = moment_instant_rd_seconds(m2); r = (n1 > n2) - (n1 < n2); if (r == 0 && precision != 0) { n1 = m1->nsec - m1->nsec % kPow10[9 - precision]; n2 = m2->nsec - m2->nsec % kPow10[9 - precision]; r = (n1 > n2) - (n1 < n2); } } return r; } bool moment_equals(const moment_t *m1, const moment_t *m2) { return memcmp(m1, m2, sizeof(moment_t)) == 0; } int64_t moment_epoch(const moment_t *mt) { return (moment_instant_rd_seconds(mt) - UNIX_EPOCH); } int moment_year(const moment_t *mt) { return dt_year(moment_local_dt(mt)); } int moment_month(const moment_t *mt) { return dt_month(moment_local_dt(mt)); } int moment_quarter(const moment_t *mt) { return dt_quarter(moment_local_dt(mt)); } int moment_week(const moment_t *mt) { return dt_woy(moment_local_dt(mt)); } int moment_day_of_year(const moment_t *mt) { return dt_doy(moment_local_dt(mt)); } int moment_day_of_quarter(const moment_t *mt) { return dt_doq(moment_local_dt(mt)); } int moment_day_of_month(const moment_t *mt) { return dt_dom(moment_local_dt(mt)); } int moment_day_of_week(const moment_t *mt) { return dt_dow(moment_local_dt(mt)); } int moment_hour(const moment_t *mt) { return (int)((moment_local_rd_seconds(mt) / 3600) % 24); } int moment_minute(const moment_t *mt) { return (int)((moment_local_rd_seconds(mt) / 60) % 60); } int moment_minute_of_day(const moment_t *mt) { return (int)((moment_local_rd_seconds(mt) / 60) % 1440); } int moment_second(const moment_t *mt) { return (int)(moment_local_rd_seconds(mt) % 60); } int moment_second_of_day(const moment_t *mt) { return (int)(moment_local_rd_seconds(mt) % 86400); } int moment_millisecond(const moment_t *mt) { return (mt->nsec / 1000000); } int moment_millisecond_of_day(const moment_t *mt) { return moment_second_of_day(mt) * 1000 + moment_millisecond(mt); } int moment_microsecond(const moment_t *mt) { return (mt->nsec / 1000); } int64_t moment_microsecond_of_day(const moment_t *mt) { const int64_t sod = moment_local_rd_seconds(mt) % 86400; return sod * 1000000 + (mt->nsec / 1000); } int moment_nanosecond(const moment_t *mt) { return mt->nsec; } int64_t moment_nanosecond_of_day(const moment_t *mt) { const int64_t sod = moment_local_rd_seconds(mt) % 86400; return sod * 1000000000 + mt->nsec; } NV moment_jd(const moment_t *mt) { return moment_mjd(mt) + 2400000.5; } NV moment_mjd(const moment_t *mt) { const int64_t s = moment_instant_rd_seconds(mt); const int64_t d = (s / SECS_PER_DAY) - 678576; const int64_t n = (s % SECS_PER_DAY) * NANOS_PER_SEC + mt->nsec; return (NV)d + (NV)n * (1E-9/60/60/24); } NV moment_rd(const moment_t *mt) { const int64_t s = moment_local_rd_seconds(mt); const int64_t d = (s / SECS_PER_DAY); const int64_t n = (s % SECS_PER_DAY) * NANOS_PER_SEC + mt->nsec; return (NV)d + (NV)n * (1E-9/60/60/24); } int moment_rata_die_day(const moment_t *mt) { return dt_rdn(moment_local_dt(mt)); } int moment_offset(const moment_t *mt) { return mt->offset; } int moment_precision(const moment_t *mt) { int v, i; v = mt->nsec; if (v != 0) { for (i = 8; i > 0; i--) { if ((v % kPow10[i]) == 0) break; } return 9 - i; } v = moment_second_of_day(mt); if (v != 0) { if ((v % 3600) == 0) return -2; else if ((v % 60) == 0) return -1; else return 0; } return -3; } int moment_length_of_year(const moment_t *mt) { return dt_length_of_year(moment_local_dt(mt)); } int moment_length_of_quarter(const moment_t *mt) { return dt_length_of_quarter(moment_local_dt(mt)); } int moment_length_of_month(const moment_t *mt) { return dt_length_of_month(moment_local_dt(mt)); } int moment_length_of_week_year(const moment_t *mt) { return dt_length_of_week_year(moment_local_dt(mt)); } bool moment_is_leap_year(const moment_t *mt) { return dt_leap_year(moment_year(mt)); } int THX_moment_internal_western_easter(pTHX_ int64_t y) { THX_check_year(aTHX_ y); return dt_rdn(dt_from_easter((int)y, DT_WESTERN)); } int THX_moment_internal_orthodox_easter(pTHX_ int64_t y) { THX_check_year(aTHX_ y); return dt_rdn(dt_from_easter((int)y, DT_ORTHODOX)); }