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; unsigned int m28wd; /* rd should not overflow */ assert((signed int)md - 28 >= 0); /* wday of the 1st and 28th */ m01wd = __get_m01_wday(y, m); m28wd = __uimod((signed int)m01wd - 1, GREG_DAYS_P_WEEK); if (LIKELY(rd > 0)) { switch ((dt_dow_t)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; }
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; } } }