]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix "tomorrow UTC" and similar phrases by not setting the default
authorTim Kientzle <kientzle@gmail.com>
Tue, 3 Mar 2009 03:55:54 +0000 (22:55 -0500)
committerTim Kientzle <kientzle@gmail.com>
Tue, 3 Mar 2009 03:55:54 +0000 (22:55 -0500)
time elements until after we've parsed the string.  Then we can
use the parsed timezone (if any) to set up the default time elements.
In particular, this makes this code a lot easier to test, since
otherwise local DST keeps intruding.

SVN-Revision: 720

tar/getdate.c
tar/test/test_getdate.c

index 5854f48176dd734cb2f94a18695ac74f107fdf2d..8df1e26fba779ce30ba855e4b35002ac60ce3841 100644 (file)
@@ -64,8 +64,10 @@ struct gdstate {
        /* HaveXxxx counts how many of this kind of phrase we've seen;
         * it's a fatal error to have more than one time, zone, day,
         * or date phrase. */
-       int     HaveDate; /* year/month/day information */
-       int     HaveDay; /* Day of week */
+       int     HaveYear;
+       int     HaveMonth;
+       int     HaveDay;
+       int     HaveWeekDay; /* Day of week */
        int     HaveTime; /* Hour/minute/second */
        int     HaveZone; /* timezone and/or DST info */
        int     HaveRel; /* time offset; we can have more than one */
@@ -147,6 +149,7 @@ timephrase(struct gdstate *gds)
        if (gds->tokenp[0].token == '+'
            && gds->tokenp[1].token == tUNUMBER) {
                /* "7:14+0700" */
+               gds->HaveZone++;
                gds->DSTmode = DSToff;
                gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
                    + (gds->tokenp[1].value % 100) * MINUTE);
@@ -155,6 +158,7 @@ timephrase(struct gdstate *gds)
        if (gds->tokenp[0].token == '-'
            && gds->tokenp[1].token == tUNUMBER) {
                /* "19:14:12-0530" */
+               gds->HaveZone++;
                gds->DSTmode = DSToff;
                gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
                    + (gds->tokenp[1].value % 100) * MINUTE);
@@ -207,7 +211,9 @@ datephrase(struct gdstate *gds)
            && gds->tokenp[2].token == tUNUMBER
            && gds->tokenp[3].token == '/'
            && gds->tokenp[4].token == tUNUMBER) {
-               gds->HaveDate++;
+               gds->HaveYear++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                if (gds->tokenp[0].value >= 13) {
                        /* First number is big:  2004/01/29, 99/02/17 */
                        gds->Year = gds->tokenp[0].value;
@@ -233,7 +239,8 @@ datephrase(struct gdstate *gds)
            && gds->tokenp[1].token == '/'
            && gds->tokenp[2].token == tUNUMBER) {
                /* "1/15" */
-               gds->HaveDate++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                gds->Month = gds->tokenp[0].value;
                gds->Day = gds->tokenp[2].value;
                gds->tokenp += 3;
@@ -246,7 +253,9 @@ datephrase(struct gdstate *gds)
            && gds->tokenp[3].token == '-'
            && gds->tokenp[4].token == tUNUMBER) {
                /* ISO 8601 format.  yyyy-mm-dd.  */
-               gds->HaveDate++;
+               gds->HaveYear++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                gds->Year = gds->tokenp[0].value;
                gds->Month = gds->tokenp[2].value;
                gds->Day = gds->tokenp[4].value;
@@ -259,7 +268,9 @@ datephrase(struct gdstate *gds)
            && gds->tokenp[2].token == tMONTH
            && gds->tokenp[3].token == '-'
            && gds->tokenp[4].token == tUNUMBER) {
-               gds->HaveDate++;
+               gds->HaveYear++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                if (gds->tokenp[0].value > 31) {
                        /* e.g. 1992-Jun-17 */
                        gds->Year = gds->tokenp[0].value;
@@ -280,7 +291,9 @@ datephrase(struct gdstate *gds)
            && gds->tokenp[2].token == ','
            && gds->tokenp[3].token == tUNUMBER) {
                /* "June 17, 2001" */
-               gds->HaveDate++;
+               gds->HaveYear++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                gds->Month = gds->tokenp[0].value;
                gds->Day = gds->tokenp[1].value;
                gds->Year = gds->tokenp[3].value;
@@ -291,7 +304,8 @@ datephrase(struct gdstate *gds)
        if (gds->tokenp[0].token == tMONTH
            && gds->tokenp[1].token == tUNUMBER) {
                /* "May 3" */
-               gds->HaveDate++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                gds->Month = gds->tokenp[0].value;
                gds->Day = gds->tokenp[1].value;
                gds->tokenp += 2;
@@ -302,7 +316,9 @@ datephrase(struct gdstate *gds)
            && gds->tokenp[1].token == tMONTH
            && gds->tokenp[2].token == tUNUMBER) {
                /* "12 Sept 1997" */
-               gds->HaveDate++;
+               gds->HaveYear++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                gds->Day = gds->tokenp[0].value;
                gds->Month = gds->tokenp[1].value;
                gds->Year = gds->tokenp[2].value;
@@ -313,7 +329,8 @@ datephrase(struct gdstate *gds)
        if (gds->tokenp[0].token == tUNUMBER
            && gds->tokenp[1].token == tMONTH) {
                /* "12 Sept" */
-               gds->HaveDate++;
+               gds->HaveMonth++;
+               gds->HaveDay++;
                gds->Day = gds->tokenp[0].value;
                gds->Month = gds->tokenp[1].value;
                gds->tokenp += 2;
@@ -406,7 +423,7 @@ dayphrase(struct gdstate *gds)
 {
        if (gds->tokenp[0].token == tDAY) {
                /* "tues", "wednesday," */
-               gds->HaveDay++;
+               gds->HaveWeekDay++;
                gds->DayOrdinal = 1;
                gds->DayNumber = gds->tokenp[0].value;
                gds->tokenp += 1;
@@ -417,7 +434,7 @@ dayphrase(struct gdstate *gds)
        if (gds->tokenp[0].token == tUNUMBER
                && gds->tokenp[1].token == tDAY) {
                /* "second tues" "3 wed" */
-               gds->HaveDay++;
+               gds->HaveWeekDay++;
                gds->DayOrdinal = gds->tokenp[0].value;
                gds->DayNumber = gds->tokenp[1].value;
                gds->tokenp += 2;
@@ -452,7 +469,8 @@ phrase(struct gdstate *gds)
 
        /* Bare numbers sometimes have meaning. */
        if (gds->tokenp[0].token == tUNUMBER) {
-               if (gds->HaveTime && gds->HaveDate && !gds->HaveRel) {
+               if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
+                       gds->HaveYear++;
                        gds->Year = gds->tokenp[0].value;
                        gds->tokenp += 1;
                        return 1;
@@ -460,7 +478,9 @@ phrase(struct gdstate *gds)
 
                if(gds->tokenp[0].value > 10000) {
                        /* "20040301" */
-                       gds->HaveDate++;
+                       gds->HaveYear++;
+                       gds->HaveMonth++;
+                       gds->HaveDay++;
                        gds->Day= (gds->tokenp[0].value)%100;
                        gds->Month= (gds->tokenp[0].value/100)%100;
                        gds->Year = gds->tokenp[0].value/10000;
@@ -735,16 +755,20 @@ DSTcorrect(time_t Start, time_t Future)
 
 
 static time_t
-RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
+RelativeDate(time_t Start, time_t zone, int dstmode,
+    time_t DayOrdinal, time_t DayNumber)
 {
        struct tm       *tm;
-       time_t  now;
+       time_t  t, now;
 
+       t = Start - zone;
+       tm = gmtime(&t);
        now = Start;
-       tm = localtime(&now);
        now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
        now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
-       return DSTcorrect(Start, now);
+       if (dstmode == DSTmaybe)
+               return DSTcorrect(Start, now);
+       return now - Start;
 }
 
 
@@ -924,13 +948,6 @@ get_date(time_t now, char *p)
        if(local.tm_isdst)
                tzone += HOUR;
 
-       /* Initialize the year/month/day and timezone fields. */
-       gds->Year = local.tm_year + 1900;
-       gds->Month = local.tm_mon + 1;
-       gds->Day = local.tm_mday;
-       gds->Timezone = tzone;
-       gds->DSTmode = DSTmaybe;
-
        /* Tokenize the input string. */
        lasttoken = tokens;
        while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
@@ -946,15 +963,41 @@ get_date(time_t now, char *p)
                        return -1;
        }
 
-       /* If we saw more than one time, timezone, date, or day, then
-        * give up. */
-       if (gds->HaveTime > 1 || gds->HaveZone > 1
-           || gds->HaveDate > 1 || gds->HaveDay > 1)
+       /* Use current local timezone if none was specified. */
+       if (!gds->HaveZone) {
+               gds->Timezone = tzone;
+               gds->DSTmode = DSTmaybe;
+       }
+
+       /* If a timezone was specified, use that for generating the default
+        * time components instead of the local timezone. */
+       if (gds->HaveZone && gmt_ptr != NULL) {
+               now -= gds->Timezone;
+               gmt_ptr = gmtime (&now);
+               if (gmt_ptr != NULL)
+                       local = *gmt_ptr;
+               now += gds->Timezone;
+       }
+
+       if (!gds->HaveYear)
+               gds->Year = local.tm_year + 1900;
+       if (!gds->HaveMonth)
+               gds->Month = local.tm_mon + 1;
+       if (!gds->HaveDay)
+               gds->Day = local.tm_mday;
+       /* Note: No default for hour/min/sec; a specifier that just
+        * gives date always refers to 00:00 on that date. */
+
+       /* If we saw more than one time, timezone, weekday, year, month,
+        * or day, then give up. */
+       if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
+           || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
                return -1;
 
        /* Compute an absolute time based on whatever absolute information
         * we collected. */
-       if (gds->HaveDate || gds->HaveTime || gds->HaveDay) {
+       if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
+           || gds->HaveTime || gds->HaveWeekDay) {
                Start = Convert(gds->Month, gds->Day, gds->Year,
                    gds->Hour, gds->Minutes, gds->Seconds,
                    gds->Timezone, gds->DSTmode);
@@ -972,8 +1015,10 @@ get_date(time_t now, char *p)
        Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
 
        /* Adjust for day-of-week offsets. */
-       if (gds->HaveDay && !gds->HaveDate) {
-               tod = RelativeDate(Start, gds->DayOrdinal, gds->DayNumber);
+       if (gds->HaveWeekDay
+           && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
+               tod = RelativeDate(Start, gds->Timezone,
+                   gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
                Start += tod;
        }
 
index 7b1c260acc5844d22a942d3d2840c0dcfc3014ec..eac571049fe8877701be06ce4a6fda89fb70b1e7 100644 (file)
@@ -36,9 +36,6 @@ time_t get_date(time_t, const char *);
 DEFINE_TEST(test_getdate)
 {
        time_t now = time(NULL);
-       struct tm tm = *localtime(&now);
-       time_t dayStart = now
-           - ((tm.tm_hour * 60L + tm.tm_min) * 60L + tm.tm_sec);
 
        assertEqualInt(get_date(now, "Jan 1, 1970 UTC"), 0);
        assertEqualInt(get_date(now, "7:12:18-0530 4 May 1983"), 420900138);
@@ -49,23 +46,35 @@ DEFINE_TEST(test_getdate)
        assertEqualInt(get_date(now, "Sun Feb 22 17:38:26 PST 2009"),
            1235353106);
        /* Basic relative offsets. */
+       /* If we use the actual current time as the reference, then
+        * these tests break around DST changes, so it's actually
+        * important to use a specific reference time here. */
+       assertEqualInt(get_date(0, "tomorrow"), 24 * 60 * 60);
+       assertEqualInt(get_date(0, "yesterday"), - 24 * 60 * 60);
+       assertEqualInt(get_date(0, "now + 1 hour"), 60 * 60);
+       assertEqualInt(get_date(0, "now + 1 hour + 1 minute"), 60 * 60 + 60);
+       /* Repeat the above for a different start time. */
+       now = 1231113600; /* Jan 5, 2009 00:00 UTC */
+       assertEqualInt(get_date(0, "Jan 5, 2009 00:00 UTC"), now);
        assertEqualInt(get_date(now, "tomorrow"), now + 24 * 60 * 60);
        assertEqualInt(get_date(now, "yesterday"), now - 24 * 60 * 60);
        assertEqualInt(get_date(now, "now + 1 hour"), now + 60 * 60);
        assertEqualInt(get_date(now, "now + 1 hour + 1 minute"),
            now + 60 * 60 + 60);
-       /* "tuesday" is the start of the first tuesday today or later */
-       assertEqualInt(get_date(now, "tuesday"),
-           dayStart + ((2 - tm.tm_wday + 7) % 7) * 24 * 60 * 60);
+       assertEqualInt(get_date(now, "tomorrow 5:16am UTC"),
+           now + 24 * 60 * 60 + 5 * 60 * 60 + 16 * 60);
+       assertEqualInt(get_date(now, "UTC 5:16am tomorrow"),
+           now + 24 * 60 * 60 + 5 * 60 * 60 + 16 * 60);
+
+       /* Jan 5, 2009 was a Monday. */
+       assertEqualInt(get_date(now, "monday UTC"), now);
+       assertEqualInt(get_date(now, "sunday UTC"), now + 6 * 24 * 60 * 60);
+       assertEqualInt(get_date(now, "tuesday UTC"), now + 24 * 60 * 60);
        /* "next tuesday" is one week after "tuesday" */
-       assertEqualInt(get_date(now, "next tuesday"),
-           dayStart + (((2 - tm.tm_wday + 7) % 7) + 7) * 24 * 60 * 60);
+       assertEqualInt(get_date(now, "UTC next tuesday"),
+           now + 8 * 24 * 60 * 60);
        /* "last tuesday" is one week before "tuesday" */
-       assertEqualInt(get_date(now, "last tuesday"),
-           dayStart + (((2 - tm.tm_wday + 7) % 7) - 7) * 24 * 60 * 60);
-       assertEqualInt(get_date(now, "tomorrow 5:16am"),
-           dayStart + 24 * 60 * 60 + 5 * 60 * 60 + 16 * 60);
-       assertEqualInt(get_date(now, "5:16am tomorrow"),
-           dayStart + 24 * 60 * 60 + 5 * 60 * 60 + 16 * 60);
+       assertEqualInt(get_date(now, "last tuesday UTC"),
+           now - 6 * 24 * 60 * 60);
        /* TODO: Lots more tests here. */
 }