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; }
static dt_ymcw_t __ymcw_add_b(dt_ymcw_t d, int n) { /* add N business days to D */ #if 0 /* trivial trait, reduce to _add_d() problem and dispatch */ dt_dow_t wd = __ymcw_get_wday(d); return __ymcw_add_d(d, __get_d_equiv(wd, n)); #else signed int aw = n / (signed int)DUWW_BDAYS_P_WEEK; signed int ad = n % (signed int)DUWW_BDAYS_P_WEEK; if ((ad += d.w) > (signed int)DUWW_BDAYS_P_WEEK) { ad -= DUWW_BDAYS_P_WEEK; aw++; } else if (ad <= 0) { ad += DUWW_BDAYS_P_WEEK; aw--; } /* fixup for abswk count, m01 may be any wd */ { dt_dow_t m01 = __get_m01_wday(d.y, d.m); if ((dt_dow_t)d.w < m01 && (dt_dow_t)ad >= m01) { aw++; } else if ((dt_dow_t)d.w >= m01 && (dt_dow_t)ad < m01) { aw--; } } d.w = (dt_dow_t)ad; return __ymcw_add_w(d, aw); #endif }
static dt_ymcw_t __ymcw_add_d(dt_ymcw_t d, int n) { /* add N days to D * we reduce this to __ymcw_add_w() */ signed int aw = n / (signed int)GREG_DAYS_P_WEEK; signed int ad = n % (signed int)GREG_DAYS_P_WEEK; if ((ad += d.w) >= (signed int)GREG_DAYS_P_WEEK) { ad -= GREG_DAYS_P_WEEK; aw++; } else if (ad < 0) { ad += GREG_DAYS_P_WEEK; aw--; } /* fixup for abswk count, m01 may be any wd */ { dt_dow_t m01 = __get_m01_wday(d.y, d.m); if ((dt_dow_t)d.w < m01 && (dt_dow_t)ad >= m01) { aw++; } else if ((dt_dow_t)d.w >= m01 && (dt_dow_t)ad < m01) { aw--; } } d.w = (dt_dow_t)ad; return __ymcw_add_w(d, aw); }
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 dt_dow_t __bizda_get_wday(dt_bizda_t that) { dt_dow_t wd01; unsigned int b; unsigned int magic; /* find first of the month first */ wd01 = __get_m01_wday(that.y, that.m); b = that.bd; magic = (b - 1 + (wd01 < DT_SUNDAY ? wd01 : 6) - 1); /* now just add up bdays */ return (dt_dow_t)((magic % DUWW_BDAYS_P_WEEK) + DT_MONDAY); }
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; }
DEFUN int __ymcw_cmp(dt_ymcw_t d1, dt_ymcw_t d2) { if (d1.y < d2.y) { return -1; } else if (d1.y > d2.y) { return 1; } else if (d1.m < d2.m) { return -1; } else if (d1.m > d2.m) { return 1; } /* we're down to counts, however, the last W of a month is always * count 5, even though counting forward it would be 4 */ if (d1.c < d2.c) { return -1; } else if (d1.c > d2.c) { return 1; } /* now it's up to the first of the month */ { dt_dow_t wd01; unsigned int off1; unsigned int off2; wd01 = __get_m01_wday(d1.y, d1.m); /* represent cw as C-th WD01 + OFF */ off1 = __uimod(d1.w - wd01, GREG_DAYS_P_WEEK); off2 = __uimod(d2.w - wd01, GREG_DAYS_P_WEEK); if (off1 < off2) { return -1; } else if (off1 > off2) { return 1; } else { return 0; } } }
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; } }
static int __ymcw_get_bday(dt_ymcw_t that, dt_bizda_param_t bp) { dt_dow_t wd01; int res; switch (that.w) { case DT_SUNDAY: case DT_SATURDAY: return -1; default: break; } if (bp.ab != BIZDA_AFTER || bp.ref != BIZDA_ULTIMO) { /* no support yet */ return -1; } /* weekday the month started with */ wd01 = __get_m01_wday(that.y, that.m); res = (signed int)(that.w - wd01) + DUWW_BDAYS_P_WEEK * (that.c) + 1; return res; }