---
lib/date.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 455 insertions(+), 0 deletions(-)
create mode 100644 lib/date.c
diff --git a/lib/date.c b/lib/date.c
new file mode 100644
index 0000000..611ae15
--- /dev/null
+++ b/lib/date.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright ? 2009 Keith Packard <keithp at keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "notmuch.h"
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define DAY (24 * 60 * 60)
+
+static void
+today(struct tm *result, time_t after) {
+ time_t t;
+
+ if (after)
+ t = after;
+ else
+ time(&t);
+ localtime_r(&t, result);
+ result->tm_sec = result->tm_min = result->tm_hour = 0;
+}
+
+static int parse_today(const char *text, time_t *first, time_t *last, time_t
after) {
+ if (strcasecmp(text, "today") == 0) {
+ struct tm n;
+ today(&n, 0);
+ *first = mktime(&n);
+ *last = *first + DAY;
+ return 0;
+ }
+ return 1;
+}
+
+static int parse_yesterday(const char *text, time_t *first, time_t *last,
time_t after) {
+ if (strcasecmp(text, "yesterday") == 0) {
+ struct tm n;
+ today(&n, 0);
+ *last = mktime(&n);
+ *first = *last - DAY;
+ return 0;
+ }
+ return 1;
+}
+
+static int parse_thisweek(const char *text, time_t *first, time_t *last,
time_t after) {
+ if (strcasecmp(text, "thisweek") == 0) {
+ struct tm n;
+ today(&n, 0);
+ *first = mktime(&n) - (n.tm_wday * DAY);
+ *last = *first + DAY * 7;
+ return 0;
+ }
+ return 1;
+}
+
+static int parse_lastweek(const char *text, time_t *first, time_t *last,
time_t after) {
+ if (strcasecmp(text, "lastweek") == 0) {
+ struct tm n;
+ today(&n, 0);
+ *last = mktime(&n) - (n.tm_wday * DAY);
+ *first = *last - DAY * 7;
+ return 0;
+ }
+ return 1;
+}
+
+static int parse_thismonth(const char *text, time_t *first, time_t *last,
time_t after) {
+ if (strcasecmp(text, "thismonth") == 0) {
+ struct tm n;
+ today(&n, 0);
+ n.tm_mday = 1;
+ *first = mktime(&n);
+ if (n.tm_mon++ == 12) {
+ n.tm_mon = 0;
+ n.tm_year++;
+ }
+ *last = mktime(&n);
+ return 0;
+ }
+ return 1;
+}
+
+static int parse_lastmonth(const char *text, time_t *first, time_t *last,
time_t after) {
+ if (strcasecmp(text, "lastmonth") == 0) {
+ struct tm n;
+ today(&n, 0);
+ n.tm_mday = 1;
+ if (n.tm_mon == 0) {
+ n.tm_year--;
+ n.tm_mon = 11;
+ } else
+ n.tm_mon--;
+ *first = mktime(&n);
+ if (n.tm_mon++ == 12) {
+ n.tm_mon = 0;
+ n.tm_year++;
+ }
+ *last = mktime(&n);
+ return 0;
+ }
+ return 1;
+}
+
+static const char *months[12][2] = {
+ { "January", "Jan" },
+ { "February", "Feb" },
+ { "March", "Mar" },
+ { "April", "Apr" },
+ { "May", "May" },
+ { "June", "Jun" },
+ { "July", "Jul" },
+ { "August", "Aug" },
+ { "September", "Sep" },
+ { "October", "Oct" },
+ { "November", "Nov" },
+ { "December", "Dec" },
+};
+
+static int year(const char *text, int *y) {
+ char *end;
+ *y = strtol(text, &end, 10);
+ if (end == text)
+ return 1;
+ if (*end != '\0')
+ return 1;
+ if (*y < 1970 || *y > 2038)
+ return 1;
+ *y -= 1900;
+ return 0;
+}
+
+static int month(const char *text, int *m) {
+ char *end;
+ int i;
+ for (i = 0; i < 12; i++) {
+ if (strcasecmp(text, months[i][0]) == 0 ||
+ strcasecmp(text, months[i][1]) == 0)
+ {
+ *m = i;
+ return 0;
+ }
+ }
+ *m = strtol(text, &end, 10);
+ if (end == text)
+ return 1;
+ if (*end != '\0')
+ return 1;
+ if (*m < 1 || *m > 12)
+ return 1;
+ *m -= 1;
+ return 0;
+}
+
+static int day(const char *text, int *d) {
+ char *end;
+ *d = strtol(text, &end, 10);
+ if (end == text)
+ return 1;
+ if (*end != '\0')
+ return 1;
+ if (*d < 1 || *d > 31)
+ return 1;
+ return 0;
+}
+
+/* month[-day] */
+static int parse_month(const char *text, time_t *first, time_t *last, time_t
after) {
+ int m = 0, d = 0;
+ int i;
+ struct tm n;
+ char tmp[80];
+ char *t;
+ char *save;
+ char *token;
+
+ if(strlen (text) >= sizeof (tmp))
+ return 1;
+ strcpy(tmp, text);
+
+ t = tmp;
+ save = NULL;
+ i = 0;
+ while ((token = strtok_r(t, "-", &save)) != NULL) {
+ i++;
+ switch(i) {
+ case 1:
+ if (month(token, &m) != 0)
+ return 1;
+ break;
+ case 2:
+ if (day(token, &d) != 0)
+ return 1;
+ break;
+ default:
+ return 1;
+ }
+ t = NULL;
+ }
+ today(&n, after);
+ if (after) {
+ if (m < n.tm_mon)
+ n.tm_year++;
+ } else {
+ if (m > n.tm_mon)
+ n.tm_year--;
+ }
+ switch (i) {
+ case 1:
+ n.tm_mday = 1;
+ n.tm_mon = m;
+ *first = mktime(&n);
+ if (++n.tm_mon > 11) {
+ n.tm_mon = 0;
+ n.tm_year++;
+ }
+ *last = mktime(&n);
+ return 0;
+ case 2:
+ n.tm_mday = d;
+ n.tm_mon = m;
+ *first = mktime(&n);
+ *last = *first + DAY;
+ return 0;
+ }
+ return 1;
+}
+
+/* year[-month[-day]] */
+static int parse_iso(const char *text, time_t *first, time_t *last, time_t
after) {
+ int y = 0, m = 0, d = 0;
+ int i;
+ struct tm n;
+ char tmp[80];
+ char *t;
+ char *save;
+ char *token;
+
+ if(strlen (text) >= sizeof (tmp))
+ return 1;
+ strcpy(tmp, text);
+
+ t = tmp;
+ save = NULL;
+ i = 0;
+ while ((token = strtok_r(t, "-", &save)) != NULL) {
+ i++;
+ switch(i) {
+ case 1:
+ if (year(token, &y) != 0)
+ return 1;
+ break;
+ case 2:
+ if (month(token, &m) != 0)
+ return 1;
+ break;
+ case 3:
+ if (day(token, &d) != 0)
+ return 1;
+ break;
+ default:
+ return 1;
+ }
+ t = NULL;
+ }
+ today(&n, 0);
+ switch (i) {
+ case 1:
+ n.tm_mday = 1;
+ n.tm_mon = 0;
+ n.tm_year = y;
+ *first = mktime(&n);
+ n.tm_year = y + 1;
+ *last = mktime(&n);
+ return 0;
+ case 2:
+ n.tm_mday = 1;
+ n.tm_mon = m;
+ n.tm_year = y;
+ *first = mktime(&n);
+ if (++n.tm_mon > 11) {
+ n.tm_mon = 0;
+ n.tm_year++;
+ }
+ *last = mktime(&n);
+ return 0;
+ case 3:
+ n.tm_mday = d;
+ n.tm_mon = m;
+ n.tm_year = y;
+ *first = mktime(&n);
+ *last = *first + DAY;
+ return 0;
+ }
+ return 1;
+}
+
+/* month[/day[/year]] */
+static int parse_us(const char *text, time_t *first, time_t *last, time_t
after) {
+ int y = 0, m = 0, d = 0;
+ int i;
+ struct tm n;
+ char tmp[80];
+ char *t;
+ char *save;
+ char *token;
+
+ if(strlen (text) >= sizeof (tmp))
+ return 1;
+ strcpy(tmp, text);
+
+ t = tmp;
+ save = NULL;
+ i = 0;
+ while ((token = strtok_r(t, "/", &save)) != NULL) {
+ i++;
+ switch(i) {
+ case 1:
+ if (month(token, &m) != 0)
+ return 1;
+ break;
+ case 2:
+ if (day(token, &d) != 0)
+ return 1;
+ break;
+ case 3:
+ if (year(token, &y) != 0)
+ return 1;
+ break;
+ default:
+ return 1;
+ }
+ t = NULL;
+ }
+ today(&n, after);
+ if (after) {
+ if (m < n.tm_mon)
+ n.tm_year++;
+ } else {
+ if (m > n.tm_mon)
+ n.tm_year--;
+ }
+ switch (i) {
+ case 1:
+ n.tm_mday = 1;
+ n.tm_mon = m;
+ *first = mktime(&n);
+ if (++n.tm_mon > 11) {
+ n.tm_mon = 0;
+ n.tm_year++;
+ }
+ *last = mktime(&n);
+ return 0;
+ case 2:
+ n.tm_mday = d;
+ n.tm_mon = m;
+ *first = mktime(&n);
+ *last = *first + DAY;
+ return 0;
+ case 3:
+ n.tm_mday = d;
+ n.tm_mon = m;
+ n.tm_year = y;
+ *first = mktime(&n);
+ *last = *first + DAY;
+ return 0;
+ }
+ return 1;
+}
+
+static int (*parsers[])(const char *text, time_t *first, time_t *last, time_t
after) = {
+ parse_today,
+ parse_yesterday,
+ parse_thisweek,
+ parse_lastweek,
+ parse_thismonth,
+ parse_lastmonth,
+ parse_month,
+ parse_iso,
+ parse_us,
+ 0,
+};
+
+static notmuch_status_t
+notmuch_one_date(const char *text, time_t *first, time_t *last, time_t after)
+{
+ int i;
+ for (i = 0; parsers[i]; i++)
+ if (parsers[i](text, first, last, after) == 0)
+ return NOTMUCH_STATUS_SUCCESS;
+ return NOTMUCH_STATUS_INVALID_DATE;
+}
+
+notmuch_status_t
+notmuch_date(const char *text, time_t *first, time_t *last)
+{
+ char *dots;
+ char first_text[80], last_text[80];
+ notmuch_status_t status;
+ time_t first_first, first_last, last_first, last_last;
+
+ if (strlen(text) > sizeof (first_text))
+ return NOTMUCH_STATUS_INVALID_DATE;
+ dots = strstr(text, "..");
+ if (dots) {
+ strncpy(first_text, text, dots - text);
+ first_text[dots-text] = '\0';
+ status = notmuch_one_date(first_text, &first_first, &first_last, 0);
+ if (status)
+ return status;
+ status = notmuch_one_date(dots + 2, &last_first, &last_last,
first_first);
+ if (status)
+ return status;
+ *first = first_first;
+ *last = last_last;
+ return 0;
+ }
+ return notmuch_one_date(text, first, last, 0);
+}
+
+#if 1
+int
+main (int argc, char **argv)
+{
+ int i;
+ for (i = 1; i < argc; i++) {
+ time_t first, last;
+
+ if (notmuch_date(argv[i], &first, &last) == 0) {
+ char first_string[80], last_string[80];
+
+ ctime_r(&first, first_string);
+ first_string[strlen(first_string)-1] = '\0';
+ ctime_r(&last, last_string);
+ last_string[strlen(last_string)-1] = '\0';
+ printf ("%s: %s - %s\n", argv[i], first_string, last_string);
+ }
+ }
+}
+#endif
--
1.6.6
--
keith.packard at intel.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL:
<http://notmuchmail.org/pipermail/notmuch/attachments/20100125/1fa59cc1/attachment-0001.pgp>