]> granicus.if.org Git - php/commitdiff
- Add support for more ISO8601 style formats.
authorMoriyoshi Koizumi <moriyoshi@php.net>
Mon, 1 Mar 2004 10:42:28 +0000 (10:42 +0000)
committerMoriyoshi Koizumi <moriyoshi@php.net>
Mon, 1 Mar 2004 10:42:28 +0000 (10:42 +0000)
 . Timezone specifier (ex. 20040301T00:00:00+1900)
 . Week specifier "W"  (ex. 1997W021)
- Modified test case to test new features.

ext/standard/parsedate.y
ext/standard/tests/time/002.phpt

index b98d5c8981b3be6fc03d879d6baf0e9f5802f557..ee75d8e74e6fa8cc1746cf36facb6ca165e3ad42 100644 (file)
@@ -151,17 +151,17 @@ typedef union _date_ll {
 static int yylex (YYSTYPE *lvalp, void *parm);
 %}
 
-/* This grammar has 22 shift/reduce conflicts. */
-%expect 22
+/* This grammar has 24 shift/reduce conflicts. */
+%expect 36
 %pure_parser
 
-%token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID tTZONE tZZONE 
+%token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID tTZONE tWZONE tZZONE
 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
 
 %type  <Number>        tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
 %type  <Number>        tMONTH tMONTH_UNIT
-%type  <Number>        tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE tTZONE tZZONE 
+%type  <Number>        tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE tTZONE tWZONE tZZONE 
 %type  <Meridian>      tMERIDIAN 
 
 %%
@@ -195,71 +195,66 @@ time      : tUNUMBER tMERIDIAN {
            ((struct date_yy *)parm)->yySeconds = 0;
            ((struct date_yy *)parm)->yyMeridian = $2;
        }
-       | tUNUMBER ':' tUNUMBER {
-           ((struct date_yy *)parm)->yyHour = $1;
-           ((struct date_yy *)parm)->yyMinutes = $3;
-           ((struct date_yy *)parm)->yySeconds = 0;
+       | iso8601time_colon
+       /* | pgsqltime  ... shares a common spec with ISO8601 */
+       ;
+
+iso8601time_colon: HMStime_with_colon sec_fraction_part iso8601zonepart {
+           ((struct date_yy *)parm)->yyMeridian = MER24;
        }
-       | tUNUMBER ':' tUNUMBER tSNUMBER {
-           ((struct date_yy *)parm)->yyHour = $1;
-           ((struct date_yy *)parm)->yyMinutes = $3;
+       |  HMtime_with_colon sec_fraction_part iso8601zonepart {
+           ((struct date_yy *)parm)->yySeconds = 0;
            ((struct date_yy *)parm)->yyMeridian = MER24;
-           ((struct date_yy *)parm)->yyHaveZone++;
-           ((struct date_yy *)parm)->yyTimezone = ($4 < 0
-                         ? -$4 % 100 + (-$4 / 100) * 60
-                         : - ($4 % 100 + ($4 / 100) * 60));
        }
-       | tUNUMBER ':' tUNUMBER ':' tUNUMBER {
-           ((struct date_yy *)parm)->yyHour = $1;
-           ((struct date_yy *)parm)->yyMinutes = $3;
-           ((struct date_yy *)parm)->yySeconds = $5;
+    ;
+
+iso8601zonepart: zonepart_numeric_without_colon {
+               ((struct date_yy *)parm)->yyHaveZone++;
        }
-       | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
-           /* ISO 8601 format.  hh:mm:ss[+-][0-9]{2}([0-9]{2})?.  */
-           ((struct date_yy *)parm)->yyHour = $1;
-           ((struct date_yy *)parm)->yyMinutes = $3;
-           ((struct date_yy *)parm)->yySeconds = $5;
-           ((struct date_yy *)parm)->yyMeridian = MER24;
-           ((struct date_yy *)parm)->yyHaveZone++;
-               if ($6 <= -100 || $6 >= 100) {
-                       ((struct date_yy *)parm)->yyTimezone =  
-                               -$6 % 100 + (-$6 / 100) * 60;
-               } else {
-                       ((struct date_yy *)parm)->yyTimezone =  -$6 * 60;
-               }
-    }
-    | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER pgsqlzonepart {
-           ((struct date_yy *)parm)->yyHour = $1;
-           ((struct date_yy *)parm)->yyMinutes = $3;
-           ((struct date_yy *)parm)->yySeconds = $5;
-           ((struct date_yy *)parm)->yyMeridian = MER24;
+       | zonepart_numeric_with_colon {
+               ((struct date_yy *)parm)->yyHaveZone++;
+       }
+       | zone {
+               ((struct date_yy *)parm)->yyHaveZone++;
        }
+       | /* empty */
        ;
 
-iso8601time: tUNUMBER ':' tUNUMBER ':' tUNUMBER  {
-           ((struct date_yy *)parm)->yyHour = $1;
-           ((struct date_yy *)parm)->yyMinutes = $3;
-           ((struct date_yy *)parm)->yySeconds = $5;
-           ((struct date_yy *)parm)->yyMeridian = MER24;
-       }  
-    ;
-
+sec_fraction_part: '.' tUNUMBER {
+       }
+       | /* empty */
+       ;
 
-pgsqlzonepart : tSNUMBER {
-           ((struct date_yy *)parm)->yyHaveZone++;
+zonepart_numeric_without_colon: tSNUMBER {
+               /* format: [+-]hhmm */
                if ($1 <= -100 || $1 >= 100) {
-                       ((struct date_yy *)parm)->yyTimezone =  
-                               -$1 % 100 + (-$1 / 100) * 60;
-               } else {
-                       ((struct date_yy *)parm)->yyTimezone =  -$1 * 60;
+                       ((struct date_yy *)parm)->yyTimezone = (-$1 / 100) * 60 + (-$1 % 100);
+               } else if ($1 >= -99 || $1 <= 99) {
+                       ((struct date_yy *)parm)->yyTimezone = -$1 * 60;
                }
        }
-       | zone {
-           ((struct date_yy *)parm)->yyHaveZone++;
+       ;
+
+zonepart_numeric_with_colon: tSNUMBER ':' tUNUMBER {
+               /* format: [+-]hh:mm */
+               ((struct date_yy *)parm)->yyTimezone = -$1 * 60 + ($1 > 0 ? -$3: $3);
+       }
+       ;
+
+HMStime_with_colon: HMtime_with_colon ':' tUNUMBER {
+               /* format: hh:mm:ss */
+           ((struct date_yy *)parm)->yySeconds = $3;
+       }
+       ;
+
+HMtime_with_colon: tUNUMBER ':' tUNUMBER {
+               /* format: hh:mm */
+           ((struct date_yy *)parm)->yyHour = $1;
+           ((struct date_yy *)parm)->yyMinutes = $3;
        }
-       | /* empty */
        ;
 
+
        /* we have to deal with a special case for the datetime format 
           of XML Schema here: '2003-11-18T22:40:00Z'
           the combination of a 'T' timezone specifier later followed
@@ -271,6 +266,9 @@ pgsqlzonepart : tSNUMBER {
 zone   : tTZONE {
            ((struct date_yy *)parm)->yyTimezone = $1;
        }
+       | tWZONE {
+           ((struct date_yy *)parm)->yyTimezone = $1;
+       }
        | tZZONE {
            ((struct date_yy *)parm)->yyTimezone = $1;
        }
@@ -280,8 +278,7 @@ zone        : tTZONE {
        | tDAYZONE {
            ((struct date_yy *)parm)->yyTimezone = $1 - 60;
        }
-       |
-         tZONE tDST {
+       | tZONE tDST {
            ((struct date_yy *)parm)->yyTimezone = $1 - 60;
        }
        ;
@@ -335,13 +332,6 @@ date       : tUNUMBER '/' tUNUMBER {
        }
        | iso8601date
        | iso8601datetime {
-               ((struct date_yy *)parm)->yyTimezone = 0;
-               ((struct date_yy *)parm)->yyHaveZone++;
-                       ((struct date_yy *)parm)->yyHaveTime++;
-    }
-       | iso8601datetime tZZONE {
-               ((struct date_yy *)parm)->yyTimezone = 0;
-               ((struct date_yy *)parm)->yyHaveZone++;
                        ((struct date_yy *)parm)->yyHaveTime++;
     }
        | tUNUMBER tMONTH tSNUMBER {
@@ -384,7 +374,23 @@ date       : tUNUMBER '/' tUNUMBER {
        ;
 
 iso8601datetime: iso8601date tTZONE iso8601time
-       ;
+       | tUNUMBER tTZONE iso8601time {
+               int i = $1;
+
+               if (i >= 10000) {
+                       /* format: yyyymmdd */
+                       ((struct date_yy *)parm)->yyYear = i / 10000;
+                       i %= 10000;
+                       ((struct date_yy *)parm)->yyMonth = i / 100;
+                       i %= 100;
+                       ((struct date_yy *)parm)->yyDay = i;
+               } else if (i >= 1000 && i <= 9999) {
+                       /* format: yyyy */
+                       ((struct date_yy *)parm)->yyYear = i;
+                       ((struct date_yy *)parm)->yyDay= 1;
+                       ((struct date_yy *)parm)->yyMonth = 1;
+               }
+       }
 
 iso8601date: tUNUMBER tSNUMBER tSNUMBER {
            /* ISO 8601 format.  yyyy-mm-dd.  */
@@ -392,8 +398,59 @@ iso8601date: tUNUMBER tSNUMBER tSNUMBER {
            ((struct date_yy *)parm)->yyMonth = -$2;
            ((struct date_yy *)parm)->yyDay = -$3;
        }
+       | tUNUMBER tSNUMBER {
+               /* ISO 8601 format   yyyy-mm */
+           ((struct date_yy *)parm)->yyYear = $1;
+           ((struct date_yy *)parm)->yyMonth = -$2;
+           ((struct date_yy *)parm)->yyDay = 1;
+       }
+       | tUNUMBER iso8601weekspec {
+               const int om = (1 + 9) % 12; /* offset month */
+               const int oy = $1 - 1; /* offset year */
+
+               ((struct date_yy *)parm)->yyYear = $1;
+               ((struct date_yy *)parm)->yyMonth = 1;
+               /* Zeller's formula */
+               ((struct date_yy *)parm)->yyDay -= ((13 * om + 12) / 5 +
+                                       oy + oy / 4 + oy / 400 - oy / 100) % 7 - 1;
+       }
     ;
 
+iso8601weekspec: tWZONE tUNUMBER {
+               ((struct date_yy *)parm)->yyDay = ($2 / 10) * 7 + ($2 % 10) - 8;
+       }
+       | tWZONE tUNUMBER tSNUMBER {
+               ((struct date_yy *)parm)->yyDay = $2 * 7 - $3 - 8;
+       }
+       ;
+
+iso8601time:
+       iso8601time_colon
+       | tUNUMBER sec_fraction_part iso8601zonepart {
+               int i = $1;
+
+               if (i <= -100000 || i >= 100000) {
+                       ((struct date_yy *)parm)->yyHour = i / 10000;
+                       i %= 10000;
+                       ((struct date_yy *)parm)->yyMinutes = i / 100;
+                       i %= 100;
+               ((struct date_yy *)parm)->yySeconds = i;
+               } else if (i <= -1000 || i >= 1000) {
+                       ((struct date_yy *)parm)->yyHour = i / 100;
+                       i %= 100;
+                       ((struct date_yy *)parm)->yyMinutes = i;
+               ((struct date_yy *)parm)->yySeconds = 0;
+               } else if (i >= -99 || i <= 99) {
+                       ((struct date_yy *)parm)->yyHour = $1;
+                       ((struct date_yy *)parm)->yyMinutes = 0;
+               ((struct date_yy *)parm)->yySeconds = 0;
+               } else {
+                       ((struct date_yy *)parm)->yyHaveTime = 0;
+               }
+           ((struct date_yy *)parm)->yyMeridian = MER24;
+       }
+       ;
+
 rel    : relunit tAGO {
            ((struct date_yy *)parm)->yyRelSeconds =
                        -((struct date_yy *)parm)->yyRelSeconds;
@@ -697,7 +754,7 @@ static TABLE const MilitaryTable[] = {
     { "t",     tTZONE, HOUR (  7) },
     { "u",     tZONE,  HOUR (  8) },
     { "v",     tZONE,  HOUR (  9) },
-    { "w",     tZONE,  HOUR ( 10) },
+    { "w",     tWZONE, HOUR ( 10) },
     { "x",     tZONE,  HOUR ( 11) },
     { "y",     tZONE,  HOUR ( 12) },
     { "z",     tZZONE, HOUR (  0) },
index ed1b0f137002bc944952891ec37577c93101f297..78cfad9fbc7af494bb0f1705b254e36627a88796 100644 (file)
@@ -25,14 +25,22 @@ if (!@putenv("TZ=EST5") || getenv("TZ") != 'EST5') {
            "2001-10-22 21:19:58-02",
            "2001-10-22 21:19:58-0213",
            "2001-10-22 21:19:58+02",
-       "2001-10-22 21:19:58+0213"
+       "2001-10-22 21:19:58+0213",
+               "2001-10-22T21:20:58-03:40",
+               "2001-10-22T211958-2",
+               "20011022T211958+0213",
+               "20011022T21:20+0215",
+               "1997W011",
+               "2004W101T05:00+0",
        );
 
+       echo "*** GMT0\n";
        putenv ("TZ=GMT0");
        foreach ($dates as $date) {
            echo date ("Y-m-d H:i:s\n", strtotime ($date));
        }
 
+       echo "*** US/Eastern\n";
        putenv("TZ=US/Eastern");
        if( date("T") == "GMT" ) {
                // POSIX style
@@ -44,6 +52,7 @@ if (!@putenv("TZ=EST5") || getenv("TZ") != 'EST5') {
        }
 ?>
 --EXPECT--
+*** GMT0
 1999-10-13 00:00:00
 1999-10-13 00:00:00
 2000-01-19 00:00:00
@@ -58,6 +67,13 @@ if (!@putenv("TZ=EST5") || getenv("TZ") != 'EST5') {
 2001-10-22 23:32:58
 2001-10-22 19:19:58
 2001-10-22 19:06:58
+2001-10-23 01:00:58
+2001-10-22 23:19:58
+2001-10-22 19:06:58
+2001-10-22 19:05:00
+1996-12-30 00:00:00
+2004-03-01 05:00:00
+*** US/Eastern
 1999-10-13 00:00:00
 1999-10-13 00:00:00
 2000-01-19 00:00:00
@@ -72,3 +88,9 @@ if (!@putenv("TZ=EST5") || getenv("TZ") != 'EST5') {
 2001-10-22 19:32:58
 2001-10-22 15:19:58
 2001-10-22 15:06:58
+2001-10-22 21:00:58
+2001-10-22 19:19:58
+2001-10-22 15:06:58
+2001-10-22 15:05:00
+1996-12-30 00:00:00
+2004-03-01 00:00:00