"Fossies" - the Fresh Open Source Software Archive

Member "dateutils-0.4.6/lib/ymd.c" (19 Mar 2019, 16542 Bytes) of package /linux/privat/dateutils-0.4.6.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ymd.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.4.5_vs_0.4.6.

    1 /*** ymd.c -- guts for ymd dates
    2  *
    3  * Copyright (C) 2010-2019 Sebastian Freundt
    4  *
    5  * Author:  Sebastian Freundt <freundt@ga-group.nl>
    6  *
    7  * This file is part of dateutils.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  *
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  *
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * 3. Neither the name of the author nor the names of any contributors
   21  *    may be used to endorse or promote products derived from this
   22  *    software without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
   25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   27  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   31  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
   33  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   34  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   35  *
   36  **/
   37 /* set aspect temporarily */
   38 #define ASPECT_YMD
   39 /* permanent aspect, to be read as have we ever seen aspect_ymd */
   40 #if !defined ASPECT_YMD_
   41 #define ASPECT_YMD_
   42 #endif  /* !ASPECT_YMD_ */
   43 
   44 #include "nifty.h"
   45 
   46 /* some algorithmic choices */
   47 #if defined YMD_GET_WDAY_LOOKUP
   48 #elif defined YMD_GET_WDAY_ZELLER
   49 #elif defined YMD_GET_WDAY_SAKAMOTO
   50 #elif defined YMD_GET_WDAY_7YM_ALL
   51 #elif defined YMD_GET_WDAY_7YM_REP
   52 #else
   53 /* default algo */
   54 # define YMD_GET_WDAY_LOOKUP
   55 #endif
   56 
   57 #if !defined DEFUN
   58 # define DEFUN
   59 #endif  /* !DEFUN */
   60 
   61 
   62 #if !defined YMD_ASPECT_HELPERS_
   63 #define YMD_ASPECT_HELPERS_
   64 DEFUN __attribute__((pure)) inline unsigned int
   65 __get_mdays(unsigned int y, unsigned int m)
   66 {
   67 /* get the number of days in Y-M */
   68     unsigned int res;
   69 
   70     if (UNLIKELY(m < 1 || m > GREG_MONTHS_P_YEAR)) {
   71         return 0;
   72     }
   73 
   74     /* use our cumulative yday array */
   75     res = __md_get_yday(y, m + 1, 0);
   76     return res - __md_get_yday(y, m, 0);
   77 }
   78 
   79 DEFUN __attribute__((pure)) dt_ymd_t
   80 __ymd_fixup(dt_ymd_t d)
   81 {
   82 /* given dates like 2012-02-32 this returns 2012-02-29 */
   83     int mdays;
   84 
   85     if (LIKELY(d.d <= 28)) {
   86         /* every month has 28 days in our range */
   87         ;
   88     } else if (UNLIKELY(d.m == 0 || d.m > GREG_MONTHS_P_YEAR)) {
   89         ;
   90     } else if (d.d > (mdays = __get_mdays(d.y, d.m))) {
   91         d.d = mdays;
   92     }
   93     return d;
   94 }
   95 
   96 static dt_dow_t
   97 __get_m01_wday(unsigned int year, unsigned int mon)
   98 {
   99 /* get the weekday of the first of MONTH in YEAR */
  100     unsigned int off;
  101     dt_dow_t cand;
  102 
  103     if (UNLIKELY(mon < 1 || mon > GREG_MONTHS_P_YEAR)) {
  104         return DT_MIRACLEDAY;
  105     }
  106     cand = __get_jan01_wday(year);
  107     off = __md_get_yday(year, mon, 0);
  108     off = (cand + off) % GREG_DAYS_P_WEEK;
  109     return (dt_dow_t)(off ?: DT_SUNDAY);
  110 }
  111 
  112 #if defined YMD_GET_WDAY_LOOKUP
  113 /* lookup version */
  114 static dt_dow_t
  115 __get_dom_wday(unsigned int year, unsigned int mon, unsigned int dom)
  116 {
  117     unsigned int yd;
  118     unsigned int j01_wd;
  119 
  120     if ((yd = __md_get_yday(year, mon, dom)) > 0 &&
  121         (j01_wd = __get_jan01_wday(year)) != DT_MIRACLEDAY) {
  122         unsigned int wd = (yd - 1 + j01_wd) % GREG_DAYS_P_WEEK;
  123         return (dt_dow_t)(wd ?: DT_SUNDAY);
  124     }
  125     return DT_MIRACLEDAY;
  126 }
  127 
  128 #elif defined YMD_GET_WDAY_ZELLER
  129 /* Zeller algorithm */
  130 static dt_dow_t
  131 __get_dom_wday(int year, int mon, int dom)
  132 {
  133 /* this is Zeller's method, but there's a problem when we use this for
  134  * the bizda calendar. */
  135     int w;
  136     int c, x;
  137     int d, y;
  138     unsigned int wd;
  139 
  140     if ((mon -= 2) <= 0) {
  141         mon += 12;
  142         year--;
  143     }
  144 
  145     d = year / 100;
  146     c = year % 100;
  147     x = c / 4;
  148     y = d / 4;
  149 
  150     w = (13 * mon - 1) / 5;
  151     wd = (w + x + y + dom + c - 2 * d) % GREG_DAYS_P_WEEK;
  152     return (dt_dow_t)(wd ?: DT_SUNDAY);
  153 }
  154 #elif defined YMD_GET_WDAY_SAKAMOTO
  155 /* Sakamoto method */
  156 static dt_dow_t
  157 __get_dom_wday(int year, int mon, int dom)
  158 {
  159     static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
  160     int res;
  161     unsigned int wd;
  162 
  163     year -= mon < 3;
  164     res = year + year / 4 - year / 100 + year / 400;
  165     res += t[mon - 1] + dom;
  166     wd = (unsigned int)res % GREG_DAYS_P_WEEK;
  167     return (dt_dow_t)(wd ?: DT_SUNDAY);
  168 }
  169 
  170 #elif defined YMD_GET_WDAY_7YM_ALL || defined YMD_GET_WDAY_7YM_REP
  171 /* this one's work in progress
  172  * first observe the 7 patterns in months FG and H..Z and how leap years
  173  * correspond to ordinary years.
  174  *
  175  * Use:
  176  *   dseq 1917-01-01 1mo 2417-12-01 -f '%Y %m %_a' |           \
  177  *     awk 'BEGIN{FS=" "}                                      \
  178  *          {a[$1] = "" a[$1] "" ($2=="03" ? " " : "") "" $3;} \
  179  *          END{for (i in a) print(i "\t" a[i]);}' |           \
  180  *     sort | sort -k3 -k2 -u
  181  *
  182  * for instance to get this table (months FG and months H..Z):
  183  *   1924   TF ATRSTFMWAM
  184  *   1919   WA ATRSTFMWAM
  185  *   1940   MR FMWAMRSTFS
  186  *   1918   TF FMWAMRSTFS
  187  *   1926   FM MRATRSWFMW
  188  *   1920   RS MRATRSWFMW
  189  *   1917   MR RSTFSWAMRA
  190  *   1928   SW RSTFSWAMRA
  191  *   1925   RS SWFMWATRST
  192  *   1936   WA SWFMWATRST
  193  *   1921   AT TFSWFMRATR
  194  *   1932   FM TFSWFMRATR
  195  *   1944   AT WAMRATFSWF
  196  *   1922   SW WAMRATFSWF
  197  *
  198  * and note how leap years and ordinary years pair up.
  199  *
  200  * After a bit of number crunching (consider all years mod 28) we'll find:
  201  *           H..Z
  202  *  0 -> 6 -> 17 -> 23
  203  *  4 -> 10 -> 21 -> 27
  204  *  8 -> 14 -> 25 -> 3
  205  * 12 -> 18 -> 1 -> 7
  206  * 16 -> 22 -> 5 -> 11
  207  * 20 -> 26 -> 9 -> 15
  208  * 24 -> 2 -> 13 -> 19
  209  *
  210  * where obviously the FG years are a permutation (13 14 15 21 22 17 18)^-1
  211  * of the H..Z years.
  212  *
  213  * It's quite easy to see how this system forms an orbit through C28 via:
  214  *
  215  *   [0]C4 + 0 = [4]C4 + 1 = [1]C4 + 2 = [5]C4 + 3
  216  *   [1]C4 + 0 = [5]C4 + 1 = [2]C4 + 2 = [6]C4 + 3
  217  *   [2]C4 + 0 = [6]C4 + 1 = [3]C4 + 2 = [0]C4 + 3
  218  *   [3]C4 + 0 = [0]C4 + 1 = [4]C4 + 2 = [1]C4 + 3
  219  *   [4]C4 + 0 = [1]C4 + 1 = [5]C4 + 2 = [2]C4 + 3
  220  *   [5]C4 + 0 = [2]C4 + 1 = [6]C4 + 2 = [3]C4 + 3
  221  *   [6]C4 + 0 = [3]C4 + 1 = [0]C4 + 2 = [4]C4 + 3
  222  *
  223  * and so by decomposing a year mod 28 and into C7*C4 we can map it to the
  224  * weekday.
  225  *
  226  * Here's the algo:
  227  * input: year
  228  * output: idx, weekday series index for H..
  229  *
  230  * year <- year mod 28
  231  * x,y  <- decompose year as [x]C4 + y
  232  * idx  <- x - 0, if y = 0
  233  *         x - 4, if y = 1
  234  *         x - 1, if y = 2
  235  *         x - 5, if y = 3
  236  *
  237  * Proceed similar for FG series.
  238  * That's how the formulas below came into being.
  239  *
  240  * For the shortened version (REP for representant) you have observe
  241  * that from leap year to leap year the weekday difference for each
  242  * respective month is 5. */
  243 static unsigned int
  244 __get_widx_H(unsigned int y)
  245 {
  246 /* return the equivalence class number for H.. weekdays for year Y. */
  247     static unsigned int add[] = {0, 3, 6, 2};
  248     unsigned int idx, res;
  249 
  250     y = y % 28U;
  251     idx = y / 4U;
  252     res = y % 4U;
  253     return (idx + add[res]) % GREG_DAYS_P_WEEK;
  254 }
  255 
  256 static unsigned int
  257 __get_widx_FG(unsigned int y)
  258 {
  259 /* return the equivalence class number for FG weekdays for year Y. */
  260     static unsigned int add[] = {0, 6, 2, 5};
  261     unsigned int idx, res;
  262 
  263     y = y % 28U;
  264     idx = y / 4U;
  265     res = y % 4U;
  266     return (idx + add[res]) % GREG_DAYS_P_WEEK;
  267 }
  268 
  269 #if !defined WITH_FAST_ARITH
  270 static inline __attribute__((pure)) unsigned int
  271 __get_28y_year_equiv_H(unsigned year)
  272 {
  273 /* like __get_28y_year_equiv() but for months H..Z
  274  * needed for 7YM algo */
  275     year = year % 400U;
  276 
  277     if (year >= 300U) {
  278         return year + 1600U;
  279     } else if (year >= 200U) {
  280         return year + 1724U;
  281     } else if (year >= 100U) {
  282         return year + 1820U;
  283     }
  284     return year + 2000;
  285 }
  286 #endif  /* !WITH_FAST_ARITH */
  287 
  288 #if defined YMD_GET_WDAY_7YM_ALL
  289 static inline __attribute__((pure)) unsigned int
  290 __get_ser(unsigned int class, unsigned int mon)
  291 {
  292 # define M  (DT_MONDAY)
  293 # define T  (DT_TUESDAY)
  294 # define W  (DT_WEDNESDAY)
  295 # define R  (DT_THURSDAY)
  296 # define F  (DT_FRIDAY)
  297 # define A  (DT_SATURDAY)
  298 # define S  (DT_SUNDAY)
  299     static uint8_t ser[][12] = {
  300         {
  301             /* 1932 = [[0]C4 + 0]C28 */
  302             F, M,  T, F, S, W, F, M, R, A, T, R,
  303         }, {
  304             /* 1936 = [[1]C4 + 0]C28 */
  305             W, A,  S, W, F, M, W, A, T, R, S, T,
  306         }, {
  307             /* 1940 = [[2]C4 + 0]C28 */
  308             M, R,  F, M, W, A, M, R, S, T, F, S,
  309         }, {
  310             /* 1944 = [[3]C4 + 0]C28 */
  311             A, T,  W, A, M, R, A, T, F, S, W, F,
  312         }, {
  313             /* 1920 = [[4]C4 + 0]C28 */
  314             R, S,  M, R, A, T, R, S, W, F, M, W,
  315         }, {
  316             /* 1924 = [[5]C4 + 0]C28 */
  317             T, F,  A, T, R, S, T, F, M, W, A, M,
  318         }, {
  319             /* 1928 = [[6]C4 + 0]C28 */
  320             S, W,  R, S, T, F, S, W, A, M, R, A,
  321         },
  322     };
  323 # undef M
  324 # undef T
  325 # undef W
  326 # undef R
  327 # undef F
  328 # undef A
  329 # undef S
  330     return ser[class][mon];
  331 }
  332 #elif defined YMD_GET_WDAY_7YM_REP
  333 static inline __attribute__((pure)) unsigned int
  334 __get_ser(unsigned int class, unsigned int mon)
  335 {
  336 # define M  (DT_MONDAY)
  337 # define T  (DT_TUESDAY)
  338 # define W  (DT_WEDNESDAY)
  339 # define R  (DT_THURSDAY)
  340 # define F  (DT_FRIDAY)
  341 # define A  (DT_SATURDAY)
  342 # define S  (DT_SUNDAY)
  343     static unsigned int ser[12] = {
  344         /* 1932 = [[0]C4 + 0]C28 */
  345         F, M,  T, F, S, W, F, M, R, A, T, R,
  346     };
  347 # undef M
  348 # undef T
  349 # undef W
  350 # undef R
  351 # undef F
  352 # undef A
  353 # undef S
  354     return ser[mon] + 5 * class;
  355 }
  356 #endif  /* 7YM_ALL | 7YM_RES */
  357 
  358 static dt_dow_t
  359 __get_dom_wday(unsigned int year, unsigned int mon, unsigned int dom)
  360 {
  361     unsigned int bm = mon - 1;
  362     unsigned int bd = dom - 1;
  363     unsigned int idx;
  364     unsigned int wd;
  365 
  366     if (bm < 2U) {
  367         /* FG */
  368 #if defined WITH_FAST_ARITH
  369         unsigned int by = year;
  370 #else  /* !WITH_FAST_ARITH */
  371         unsigned int by = __get_28y_year_equiv(year);
  372 #endif  /* !WITH_FAST_ARITH */
  373         idx = __get_widx_FG(by);
  374     } else {
  375 #if defined WITH_FAST_ARITH
  376         unsigned int by = year;
  377 #else  /* !WITH_FAST_ARITH */
  378         unsigned int by = __get_28y_year_equiv_H(year);
  379 #endif  /* !WITH_FAST_ARITH */
  380         idx = __get_widx_H(by);
  381     }
  382 
  383     wd = (__get_ser(idx, bm) + bd) % GREG_DAYS_P_WEEK;
  384     return (dt_dow_t)(wd ?: DT_SUNDAY);
  385 }
  386 
  387 #endif  /* 0 */
  388 
  389 /* try to get helpers like __get_d_equiv() et al */
  390 #include "bizda.c"
  391 #endif  /* YMD_ASPECT_HELPERS_ */
  392 
  393 
  394 #if defined ASPECT_GETTERS && !defined YMD_ASPECT_GETTERS_
  395 #define YMD_ASPECT_GETTERS_
  396 static unsigned int
  397 __ymd_get_yday(dt_ymd_t that)
  398 {
  399     unsigned int res;
  400 
  401     if (UNLIKELY(that.y == 0 ||
  402              that.m == 0 || that.m > GREG_MONTHS_P_YEAR)) {
  403         return 0;
  404     }
  405     /* process */
  406     res = __md_get_yday(that.y, that.m, that.d);
  407     return res;
  408 }
  409 
  410 static dt_dow_t
  411 __ymd_get_wday(dt_ymd_t that)
  412 {
  413     return __get_dom_wday(that.y, that.m, that.d);
  414 }
  415 
  416 DEFUN unsigned int
  417 __ymd_get_count(dt_ymd_t that)
  418 {
  419 /* get N where N is the N-th occurrence of wday in the month of that year */
  420 #if 0
  421 /* this proves to be a disaster when comparing ymcw dates */
  422     if (UNLIKELY(that.d + GREG_DAYS_P_WEEK > __get_mdays(that.y, that.m))) {
  423         return 5;
  424     }
  425 #endif
  426     return (that.d - 1U) / GREG_DAYS_P_WEEK + 1U;
  427 }
  428 
  429 static int
  430 __ymd_get_wcnt_abs(dt_ymd_t d)
  431 {
  432 /* absolutely count the n-th occurrence of WD regardless what WD
  433  * the year started with
  434  * generally the path ymd->yd->get_wcnt_abs() is preferred */
  435     int yd = __ymd_get_yday(d);
  436 
  437     /* and now express yd as 7k + n relative to jan01 */
  438     return (yd - 1) / 7 + 1;
  439 }
  440 
  441 static int
  442 __ymd_get_bday(dt_ymd_t that, dt_bizda_param_t bp)
  443 {
  444     dt_dow_t wdd;
  445 
  446     if (bp.ab != BIZDA_AFTER || bp.ref != BIZDA_ULTIMO) {
  447         /* no support yet */
  448         return -1;
  449     }
  450 
  451     /* weekday the month started with */
  452     switch ((wdd = __ymd_get_wday(that))) {
  453     case DT_SUNDAY:
  454     case DT_SATURDAY:
  455         return -1;
  456     case DT_MONDAY:
  457     case DT_TUESDAY:
  458     case DT_WEDNESDAY:
  459     case DT_THURSDAY:
  460     case DT_FRIDAY:
  461     case DT_MIRACLEDAY:
  462     default:
  463         break;
  464     }
  465     /* get the number of business days between 1 and that.d */
  466     return __get_nbdays(that.d, wdd);
  467 }
  468 #endif  /* YMD_ASPECT_GETTERS_ */
  469 
  470 
  471 #if defined ASPECT_CONV && !defined YMD_ASPECT_CONV_
  472 #define YMD_ASPECT_CONV_
  473 /* we need some getter stuff, so get it */
  474 #define ASPECT_GETTERS
  475 #include "ymd.c"
  476 #undef ASPECT_GETTERS
  477 
  478 static dt_ymcw_t
  479 __ymd_to_ymcw(dt_ymd_t d)
  480 {
  481     unsigned int c = __ymd_get_count(d);
  482     unsigned int w = __ymd_get_wday(d);
  483 #if defined HAVE_ANON_STRUCTS_INIT
  484     return (dt_ymcw_t){.y = d.y, .m = d.m, .c = c, .w = w};
  485 #else
  486     dt_ymcw_t res;
  487     res.y = d.y;
  488     res.m = d.m;
  489     res.c = c;
  490     res.w = w;
  491     return res;
  492 #endif
  493 }
  494 
  495 static dt_ywd_t
  496 __ymd_to_ywd(dt_ymd_t d)
  497 {
  498     dt_dow_t w = __ymd_get_wday(d);
  499     unsigned int c = __ymd_get_wcnt_abs(d);
  500     return __make_ywd_c(d.y, c, w, YWD_ABSWK_CNT);
  501 }
  502 
  503 static dt_daisy_t
  504 __ymd_to_daisy(dt_ymd_t d)
  505 {
  506     dt_daisy_t res;
  507     unsigned int sy = d.y;
  508     unsigned int sm = d.m;
  509     unsigned int sd;
  510 
  511     if (UNLIKELY((signed int)TO_BASE(sy) < 0)) {
  512         return 0;
  513     }
  514 
  515 #if !defined WITH_FAST_ARITH || defined OMIT_FIXUPS
  516     /* the non-fast arith has done the fixup already */
  517     sd = d.d;
  518 #else  /* WITH_FAST_ARITH && !OMIT_FIXUPS */
  519     {
  520         unsigned int tmp = __get_mdays(sy, sm);
  521         if (UNLIKELY((sd = d.m) > tmp)) {
  522             sd = tmp;
  523         }
  524     }
  525 #endif  /* !WITH_FAST_ARITH || OMIT_FIXUPS */
  526 
  527     res = __jan00_daisy(sy);
  528     res += __md_get_yday(sy, sm, sd);
  529     return res;
  530 }
  531 
  532 static dt_yd_t
  533 __ymd_to_yd(dt_ymd_t d)
  534 {
  535     int yd = __ymd_get_yday(d);
  536 #if defined HAVE_ANON_STRUCTS_INIT
  537     return (dt_yd_t){.y = d.y, .d = yd};
  538 #else
  539     dt_yd_t res;
  540     res.y = d.y;
  541     res.d = yd;
  542     return res;
  543 #endif
  544 }
  545 #endif  /* ASPECT_CONV */
  546 
  547 
  548 #if defined ASPECT_ADD && !defined YMD_ASPECT_ADD_
  549 #define YMD_ASPECT_ADD_
  550 static __attribute__((pure)) dt_ymd_t
  551 __ymd_fixup_d(unsigned int y, signed int m, signed int d)
  552 {
  553     dt_ymd_t res = {0};
  554 
  555     m += !m;
  556     if (LIKELY(d >= 1 && d <= 28)) {
  557         /* all months in our design range have at least 28 days */
  558         ;
  559     } else if (d < 1) {
  560         int mdays;
  561 
  562         do {
  563             if (UNLIKELY(--m < 1)) {
  564                 --y;
  565                 m = GREG_MONTHS_P_YEAR;
  566             }
  567             mdays = __get_mdays(y, m);
  568             d += mdays;
  569         } while (d < 1);
  570 
  571     } else {
  572         int mdays;
  573 
  574         while (d > (mdays = __get_mdays(y, m))) {
  575             d -= mdays;
  576             if (UNLIKELY(++m > (signed int)GREG_MONTHS_P_YEAR)) {
  577                 ++y;
  578                 m = 1;
  579             }
  580         }
  581     }
  582 
  583     res.y = y;
  584     res.m = m;
  585     res.d = d;
  586     return res;
  587 }
  588 
  589 static dt_ymd_t
  590 __ymd_add_d(dt_ymd_t d, int n)
  591 {
  592 /* add N days to D */
  593     signed int tgtd = d.d + n;
  594 
  595     /* fixup the day */
  596     return __ymd_fixup_d(d.y, d.m, tgtd);
  597 }
  598 
  599 static dt_ymd_t
  600 __ymd_add_b(dt_ymd_t d, int n)
  601 {
  602 /* add N business days to D */
  603     dt_dow_t wd = __ymd_get_wday(d);
  604     int tgtd = d.d + __get_d_equiv(wd, n);
  605 
  606     /* fixup the day, i.e. 2012-01-34 -> 2012-02-03 */
  607     return __ymd_fixup_d(d.y, d.m, tgtd);
  608 }
  609 
  610 static dt_ymd_t
  611 __ymd_add_w(dt_ymd_t d, int n)
  612 {
  613 /* add N weeks to D */
  614     return __ymd_add_d(d, GREG_DAYS_P_WEEK * n);
  615 }
  616 
  617 static dt_ymd_t
  618 __ymd_add_m(dt_ymd_t d, int n)
  619 {
  620 /* add N months to D */
  621     signed int tgtm = d.m + n;
  622 
  623     while (tgtm > (signed int)GREG_MONTHS_P_YEAR) {
  624         tgtm -= GREG_MONTHS_P_YEAR;
  625         ++d.y;
  626     }
  627     while (tgtm < 1) {
  628         tgtm += GREG_MONTHS_P_YEAR;
  629         --d.y;
  630     }
  631     /* final assignment */
  632     d.m = tgtm;
  633     return d;
  634 }
  635 
  636 static dt_ymd_t
  637 __ymd_add_y(dt_ymd_t d, int n)
  638 {
  639 /* add N years to D */
  640     d.y += n;
  641     return d;
  642 }
  643 #endif  /* ASPECT_ADD */
  644 
  645 
  646 #if defined ASPECT_DIFF && !defined YMD_ASPECT_DIFF_
  647 #define YMD_ASPECT_DIFF_
  648 static struct dt_ddur_s
  649 __ymd_diff(dt_ymd_t d1, dt_ymd_t d2)
  650 {
  651 /* compute d2 - d1 entirely in terms of ymd */
  652     struct dt_ddur_s res = dt_make_ddur(DT_DURYMD, 0);
  653     signed int tgtd;
  654     signed int tgtm;
  655 
  656     if (d1.u > d2.u) {
  657         /* swap d1 and d2 */
  658         dt_ymd_t tmp = d1;
  659         res.neg = 1;
  660         d1 = d2;
  661         d2 = tmp;
  662     }
  663 
  664     /* first compute the difference in months Y2-M2-01 - Y1-M1-01 */
  665     tgtm = GREG_MONTHS_P_YEAR * (d2.y - d1.y) + (d2.m - d1.m);
  666     if ((tgtd = d2.d - d1.d) < 0 && tgtm != 0) {
  667         /* if tgtm is 0 it remains 0 and tgtd remains negative */
  668         /* get the target month's mdays */
  669         unsigned int d2m = d2.m;
  670         unsigned int d2y = d2.y;
  671 
  672         if (--d2m < 1) {
  673             d2m = GREG_MONTHS_P_YEAR;
  674             d2y--;
  675         }
  676         tgtd += __get_mdays(d2y, d2m);
  677         tgtm--;
  678 #if !defined WITH_FAST_ARITH || defined OMIT_FIXUPS
  679         /* the non-fast arith has done the fixup already */
  680 #else  /* WITH_FAST_ARITH && !defined OMIT_FIXUPS */
  681     } else if (tgtm == 0) {
  682         /* check if we're not diffing two lazy representations
  683          * e.g. 2010-02-28 and 2010-02-31 */
  684         ;
  685 #endif  /* !OMIT_FIXUPS */
  686     }
  687     /* fill in the results */
  688     res.ymd.y = tgtm / GREG_MONTHS_P_YEAR;
  689     res.ymd.m = tgtm % GREG_MONTHS_P_YEAR;
  690     res.ymd.d = tgtd;
  691     return res;
  692 }
  693 #endif  /* ASPECT_DIFF */
  694 
  695 
  696 #if defined ASPECT_STRF && !defined YMD_ASPECT_STRF_
  697 #define YMD_ASPECT_STRF_
  698 
  699 #endif  /* ASPECT_STRF */
  700 
  701 #undef ASPECT_YMD
  702 
  703 /* ymd.c ends here */