diff options
| -rw-r--r-- | generic/tclClockFmt.c | 60 | ||||
| -rw-r--r-- | tests/clock.test | 14 |
2 files changed, 54 insertions, 20 deletions
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index 40c6c92..4ab2423 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -28,6 +28,10 @@ TCL_DECLARE_MUTEX(ClockFmtMutex); /* Serializes access to common format list. */ static void ClockFmtScnStorageDelete(ClockFmtScnStorage *fss); static void ClockFrmScnFinalize(void *); +#ifndef TCL_CLOCK_FULL_COMPAT +#define TCL_CLOCK_FULL_COMPAT 1 +#endif + /* * Derivation of tclStringHashKeyType with another allocEntryProc */ @@ -1019,7 +1023,8 @@ static const char * FindTokenBegin( const char *p, const char *end, - ClockScanToken *tok) + ClockScanToken *tok, + int flags) { if (p < end) { char c; @@ -1028,15 +1033,19 @@ FindTokenBegin( switch (tok->map->type) { case CTOKT_INT: case CTOKT_WIDE: - /* should match at least one digit */ - while (!isdigit(UCHAR(*p)) && (p = Tcl_UtfNext(p)) < end) {} + if (!(flags & CLF_STRICT)) { + /* should match at least one digit or space */ + while (!isdigit(UCHAR(*p)) && !isspace(UCHAR(*p)) && + (p = Tcl_UtfNext(p)) < end) {} + } else { + /* should match at least one digit */ + while (!isdigit(UCHAR(*p)) && (p = Tcl_UtfNext(p)) < end) {} + } return p; case CTOKT_WORD: c = *(tok->tokWord.start); - /* should match at least to the first char of this word */ - while (*p != c && (p = Tcl_UtfNext(p)) < end) {} - return p; + goto findChar; case CTOKT_SPACE: while (!isspace(UCHAR(*p)) && (p = Tcl_UtfNext(p)) < end) {} @@ -1044,7 +1053,15 @@ FindTokenBegin( case CTOKT_CHAR: c = *((char *)tok->map->data); - while (*p != c && (p = Tcl_UtfNext(p)) < end) {} +findChar: + if (!(flags & CLF_STRICT)) { + /* should match the char or space */ + while (*p != c && !isspace(UCHAR(*p)) && + (p = Tcl_UtfNext(p)) < end) {} + } else { + /* should match the char */ + while (*p != c && (p = Tcl_UtfNext(p)) < end) {} + } return p; } } @@ -1069,6 +1086,7 @@ FindTokenBegin( static void DetermineGreedySearchLen( + ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok, int *minLenPtr, @@ -1083,7 +1101,8 @@ DetermineGreedySearchLen( if ((tok + 1)->map) { end -= tok->endDistance + yySpaceCount; /* find position of next known token */ - p = FindTokenBegin(p, end, tok + 1); + p = FindTokenBegin(p, end, tok + 1, + TCL_CLOCK_FULL_COMPAT ? opts->flags : CLF_STRICT); if (p < end) { minLen = p - yyInput; } @@ -1134,7 +1153,8 @@ DetermineGreedySearchLen( /* try to find laTok between [lookAhMin, lookAhMax] */ while (minLen < maxLen) { - const char *f = FindTokenBegin(p, end, laTok); + const char *f = FindTokenBegin(p, end, laTok, + TCL_CLOCK_FULL_COMPAT ? opts->flags : CLF_STRICT); /* if found (not below lookAhMax) */ if (f < end) { break; @@ -1518,7 +1538,7 @@ ClockScnToken_Month_Proc( int minLen, maxLen; TclStrIdxTree *idxTree; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); /* get or create tree in msgcat dict */ @@ -1550,7 +1570,7 @@ ClockScnToken_DayOfWeek_Proc( char curTok = *tok->tokWord.start; TclStrIdxTree *idxTree; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); /* %u %w %Ou %Ow */ if (curTok != 'a' && curTok != 'A' @@ -1621,7 +1641,7 @@ ClockScnToken_amPmInd_Proc( int minLen, maxLen; Tcl_Obj *amPmObj[2]; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); amPmObj[0] = ClockMCGet(opts, MCLIT_AM); amPmObj[1] = ClockMCGet(opts, MCLIT_PM); @@ -1656,7 +1676,7 @@ ClockScnToken_LocaleERA_Proc( int minLen, maxLen; Tcl_Obj *eraObj[6]; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); eraObj[0] = ClockMCGet(opts, MCLIT_BCE); eraObj[1] = ClockMCGet(opts, MCLIT_CE); @@ -1693,7 +1713,7 @@ ClockScnToken_LocaleListMatcher_Proc( int minLen, maxLen; TclStrIdxTree *idxTree; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); /* get or create tree in msgcat dict */ @@ -1716,7 +1736,7 @@ ClockScnToken_LocaleListMatcher_Proc( static int ClockScnToken_JDN_Proc( - TCL_UNUSED(ClockFmtScnCmdArgs *), + ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { @@ -1725,7 +1745,7 @@ ClockScnToken_JDN_Proc( Tcl_WideInt intJD; int fractJD = 0, fractJDDiv = 1; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); end = yyInput + maxLen; @@ -1796,7 +1816,7 @@ ClockScnToken_TimeZone_Proc( const char *p = yyInput; Tcl_Obj *tzObjStor = NULL; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); /* numeric timezone */ if (*p == '+' || *p == '-') { @@ -1879,7 +1899,7 @@ ClockScnToken_TimeZone_Proc( static int ClockScnToken_StarDate_Proc( - TCL_UNUSED(ClockFmtScnCmdArgs *), + ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { @@ -1888,7 +1908,7 @@ ClockScnToken_StarDate_Proc( int year, fractYear, fractDayDiv, fractDay; static const char *stardatePref = "stardate "; - DetermineGreedySearchLen(info, tok, &minLen, &maxLen); + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); end = yyInput + maxLen; @@ -2436,7 +2456,7 @@ ClockScan( } } - DetermineGreedySearchLen(info, tok, &minLen, &size); + DetermineGreedySearchLen(opts, info, tok, &minLen, &size); if (size < map->minSize) { /* missing input -> error */ diff --git a/tests/clock.test b/tests/clock.test index a0b2a6a..8072a68 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -37032,12 +37032,26 @@ test clock-44.1 {regression test - time zone name containing hyphen } \ } } \ -result {12:34:56-0500} + test clock-44.2 {regression test - time zone containing only two digits} \ -body { clock scan 1985-04-12T10:15:30+04 -format %Y-%m-%dT%H:%M:%S%Z } \ -result 482134530 +test clock-44.3 {regression test - spaces between some scan tokens are optional (TCL_CLOCK_FULL_COMPAT, no-strict only)} \ + -body { + list [clock scan {9 Apr 2024} -format {%d %b%Y} -gmt 1] \ + [clock scan {Tue, 9 Apr 2024 00:00:00 +0000} -format {%a, %d %b%Y %H:%M:%S %Z} -gmt 1] + } \ + -result {1712620800 1712620800} +test clock-44.4 {regression test - spaces between all scan tokens are optional (TCL_CLOCK_FULL_COMPAT, no-strict only)} \ + -body { + list [clock scan {9 Apr 2024} -format {%d%b%Y} -gmt 1] \ + [clock scan {Tue, 9 Apr 2024 00:00:00 +0000} -format {%a,%d%b%Y%H:%M:%S%Z} -gmt 1] + } \ + -result {1712620800 1712620800} + test clock-45.1 {compat: scan regression on spaces (multiple spaces in format)} \ -body { list \ |
