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));
}
|