xref: /freebsd/bin/date/vary.c (revision 698f86e401d193b01b09cf1ae7d1a26a5652deac)
17ca215a6SBrian Somers #include <time.h>
27ca215a6SBrian Somers #include <string.h>
37ca215a6SBrian Somers #include <stdlib.h>
47ca215a6SBrian Somers #include "vary.h"
57ca215a6SBrian Somers 
67ca215a6SBrian Somers struct trans {
77ca215a6SBrian Somers   int val;
87ca215a6SBrian Somers   char *str;
97ca215a6SBrian Somers };
107ca215a6SBrian Somers 
117ca215a6SBrian Somers static struct trans trans_mon[] = {
127ca215a6SBrian Somers   { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
137ca215a6SBrian Somers   { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" },
147ca215a6SBrian Somers   { 10, "october" }, { 11, "november" }, { 12, "december" },
157ca215a6SBrian Somers   { -1, NULL }
167ca215a6SBrian Somers };
177ca215a6SBrian Somers 
187ca215a6SBrian Somers static struct trans trans_wday[] = {
197ca215a6SBrian Somers   { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
207ca215a6SBrian Somers   { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
217ca215a6SBrian Somers   { -1, NULL }
227ca215a6SBrian Somers };
237ca215a6SBrian Somers 
247ca215a6SBrian Somers static char digits[] = "0123456789";
257ca215a6SBrian Somers 
267ca215a6SBrian Somers static int
277ca215a6SBrian Somers trans(const struct trans t[], const char *arg)
287ca215a6SBrian Somers {
297ca215a6SBrian Somers   int f;
307ca215a6SBrian Somers 
317ca215a6SBrian Somers   for (f = 0; t[f].val != -1; f++)
32698f86e4SBrian Somers     if (!strncasecmp(t[f].str, arg, 3) ||
33698f86e4SBrian Somers         !strncasecmp(t[f].str, arg, strlen(t[f].str)))
347ca215a6SBrian Somers       return t[f].val;
357ca215a6SBrian Somers 
367ca215a6SBrian Somers   return -1;
377ca215a6SBrian Somers }
387ca215a6SBrian Somers 
397ca215a6SBrian Somers struct vary *
40698f86e4SBrian Somers vary_append(struct vary *v, char *arg)
417ca215a6SBrian Somers {
427ca215a6SBrian Somers   struct vary *result, **nextp;
437ca215a6SBrian Somers 
447ca215a6SBrian Somers   if (v) {
457ca215a6SBrian Somers     result = v;
467ca215a6SBrian Somers     while (v->next)
477ca215a6SBrian Somers       v = v->next;
487ca215a6SBrian Somers     nextp = &v->next;
497ca215a6SBrian Somers   } else
507ca215a6SBrian Somers     nextp = &result;
517ca215a6SBrian Somers 
527ca215a6SBrian Somers   *nextp = (struct vary *)malloc(sizeof(struct vary));
537ca215a6SBrian Somers   (*nextp)->arg = arg;
547ca215a6SBrian Somers   (*nextp)->next = NULL;
557ca215a6SBrian Somers   return result;
567ca215a6SBrian Somers }
577ca215a6SBrian Somers 
587ca215a6SBrian Somers static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
597ca215a6SBrian Somers 
607ca215a6SBrian Somers static int
617ca215a6SBrian Somers daysinmonth(const struct tm *t)
627ca215a6SBrian Somers {
637ca215a6SBrian Somers   int year;
647ca215a6SBrian Somers 
657ca215a6SBrian Somers   year = t->tm_year + 1900;
667ca215a6SBrian Somers 
677ca215a6SBrian Somers   if (t->tm_mon == 1)
687ca215a6SBrian Somers     if (!(year % 400))
697ca215a6SBrian Somers       return 29;
707ca215a6SBrian Somers     else if (!(year % 100))
717ca215a6SBrian Somers       return 28;
727ca215a6SBrian Somers     else if (!(year % 4))
737ca215a6SBrian Somers       return 29;
747ca215a6SBrian Somers     else
757ca215a6SBrian Somers       return 28;
767ca215a6SBrian Somers   else if (t->tm_mon >= 0 && t->tm_mon < 12)
777ca215a6SBrian Somers     return mdays[t->tm_mon];
787ca215a6SBrian Somers 
797ca215a6SBrian Somers   return 0;
807ca215a6SBrian Somers }
817ca215a6SBrian Somers 
827ca215a6SBrian Somers 
837ca215a6SBrian Somers static int
847ca215a6SBrian Somers adjyear(struct tm *t, char type, int val)
857ca215a6SBrian Somers {
867ca215a6SBrian Somers   switch (type) {
877ca215a6SBrian Somers     case '+':
887ca215a6SBrian Somers       t->tm_year += val;
897ca215a6SBrian Somers       break;
907ca215a6SBrian Somers     case '-':
917ca215a6SBrian Somers       t->tm_year -= val;
927ca215a6SBrian Somers       break;
937ca215a6SBrian Somers     default:
947ca215a6SBrian Somers       t->tm_year = val;
957ca215a6SBrian Somers       if (t->tm_year < 69)
967ca215a6SBrian Somers       	t->tm_year += 100;		/* as per date.c */
977ca215a6SBrian Somers       else if (t->tm_year > 1900)
987ca215a6SBrian Somers         t->tm_year -= 1900;             /* struct tm holds years since 1900 */
997ca215a6SBrian Somers       break;
1007ca215a6SBrian Somers   }
1017ca215a6SBrian Somers   return mktime(t) != -1;
1027ca215a6SBrian Somers }
1037ca215a6SBrian Somers 
1047ca215a6SBrian Somers static int
1057ca215a6SBrian Somers adjmon(struct tm *t, char type, int val, int istext)
1067ca215a6SBrian Somers {
1077ca215a6SBrian Somers   if (val < 0)
1087ca215a6SBrian Somers     return 0;
1097ca215a6SBrian Somers 
1107ca215a6SBrian Somers   switch (type) {
1117ca215a6SBrian Somers     case '+':
1127ca215a6SBrian Somers       if (istext)
1137ca215a6SBrian Somers         if (val <= t->tm_mon)
1147ca215a6SBrian Somers           val += 11 - t->tm_mon;	/* early next year */
1157ca215a6SBrian Somers         else
1167ca215a6SBrian Somers           val -= t->tm_mon + 1;		/* later this year */
1177ca215a6SBrian Somers       if (!adjyear(t, '+', (t->tm_mon + val) / 12))
1187ca215a6SBrian Somers         return 0;
1197ca215a6SBrian Somers       val %= 12;
1207ca215a6SBrian Somers       t->tm_mon += val;
1217ca215a6SBrian Somers       if (t->tm_mon > 11)
1227ca215a6SBrian Somers         t->tm_mon -= 12;
1237ca215a6SBrian Somers       break;
1247ca215a6SBrian Somers 
1257ca215a6SBrian Somers     case '-':
1267ca215a6SBrian Somers       if (istext)
1277ca215a6SBrian Somers         if (val-1 > t->tm_mon)
1287ca215a6SBrian Somers           val = 13 - val + t->tm_mon;	/* later last year */
1297ca215a6SBrian Somers         else
1307ca215a6SBrian Somers           val = t->tm_mon - val + 1;	/* early this year */
1317ca215a6SBrian Somers       if (!adjyear(t, '-', val / 12))
1327ca215a6SBrian Somers         return 0;
1337ca215a6SBrian Somers       val %= 12;
1347ca215a6SBrian Somers       if (val > t->tm_mon) {
1357ca215a6SBrian Somers         if (!adjyear(t, '-', 1))
1367ca215a6SBrian Somers           return 0;
1377ca215a6SBrian Somers         val -= 12;
1387ca215a6SBrian Somers       }
1397ca215a6SBrian Somers       t->tm_mon -= val;
1407ca215a6SBrian Somers       break;
1417ca215a6SBrian Somers 
1427ca215a6SBrian Somers     default:
1437ca215a6SBrian Somers       if (val > 12 || val < 1)
1447ca215a6SBrian Somers         return 0;
1457ca215a6SBrian Somers       t->tm_mon = --val;
1467ca215a6SBrian Somers   }
1477ca215a6SBrian Somers 
1487ca215a6SBrian Somers   return mktime(t) != -1;
1497ca215a6SBrian Somers }
1507ca215a6SBrian Somers 
1517ca215a6SBrian Somers static int
1527ca215a6SBrian Somers adjday(struct tm *t, char type, int val)
1537ca215a6SBrian Somers {
1547ca215a6SBrian Somers   int mdays;
1557ca215a6SBrian Somers   switch (type) {
1567ca215a6SBrian Somers     case '+':
1577ca215a6SBrian Somers       while (val) {
1587ca215a6SBrian Somers         mdays = daysinmonth(t);
1597ca215a6SBrian Somers         if (val > mdays - t->tm_mday) {
1607ca215a6SBrian Somers           val -= mdays - t->tm_mday + 1;
1617ca215a6SBrian Somers           t->tm_mday = 1;
1627ca215a6SBrian Somers           if (!adjmon(t, '+', 1, 0))
1637ca215a6SBrian Somers             return 0;
1647ca215a6SBrian Somers         } else {
1657ca215a6SBrian Somers           t->tm_mday += val;
1667ca215a6SBrian Somers           val = 0;
1677ca215a6SBrian Somers         }
1687ca215a6SBrian Somers       }
1697ca215a6SBrian Somers       break;
1707ca215a6SBrian Somers     case '-':
1717ca215a6SBrian Somers       while (val)
1727ca215a6SBrian Somers         if (val >= t->tm_mday) {
1737ca215a6SBrian Somers           val -= t->tm_mday;
1747ca215a6SBrian Somers           t->tm_mday = 1;
1757ca215a6SBrian Somers           if (!adjmon(t, '-', 1, 0))
1767ca215a6SBrian Somers             return 0;
1777ca215a6SBrian Somers           t->tm_mday = daysinmonth(t);
1787ca215a6SBrian Somers         } else {
1797ca215a6SBrian Somers           t->tm_mday -= val;
1807ca215a6SBrian Somers           val = 0;
1817ca215a6SBrian Somers         }
1827ca215a6SBrian Somers       break;
1837ca215a6SBrian Somers     default:
1847ca215a6SBrian Somers       if (val > 0 && val <= daysinmonth(t))
1857ca215a6SBrian Somers         t->tm_mday = val;
1867ca215a6SBrian Somers       else
1877ca215a6SBrian Somers         return 0;
1887ca215a6SBrian Somers       break;
1897ca215a6SBrian Somers   }
1907ca215a6SBrian Somers 
1917ca215a6SBrian Somers   return mktime(t) != -1;
1927ca215a6SBrian Somers }
1937ca215a6SBrian Somers 
1947ca215a6SBrian Somers static int
195698f86e4SBrian Somers adjwday(struct tm *t, char type, int val, int istext)
1967ca215a6SBrian Somers {
1977ca215a6SBrian Somers   if (val < 0)
1987ca215a6SBrian Somers     return 0;
1997ca215a6SBrian Somers 
200698f86e4SBrian Somers   switch (type) {
2017ca215a6SBrian Somers     case '+':
2027ca215a6SBrian Somers       if (istext)
2037ca215a6SBrian Somers         if (val < t->tm_wday)
2047ca215a6SBrian Somers           val = 7 - t->tm_wday + val;  /* early next week */
2057ca215a6SBrian Somers         else
2067ca215a6SBrian Somers           val -= t->tm_wday;           /* later this week */
2077ca215a6SBrian Somers       else
2087ca215a6SBrian Somers         val *= 7;                      /* "-W +5" == "5 weeks in the future" */
2097ca215a6SBrian Somers       return adjday(t, '+', val);
2107ca215a6SBrian Somers     case '-':
2117ca215a6SBrian Somers       if (istext)
2127ca215a6SBrian Somers         if (val > t->tm_wday)
2137ca215a6SBrian Somers           val = 7 - val + t->tm_wday;  /* later last week */
2147ca215a6SBrian Somers         else
2157ca215a6SBrian Somers           val = t->tm_wday - val;      /* early this week */
2167ca215a6SBrian Somers       else
2177ca215a6SBrian Somers         val *= 7;                      /* "-W -5" == "5 weeks ago" */
2187ca215a6SBrian Somers       return adjday(t, '-', val);
2197ca215a6SBrian Somers     default:
2207ca215a6SBrian Somers       if (val < t->tm_wday)
2217ca215a6SBrian Somers         return adjday(t, '-', t->tm_wday - val);
2227ca215a6SBrian Somers       else if (val > 6)
2237ca215a6SBrian Somers         return 0;
2247ca215a6SBrian Somers       else if (val > t->tm_wday)
2257ca215a6SBrian Somers         return adjday(t, '+', val - t->tm_wday);
2267ca215a6SBrian Somers   }
2277ca215a6SBrian Somers   return 1;
2287ca215a6SBrian Somers }
2297ca215a6SBrian Somers 
2307ca215a6SBrian Somers static int
231698f86e4SBrian Somers adjhour(struct tm *t, char type, int val)
2327ca215a6SBrian Somers {
233698f86e4SBrian Somers   if (val < 0)
234698f86e4SBrian Somers     return 0;
235698f86e4SBrian Somers 
236698f86e4SBrian Somers   switch (type) {
2377ca215a6SBrian Somers     case '+':
238698f86e4SBrian Somers       if (!adjday(t, '+', (t->tm_hour + val) / 24))
239698f86e4SBrian Somers         return 0;
240698f86e4SBrian Somers       val %= 24;
241698f86e4SBrian Somers       t->tm_hour += val;
242698f86e4SBrian Somers       if (t->tm_hour > 23)
243698f86e4SBrian Somers         t->tm_hour -= 24;
244698f86e4SBrian Somers       break;
245698f86e4SBrian Somers 
2467ca215a6SBrian Somers     case '-':
247698f86e4SBrian Somers       if (!adjday(t, '-', val / 24))
248698f86e4SBrian Somers         return 0;
249698f86e4SBrian Somers       val %= 24;
250698f86e4SBrian Somers       if (val > t->tm_hour) {
251698f86e4SBrian Somers         if (!adjday(t, '-', 1))
252698f86e4SBrian Somers           return 0;
253698f86e4SBrian Somers         val -= 24;
2547ca215a6SBrian Somers       }
255698f86e4SBrian Somers       t->tm_hour -= val;
256698f86e4SBrian Somers       break;
257698f86e4SBrian Somers 
258698f86e4SBrian Somers     default:
259698f86e4SBrian Somers       if (val > 23)
260698f86e4SBrian Somers         return 0;
261698f86e4SBrian Somers       t->tm_hour = val;
262698f86e4SBrian Somers   }
263698f86e4SBrian Somers 
264698f86e4SBrian Somers   return mktime(t) != -1;
265698f86e4SBrian Somers }
266698f86e4SBrian Somers 
267698f86e4SBrian Somers static int
268698f86e4SBrian Somers adjmin(struct tm *t, char type, int val)
269698f86e4SBrian Somers {
270698f86e4SBrian Somers   if (val < 0)
271698f86e4SBrian Somers     return 0;
272698f86e4SBrian Somers 
273698f86e4SBrian Somers   switch (type) {
274698f86e4SBrian Somers     case '+':
275698f86e4SBrian Somers       if (!adjhour(t, '+', (t->tm_min + val) / 60))
276698f86e4SBrian Somers         return 0;
277698f86e4SBrian Somers       val %= 60;
278698f86e4SBrian Somers       t->tm_min += val;
279698f86e4SBrian Somers       if (t->tm_min > 59)
280698f86e4SBrian Somers         t->tm_min -= 60;
281698f86e4SBrian Somers       break;
282698f86e4SBrian Somers 
283698f86e4SBrian Somers     case '-':
284698f86e4SBrian Somers       if (!adjhour(t, '-', val / 60))
285698f86e4SBrian Somers         return 0;
286698f86e4SBrian Somers       val %= 60;
287698f86e4SBrian Somers       if (val > t->tm_min) {
288698f86e4SBrian Somers         if (!adjhour(t, '-', 1))
289698f86e4SBrian Somers           return 0;
290698f86e4SBrian Somers         val -= 60;
291698f86e4SBrian Somers       }
292698f86e4SBrian Somers       t->tm_min -= val;
293698f86e4SBrian Somers       break;
294698f86e4SBrian Somers 
295698f86e4SBrian Somers     default:
296698f86e4SBrian Somers       if (val > 59)
297698f86e4SBrian Somers         return 0;
298698f86e4SBrian Somers       t->tm_min = val;
299698f86e4SBrian Somers   }
300698f86e4SBrian Somers 
301698f86e4SBrian Somers   return mktime(t) != -1;
3027ca215a6SBrian Somers }
3037ca215a6SBrian Somers 
3047ca215a6SBrian Somers const struct vary *
3057ca215a6SBrian Somers vary_apply(const struct vary *v, struct tm *t)
3067ca215a6SBrian Somers {
307698f86e4SBrian Somers   char type;
308698f86e4SBrian Somers   char which;
309698f86e4SBrian Somers   char *arg;
310698f86e4SBrian Somers   int len;
311698f86e4SBrian Somers   int val;
312698f86e4SBrian Somers 
3137ca215a6SBrian Somers   for (; v; v = v->next) {
314698f86e4SBrian Somers     type = *v->arg;
315698f86e4SBrian Somers     arg = v->arg;
316698f86e4SBrian Somers     if (type == '+' || type == '-')
317698f86e4SBrian Somers       arg++;
318698f86e4SBrian Somers     else
319698f86e4SBrian Somers       type = '\0';
320698f86e4SBrian Somers     len = strlen(arg);
321698f86e4SBrian Somers     if (len < 2)
3227ca215a6SBrian Somers       return v;
323698f86e4SBrian Somers 
324698f86e4SBrian Somers     if (strspn(arg, digits) != len-1) {
325698f86e4SBrian Somers       val = trans(trans_wday, arg);
326698f86e4SBrian Somers       if (val != -1) {
327698f86e4SBrian Somers           if (!adjwday(t, type, val, 1))
3287ca215a6SBrian Somers             return v;
329698f86e4SBrian Somers       } else {
330698f86e4SBrian Somers         val = trans(trans_mon, arg);
331698f86e4SBrian Somers         if (val != -1) {
332698f86e4SBrian Somers           if (!adjmon(t, type, val, 1))
333698f86e4SBrian Somers             return v;
334698f86e4SBrian Somers         } else
335698f86e4SBrian Somers           return v;
336698f86e4SBrian Somers       }
337698f86e4SBrian Somers     } else {
338698f86e4SBrian Somers       val = atoi(arg);
339698f86e4SBrian Somers       which = arg[len-1];
340698f86e4SBrian Somers 
341698f86e4SBrian Somers       switch (which) {
3427ca215a6SBrian Somers         case 'M':
343698f86e4SBrian Somers           if (!adjmin(t, type, val))
3447ca215a6SBrian Somers             return v;
3457ca215a6SBrian Somers           break;
346698f86e4SBrian Somers         case 'H':
347698f86e4SBrian Somers           if (!adjhour(t, type, val))
348698f86e4SBrian Somers             return v;
349698f86e4SBrian Somers           break;
350698f86e4SBrian Somers         case 'd':
351698f86e4SBrian Somers           if (!adjday(t, type, val))
352698f86e4SBrian Somers             return v;
353698f86e4SBrian Somers           break;
354698f86e4SBrian Somers         case 'w':
355698f86e4SBrian Somers           if (!adjwday(t, type, val, 0))
356698f86e4SBrian Somers             return v;
357698f86e4SBrian Somers           break;
358698f86e4SBrian Somers         case 'm':
359698f86e4SBrian Somers           if (!adjmon(t, type, val, 0))
360698f86e4SBrian Somers             return v;
361698f86e4SBrian Somers           break;
362698f86e4SBrian Somers         case 'y':
363698f86e4SBrian Somers           if (!adjyear(t, type, val))
3647ca215a6SBrian Somers             return v;
3657ca215a6SBrian Somers           break;
3667ca215a6SBrian Somers         default:
3677ca215a6SBrian Somers           return v;
3687ca215a6SBrian Somers       }
3697ca215a6SBrian Somers     }
370698f86e4SBrian Somers   }
3717ca215a6SBrian Somers   return 0;
3727ca215a6SBrian Somers }
3737ca215a6SBrian Somers 
3747ca215a6SBrian Somers void
3757ca215a6SBrian Somers vary_destroy(struct vary *v)
3767ca215a6SBrian Somers {
3777ca215a6SBrian Somers   struct vary *n;
3787ca215a6SBrian Somers 
3797ca215a6SBrian Somers   while (v) {
3807ca215a6SBrian Somers     n = v->next;
3817ca215a6SBrian Somers     free(v);
3827ca215a6SBrian Somers     v = n;
3837ca215a6SBrian Somers   }
3847ca215a6SBrian Somers }
385