Browse Source

- Add support for more ISO8601 style formats.

. Timezone specifier (ex. 20040301T00:00:00+1900)
 . Week specifier "W"  (ex. 1997W021)
- Modified test case to test new features.
PEAR_1_4DEV
Moriyoshi Koizumi 23 years ago
parent
commit
df8ee549f5
  1. 189
      ext/standard/parsedate.y
  2. 24
      ext/standard/tests/time/002.phpt

189
ext/standard/parsedate.y

@ -151,17 +151,17 @@ typedef union _date_ll {
static int yylex (YYSTYPE *lvalp, void *parm); 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 %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 tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
%token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
%type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
%type <Number> tMONTH tMONTH_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 %type <Meridian> tMERIDIAN
%% %%
@ -195,71 +195,66 @@ time : tUNUMBER tMERIDIAN {
((struct date_yy *)parm)->yySeconds = 0; ((struct date_yy *)parm)->yySeconds = 0;
((struct date_yy *)parm)->yyMeridian = $2; ((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)->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) { 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 /* we have to deal with a special case for the datetime format
of XML Schema here: '2003-11-18T22:40:00Z' of XML Schema here: '2003-11-18T22:40:00Z'
the combination of a 'T' timezone specifier later followed the combination of a 'T' timezone specifier later followed
@ -271,6 +266,9 @@ pgsqlzonepart : tSNUMBER {
zone : tTZONE { zone : tTZONE {
((struct date_yy *)parm)->yyTimezone = $1; ((struct date_yy *)parm)->yyTimezone = $1;
} }
| tWZONE {
((struct date_yy *)parm)->yyTimezone = $1;
}
| tZZONE { | tZZONE {
((struct date_yy *)parm)->yyTimezone = $1; ((struct date_yy *)parm)->yyTimezone = $1;
} }
@ -280,8 +278,7 @@ zone : tTZONE {
| tDAYZONE { | tDAYZONE {
((struct date_yy *)parm)->yyTimezone = $1 - 60; ((struct date_yy *)parm)->yyTimezone = $1 - 60;
} }
|
tZONE tDST {
| tZONE tDST {
((struct date_yy *)parm)->yyTimezone = $1 - 60; ((struct date_yy *)parm)->yyTimezone = $1 - 60;
} }
; ;
@ -335,13 +332,6 @@ date : tUNUMBER '/' tUNUMBER {
} }
| iso8601date | iso8601date
| iso8601datetime { | 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++; ((struct date_yy *)parm)->yyHaveTime++;
} }
| tUNUMBER tMONTH tSNUMBER { | tUNUMBER tMONTH tSNUMBER {
@ -384,7 +374,23 @@ date : tUNUMBER '/' tUNUMBER {
; ;
iso8601datetime: iso8601date tTZONE iso8601time 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 { iso8601date: tUNUMBER tSNUMBER tSNUMBER {
/* ISO 8601 format. yyyy-mm-dd. */ /* ISO 8601 format. yyyy-mm-dd. */
@ -392,8 +398,59 @@ iso8601date: tUNUMBER tSNUMBER tSNUMBER {
((struct date_yy *)parm)->yyMonth = -$2; ((struct date_yy *)parm)->yyMonth = -$2;
((struct date_yy *)parm)->yyDay = -$3; ((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 { rel : relunit tAGO {
((struct date_yy *)parm)->yyRelSeconds = ((struct date_yy *)parm)->yyRelSeconds =
-((struct date_yy *)parm)->yyRelSeconds; -((struct date_yy *)parm)->yyRelSeconds;
@ -697,7 +754,7 @@ static TABLE const MilitaryTable[] = {
{ "t", tTZONE, HOUR ( 7) }, { "t", tTZONE, HOUR ( 7) },
{ "u", tZONE, HOUR ( 8) }, { "u", tZONE, HOUR ( 8) },
{ "v", tZONE, HOUR ( 9) }, { "v", tZONE, HOUR ( 9) },
{ "w", tZONE, HOUR ( 10) },
{ "w", tWZONE, HOUR ( 10) },
{ "x", tZONE, HOUR ( 11) }, { "x", tZONE, HOUR ( 11) },
{ "y", tZONE, HOUR ( 12) }, { "y", tZONE, HOUR ( 12) },
{ "z", tZZONE, HOUR ( 0) }, { "z", tZZONE, HOUR ( 0) },

24
ext/standard/tests/time/002.phpt

@ -25,14 +25,22 @@ if (!@putenv("TZ=EST5") || getenv("TZ") != 'EST5') {
"2001-10-22 21:19:58-02", "2001-10-22 21:19:58-02",
"2001-10-22 21:19:58-0213", "2001-10-22 21:19:58-0213",
"2001-10-22 21:19:58+02", "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"); putenv ("TZ=GMT0");
foreach ($dates as $date) { foreach ($dates as $date) {
echo date ("Y-m-d H:i:s\n", strtotime ($date)); echo date ("Y-m-d H:i:s\n", strtotime ($date));
} }
echo "*** US/Eastern\n";
putenv("TZ=US/Eastern"); putenv("TZ=US/Eastern");
if( date("T") == "GMT" ) { if( date("T") == "GMT" ) {
// POSIX style // POSIX style
@ -44,6 +52,7 @@ if (!@putenv("TZ=EST5") || getenv("TZ") != 'EST5') {
} }
?> ?>
--EXPECT-- --EXPECT--
*** GMT0
1999-10-13 00:00:00 1999-10-13 00:00:00
1999-10-13 00:00:00 1999-10-13 00:00:00
2000-01-19 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 23:32:58
2001-10-22 19:19:58 2001-10-22 19:19:58
2001-10-22 19:06: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
1999-10-13 00:00:00 1999-10-13 00:00:00
2000-01-19 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 19:32:58
2001-10-22 15:19:58 2001-10-22 15:19:58
2001-10-22 15:06: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
Loading…
Cancel
Save