static struct dt_ddur_s __ymcw_diff(dt_ymcw_t d1, dt_ymcw_t d2) { /* compute d2 - d1 entirely in terms of ymd */ struct dt_ddur_s res = dt_make_ddur(DT_DURYMCW, 0); signed int tgtd; signed int tgtm; dt_dow_t wd01, wd02; if (__ymcw_cmp(d1, d2) > 0) { dt_ymcw_t tmp = d1; d1 = d2; d2 = tmp; res.neg = 1; } wd01 = __get_m01_wday(d1.y, d1.m); if (d2.y != d1.y || d2.m != d1.m) { wd02 = __get_m01_wday(d2.y, d2.m); } else { wd02 = wd01; } /* first compute the difference in months Y2-M2-01 - Y1-M1-01 */ tgtm = GREG_MONTHS_P_YEAR * (d2.y - d1.y) + (d2.m - d1.m); /* using the firsts of the month WD01, represent d1 and d2 as * the C-th WD01 plus OFF */ { unsigned int off1; unsigned int off2; off1 = __uimod(d1.w - wd01, GREG_DAYS_P_WEEK); off2 = __uimod(d2.w - wd02, GREG_DAYS_P_WEEK); tgtd = off2 - off1 + GREG_DAYS_P_WEEK * (d2.c - d1.c); } /* fixups */ if (tgtd < (signed int)GREG_DAYS_P_WEEK && tgtm > 0) { /* if tgtm is 0 it remains 0 and tgtd remains negative */ /* get the target month's mdays */ unsigned int d2m = d2.m; unsigned int d2y = d2.y; if (--d2m < 1) { d2m = GREG_MONTHS_P_YEAR; d2y--; } tgtd += __get_mdays(d2y, d2m); tgtm--; } /* fill in the results */ res.ymcw.y = tgtm / GREG_MONTHS_P_YEAR; res.ymcw.m = tgtm % GREG_MONTHS_P_YEAR; res.ymcw.c = tgtd / GREG_DAYS_P_WEEK; res.ymcw.w = tgtd % GREG_DAYS_P_WEEK; return res; }
DEFUN unsigned int __get_bdays(unsigned int y, unsigned int m) { /* the 28th exists in every month, and it's exactly 20 bdays * away from the first, oh and it's -1 mod 7 * then to get the number of bdays remaining in the month * from the number of days remaining in the month R * we use a multiplication table, downwards the weekday of the * 28th, rightwards the days in excess of the 28th * Miracleday is only there to make the left hand side of the * multiplication 3 bits wide: * * r-> 0 1 2 3 * Sun 0 1 2 3 * Mon 0 1 2 3 * Tue 0 1 2 3 * Wed 0 1 2 2 * Thu 0 1 1 1 * Fri 0 0 0 1 * Sat 0 0 1 2 * Mir 0 0 0 0 */ unsigned int md = __get_mdays(y, m); unsigned int rd = (unsigned int)(md - 28U); dt_dow_t m01wd; dt_dow_t m28wd; /* rd should not overflow */ assert((signed int)md - 28 >= 0); /* wday of the 1st and 28th */ m01wd = __get_m01_wday(y, m); m28wd = (dt_dow_t)(m01wd - 1 ?: DT_SUNDAY); if (LIKELY(rd > 0)) { switch (m28wd) { case DT_SUNDAY: case DT_MONDAY: case DT_TUESDAY: return 20 + rd; case DT_WEDNESDAY: return 20 + rd - (rd == 3); case DT_THURSDAY: return 21; case DT_FRIDAY: return 20 + (rd == 3); case DT_SATURDAY: return 20 + rd - 1; case DT_MIRACLEDAY: default: abort(); } } return 20U; }
static unsigned int __bizda_get_mday(dt_bizda_t that) { dt_dow_t wd01; unsigned int res; /* find first of the month first */ wd01 = __get_m01_wday(that.y, that.m); switch (wd01) { case DT_MONDAY: case DT_TUESDAY: case DT_WEDNESDAY: case DT_THURSDAY: case DT_FRIDAY: res = 1; break; case DT_SATURDAY: wd01 = DT_MONDAY; res = 3; break; case DT_SUNDAY: wd01 = DT_MONDAY; res = 2; break; case DT_MIRACLEDAY: default: res = 0; break; } /* now just add up bdays */ { unsigned int wk; unsigned int nd; unsigned int b = that.bd; unsigned int magic = (b - 1 + wd01 - 1); assert(b + wd01 >= 2); wk = magic / DUWW_BDAYS_P_WEEK; nd = magic % DUWW_BDAYS_P_WEEK; res += wk * GREG_DAYS_P_WEEK + nd - wd01 + 1; } /* fixup mdays */ if (res > __get_mdays(that.y, that.m)) { res = 0; } return res; }
static unsigned int __ymcw_get_mday(dt_ymcw_t that) { unsigned int wd01; unsigned int res; /* see what weekday the first of the month was*/ wd01 = __get_m01_wday(that.y, that.m); /* first WD1 is 1, second WD1 is 8, third WD1 is 15, etc. * so the first WDx with WDx > WD1 is on (WDx - WD1) + 1 */ res = (that.w + GREG_DAYS_P_WEEK - wd01) % GREG_DAYS_P_WEEK + 1; res += GREG_DAYS_P_WEEK * (that.c - 1); /* not all months have a 5th X, so check for this */ if (res > __get_mdays(that.y, that.m)) { /* 5th = 4th in that case */ res -= GREG_DAYS_P_WEEK; } return res; }
static __attribute__((pure)) unsigned int __get_mcnt(unsigned int y, unsigned int m, dt_dow_t w) { /* get the number of weekdays W in Y-M, which is the max count * for a weekday W in ymcw dates in year Y and month M */ dt_dow_t wd01 = __get_m01_wday(y, m); unsigned int md = __get_mdays(y, m); /* the maximum number of WD01s in Y-M */ unsigned int wd01cnt = (md - 1) / GREG_DAYS_P_WEEK + 1; /* modulus */ unsigned int wd01mod = (md - 1) % GREG_DAYS_P_WEEK; /* now the next WD01MOD days also have WD01CNT occurrences * if wd01 + wd01mod exceeds the DAYS_PER_WEEK barrier wrap * around by extending W to W + DAYS_PER_WEEK */ if ((w >= wd01 && w <= wd01 + wd01mod) || (w + GREG_DAYS_P_WEEK) <= wd01 + wd01mod) { return wd01cnt; } else { return wd01cnt - 1; } }
echs_instant_t echs_instant_add(echs_instant_t bas, echs_idiff_t add) { echs_instant_t res = bas; int dd = add.d / (int)MSECS_PER_DAY; int msd = add.d % (int)MSECS_PER_DAY; int car, cdr; if (UNLIKELY(echs_instant_all_day_p(bas))) { /* just fix up the day, dom and year portion */ goto fixup_d; } else if (UNLIKELY(echs_instant_all_sec_p(bas))) { /* just fix up the sec, min, ... portions */ msd /= (int)MSECS_PER_SEC; goto fixup_S; } car = (res.ms + msd) / (int)MSECS_PER_SEC; if ((cdr = (res.ms + msd) % (int)MSECS_PER_SEC) >= 0) { res.ms = cdr; } else { res.ms = cdr + MSECS_PER_SEC; car--; } msd = car; fixup_S: car = (res.S + msd) / (int)SECS_PER_MIN; if ((cdr = (res.S + msd) % (int)SECS_PER_MIN) >= 0) { res.S = cdr; } else { res.S = cdr + SECS_PER_MIN; car--; } msd = car; car = ((int)res.M + msd) / (int)MINS_PER_HOUR; if ((cdr = ((int)res.M + msd) % (int)MINS_PER_HOUR) >= 0) { res.M = cdr; } else { res.M = cdr + MINS_PER_HOUR; car--; } msd = car; car = (res.H + msd) / (int)HOURS_PER_DAY; if ((cdr = (res.H + msd) % (int)HOURS_PER_DAY) >= 0) { res.H = cdr; } else { res.H = cdr + HOURS_PER_DAY; car--; } msd = car; /* get ready to adjust the day */ if (UNLIKELY(msd)) { dd += msd; } if (dd) { int y; int m; int d; fixup_d: y = bas.y; m = bas.m; d = bas.d + dd; if (LIKELY(d >= 1 && d <= 28)) { /* all months in our design range have 28 days */ ; } else if (d < 1) { int mdays; do { if (UNLIKELY(--m < 1)) { --y; m = 12; } mdays = __get_mdays(y, m); d += mdays; } while (d < 1); } else { int mdays; while (d > (mdays = __get_mdays(y, m))) { d -= mdays; if (UNLIKELY(++m > 12)) { ++y; m = 1; } } } res.d = d; res.m = m; res.y = y; } return res; }
echs_instant_t echs_instant_fixup(echs_instant_t e) { /* this is basically __ymd_fixup_d of dateutils * we only care about additive cockups though because instants are * chronologically ascending */ unsigned int md; if (UNLIKELY(echs_instant_all_day_p(e))) { /* just fix up the day, dom and year portion */ goto fixup_d; } else if (UNLIKELY(echs_instant_all_sec_p(e))) { /* just fix up the sec, min, ... portions */ goto fixup_S; } if (UNLIKELY(e.ms >= MSECS_PER_SEC)) { unsigned int dS = e.ms / MSECS_PER_SEC; unsigned int ms = e.ms % MSECS_PER_SEC; e.ms = ms; e.S += dS; } fixup_S: if (UNLIKELY(e.S >= SECS_PER_MIN)) { /* leap seconds? */ unsigned int dM = e.S / SECS_PER_MIN; unsigned int S = e.S % SECS_PER_MIN; e.S = S; e.M += dM; } if (UNLIKELY(e.M >= MINS_PER_HOUR)) { unsigned int dH = e.M / MINS_PER_HOUR; unsigned int M = e.M % MINS_PER_HOUR; e.M = M; e.H += dH; } if (UNLIKELY(e.H >= HOURS_PER_DAY)) { unsigned int dd = e.H / HOURS_PER_DAY; unsigned int H = e.H % HOURS_PER_DAY; e.H = H; e.d += dd; } fixup_d: refix_ym: if (UNLIKELY(e.m > 12U)) { unsigned int dy = (e.m - 1) / 12U; unsigned int m = (e.m - 1) % 12U + 1U; e.m = m; e.y += dy; } if (UNLIKELY(e.d > (md = __get_mdays(e.y, e.m)))) { e.d -= md; e.m++; goto refix_ym; } return e; }
static struct dt_d_s dround_ddur(struct dt_d_s d, struct dt_ddur_s dur, bool nextp) { switch (dur.durtyp) { unsigned int tgt; bool forw; case DT_DURD: if (dur.dv > 0) { tgt = dur.dv; forw = true; } else if (dur.dv < 0) { tgt = -dur.dv; forw = false; } else { /* user is an idiot */ break; } switch (d.typ) { unsigned int mdays; case DT_YMD: if ((forw && d.ymd.d < tgt) || (!forw && d.ymd.d > tgt)) { /* no month or year adjustment */ ; } else if (d.ymd.d == tgt && !nextp) { /* we're ON the date already and no * next/prev date is requested */ ; } else if (forw) { if (LIKELY(d.ymd.m < GREG_MONTHS_P_YEAR)) { d.ymd.m++; } else { d.ymd.m = 1; d.ymd.y++; } } else { if (UNLIKELY(--d.ymd.m < 1)) { d.ymd.m = GREG_MONTHS_P_YEAR; d.ymd.y--; } } /* get ultimo */ mdays = __get_mdays(d.ymd.y, d.ymd.m); if (UNLIKELY(tgt > mdays)) { tgt = mdays; } /* final assignment */ d.ymd.d = tgt; break; default: break; } break; case DT_DURBD: /* bizsis only work on bizsidurs atm */ if (dur.dv > 0) { tgt = dur.dv; forw = true; } else if (dur.dv < 0) { tgt = -dur.dv; forw = false; } else { /* user is an idiot */ break; } switch (d.typ) { unsigned int bdays; case DT_BIZDA: if ((forw && d.bizda.bd < tgt) || (!forw && d.bizda.bd > tgt)) { /* no month or year adjustment */ ; } else if (d.bizda.bd == tgt && !nextp) { /* we're ON the date already and no * next/prev date is requested */ ; } else if (forw) { if (LIKELY(d.bizda.m < GREG_MONTHS_P_YEAR)) { d.bizda.m++; } else { d.bizda.m = 1; d.bizda.y++; } } else { if (UNLIKELY(--d.bizda.m < 1)) { d.bizda.m = GREG_MONTHS_P_YEAR; d.bizda.y--; } } /* get ultimo */ bdays = __get_bdays(d.bizda.y, d.bizda.m); if (UNLIKELY(tgt > bdays)) { tgt = bdays; } /* final assignment */ d.bizda.bd = tgt; break; default: break; } break; case DT_DURYMD: switch (d.typ) { unsigned int mdays; case DT_YMD: tgt = dur.ymd.m; forw = !dt_dur_neg_p(dur); if ((forw && d.ymd.m < tgt) || (!forw && d.ymd.m > tgt)) { /* no year adjustment */ ; } else if (d.ymd.m == tgt && !nextp) { /* we're IN the month already and no * next/prev date is requested */ ; } else if (forw) { /* years don't wrap around */ d.ymd.y++; } else { /* years don't wrap around */ d.ymd.y--; } /* final assignment */ d.ymd.m = tgt; /* fixup ultimo mismatches */ mdays = __get_mdays(d.ymd.y, d.ymd.m); if (UNLIKELY(d.ymd.d > mdays)) { d.ymd.d = mdays; } break; default: break; } break; case DT_DURYMCW: { struct dt_d_s tmp; unsigned int wday; signed int diff; forw = !dt_dur_neg_p(dur); tgt = dur.ymcw.w; tmp = dt_dconv(DT_DAISY, d); wday = dt_get_wday(tmp); diff = (signed)tgt - (signed)wday; if ((forw && wday < tgt) || (!forw && wday > tgt)) { /* nothing to do */ ; } else if (wday == tgt && !nextp) { /* we're on WDAY already, do fuckall */ ; } else if (forw) { /* week wrap */ diff += 7; } else { /* week wrap */ diff -= 7; } /* final assignment */ tmp.daisy += diff; d = dt_dconv(d.typ, tmp); break; } case DT_DURWK: if (dur.dv > 0) { tgt = dur.dv; forw = true; } else if (dur.dv < 0) { tgt = -dur.dv; forw = false; } else { /* user is an idiot */ break; } switch (d.typ) { unsigned int nw; case DT_YWD: if ((forw && d.ywd.c < tgt) || (!forw && d.ywd.c > tgt)) { /* no year adjustment */ ; } else if (d.ywd.c == tgt && !nextp) { /* we're IN the week already and no * next/prev date is requested */ ; } else if (forw) { /* years don't wrap around */ d.ywd.y++; } else { /* years don't wrap around */ d.ywd.y--; } /* final assignment */ d.ywd.c = tgt; /* fixup ultimo mismatches */ nw = __get_isowk(d.ywd.y); if (UNLIKELY(d.ywd.c > nw)) { d.ywd.c = nw; } break; default: break; } break; default: break; } return d; }