summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclClock.c265
-rw-r--r--generic/tclClockFmt.c287
-rw-r--r--generic/tclDate.h28
-rwxr-xr-xlibrary/clock.tcl14
4 files changed, 502 insertions, 92 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index a84300a..0c08391 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -72,19 +72,21 @@ TCL_DECLARE_MUTEX(clockMutex)
static int ConvertUTCToLocal(ClientData clientData, Tcl_Interp *,
TclDateFields *, Tcl_Obj *timezoneObj, int);
static int ConvertUTCToLocalUsingTable(Tcl_Interp *,
- TclDateFields *, int, Tcl_Obj *const[]);
+ TclDateFields *, int, Tcl_Obj *const[],
+ Tcl_WideInt rangesVal[2]);
static int ConvertUTCToLocalUsingC(Tcl_Interp *,
TclDateFields *, int);
static int ConvertLocalToUTC(ClientData clientData, Tcl_Interp *,
TclDateFields *, Tcl_Obj *timezoneObj, int);
static int ConvertLocalToUTCUsingTable(Tcl_Interp *,
- TclDateFields *, int, Tcl_Obj *const[]);
+ TclDateFields *, int, Tcl_Obj *const[],
+ Tcl_WideInt rangesVal[2]);
static int ConvertLocalToUTCUsingC(Tcl_Interp *,
TclDateFields *, int);
static int ClockConfigureObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static Tcl_Obj * LookupLastTransition(Tcl_Interp *, Tcl_WideInt,
- int, Tcl_Obj *const *);
+ int, Tcl_Obj *const *, Tcl_WideInt rangesVal[2]);
static void GetYearWeekDay(TclDateFields *, int);
static void GetGregorianEraYearDay(TclDateFields *, int);
static void GetMonthDay(TclDateFields *);
@@ -1432,6 +1434,7 @@ ConvertLocalToUTC(
Tcl_Obj *tzdata; /* Time zone data */
int rowc; /* Number of rows in tzdata */
Tcl_Obj **rowv; /* Pointers to the rows */
+ Tcl_WideInt seconds;
/* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */
if (timezoneObj == dataPtr->GMTSetupTimeZone && dataPtr->GMTSetupTimeZone != NULL) {
@@ -1442,17 +1445,37 @@ ConvertLocalToUTC(
/*
* Check cacheable conversion could be used
- * (last-minute Local2UTC cache with the same TZ)
+ * (last-period Local2UTC cache within the same TZ)
*/
+ seconds = fields->localSeconds - dataPtr->Local2UTC.tzOffset;
if ( timezoneObj == dataPtr->Local2UTC.timezoneObj
&& ( fields->localSeconds == dataPtr->Local2UTC.localSeconds
- || fields->localSeconds / 60 == dataPtr->Local2UTC.localSeconds / 60
+ || ( seconds >= dataPtr->Local2UTC.rangesVal[0]
+ && seconds < dataPtr->Local2UTC.rangesVal[1])
)
&& changeover == dataPtr->Local2UTC.changeover
) {
/* the same time zone and offset (UTC time inside the last minute) */
fields->tzOffset = dataPtr->Local2UTC.tzOffset;
- fields->seconds = fields->localSeconds - fields->tzOffset;
+ fields->seconds = seconds;
+ return TCL_OK;
+ }
+
+ /*
+ * Check cacheable back-conversion could be used
+ * (last-period UTC2Local cache within the same TZ)
+ */
+ seconds = fields->localSeconds - dataPtr->UTC2Local.tzOffset;
+ if ( timezoneObj == dataPtr->UTC2Local.timezoneObj
+ && ( seconds == dataPtr->UTC2Local.seconds
+ || ( seconds >= dataPtr->UTC2Local.rangesVal[0]
+ && seconds < dataPtr->UTC2Local.rangesVal[1])
+ )
+ && changeover == dataPtr->UTC2Local.changeover
+ ) {
+ /* the same time zone and offset (UTC time inside the last minute) */
+ fields->tzOffset = dataPtr->UTC2Local.tzOffset;
+ fields->seconds = seconds;
return TCL_OK;
}
@@ -1475,11 +1498,15 @@ ConvertLocalToUTC(
*/
if (rowc == 0) {
+ dataPtr->Local2UTC.rangesVal[0] = 0;
+ dataPtr->Local2UTC.rangesVal[1] = 0;
+
if (ConvertLocalToUTCUsingC(interp, fields, changeover) != TCL_OK) {
return TCL_ERROR;
};
} else {
- if (ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv) != TCL_OK) {
+ if (ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv,
+ dataPtr->Local2UTC.rangesVal) != TCL_OK) {
return TCL_ERROR;
};
}
@@ -1516,7 +1543,8 @@ ConvertLocalToUTCUsingTable(
Tcl_Interp *interp, /* Tcl interpreter */
TclDateFields *fields, /* Time to convert, with 'seconds' filled in */
int rowc, /* Number of points at which time changes */
- Tcl_Obj *const rowv[]) /* Points at which time changes */
+ Tcl_Obj *const rowv[], /* Points at which time changes */
+ Tcl_WideInt rangesVal[2]) /* Return bounds for time period */
{
Tcl_Obj *row;
int cellc;
@@ -1540,7 +1568,8 @@ ConvertLocalToUTCUsingTable(
fields->tzOffset = 0;
fields->seconds = fields->localSeconds;
while (!found) {
- row = LookupLastTransition(interp, fields->seconds, rowc, rowv);
+ row = LookupLastTransition(interp, fields->seconds, rowc, rowv,
+ rangesVal);
if ((row == NULL)
|| TclListObjGetElements(interp, row, &cellc,
&cellv) != TCL_OK
@@ -1730,11 +1759,12 @@ ConvertUTCToLocal(
/*
* Check cacheable conversion could be used
- * (last-minute UTC2Local cache with the same TZ)
+ * (last-period UTC2Local cache within the same TZ)
*/
if ( timezoneObj == dataPtr->UTC2Local.timezoneObj
&& ( fields->seconds == dataPtr->UTC2Local.seconds
- || fields->seconds / 60 == dataPtr->UTC2Local.seconds / 60
+ || ( fields->seconds >= dataPtr->UTC2Local.rangesVal[0]
+ && fields->seconds < dataPtr->UTC2Local.rangesVal[1])
)
&& changeover == dataPtr->UTC2Local.changeover
) {
@@ -1764,11 +1794,15 @@ ConvertUTCToLocal(
*/
if (rowc == 0) {
+ dataPtr->UTC2Local.rangesVal[0] = 0;
+ dataPtr->UTC2Local.rangesVal[1] = 0;
+
if (ConvertUTCToLocalUsingC(interp, fields, changeover) != TCL_OK) {
return TCL_ERROR;
}
} else {
- if (ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv) != TCL_OK) {
+ if (ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv,
+ dataPtr->UTC2Local.rangesVal) != TCL_OK) {
return TCL_ERROR;
}
}
@@ -1806,7 +1840,8 @@ ConvertUTCToLocalUsingTable(
TclDateFields *fields, /* Fields of the date */
int rowc, /* Number of rows in the conversion table
* (>= 1) */
- Tcl_Obj *const rowv[]) /* Rows of the conversion table */
+ Tcl_Obj *const rowv[], /* Rows of the conversion table */
+ Tcl_WideInt rangesVal[2]) /* Return bounds for time period */
{
Tcl_Obj *row; /* Row containing the current information */
int cellc; /* Count of cells in the row (must be 4) */
@@ -1816,7 +1851,7 @@ ConvertUTCToLocalUsingTable(
* Look up the nearest transition time.
*/
- row = LookupLastTransition(interp, fields->seconds, rowc, rowv);
+ row = LookupLastTransition(interp, fields->seconds, rowc, rowv, rangesVal);
if (row == NULL ||
TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK ||
TclGetIntFromObj(interp, cellv[1], &fields->tzOffset) != TCL_OK) {
@@ -1943,12 +1978,13 @@ LookupLastTransition(
Tcl_Interp *interp, /* Interpreter for error messages */
Tcl_WideInt tick, /* Time from the epoch */
int rowc, /* Number of rows of tzdata */
- Tcl_Obj *const *rowv) /* Rows in tzdata */
+ Tcl_Obj *const *rowv, /* Rows in tzdata */
+ Tcl_WideInt rangesVal[2]) /* Return bounds for time period */
{
- int l;
+ int l = 0;
int u;
Tcl_Obj *compObj;
- Tcl_WideInt compVal;
+ Tcl_WideInt compVal, fromVal = tick, toVal = tick;
/*
* Examine the first row to make sure we're in bounds.
@@ -1965,14 +2001,13 @@ LookupLastTransition(
*/
if (tick < compVal) {
- return rowv[0];
+ goto done;
}
/*
* Binary-search to find the transition.
*/
- l = 0;
u = rowc-1;
while (l < u) {
int m = (l + u + 1) / 2;
@@ -1983,10 +2018,19 @@ LookupLastTransition(
}
if (tick >= compVal) {
l = m;
+ fromVal = compVal;
} else {
u = m-1;
+ toVal = compVal;
}
}
+
+done:
+
+ if (rangesVal) {
+ rangesVal[0] = fromVal;
+ rangesVal[1] = toVal;
+ }
return rowv[l];
}
@@ -2713,7 +2757,7 @@ _ClockParseFmtScnArgs(
Tcl_Interp *interp, /* Tcl interpreter */
int objc, /* Parameter count */
Tcl_Obj *const objv[], /* Parameter vector */
- ClockFmtScnCmdArgs *resOpts, /* Result vector: format, locale, timezone... */
+ ClockFmtScnCmdArgs *opts, /* Result vector: format, locale, timezone... */
int forScan /* Flag to differentiate between format and scan */
) {
ClockClientData *dataPtr = clientData;
@@ -2739,7 +2783,7 @@ _ClockParseFmtScnArgs(
* Extract values for the keywords.
*/
- memset(resOpts, 0, sizeof(*resOpts));
+ memset(opts, 0, sizeof(*opts));
for (i = 2; i < objc; i+=2) {
if (Tcl_GetIndexFromObj(interp, objv[i], options[forScan],
"option", 0, &optionIndex) != TCL_OK) {
@@ -2749,7 +2793,7 @@ _ClockParseFmtScnArgs(
}
switch (optionIndex) {
case CLOCK_FORMAT_FORMAT:
- resOpts->formatObj = objv[i+1];
+ opts->formatObj = objv[i+1];
break;
case CLOCK_FORMAT_GMT:
if (Tcl_GetBooleanFromObj(interp, objv[i+1], &gmtFlag) != TCL_OK){
@@ -2757,13 +2801,13 @@ _ClockParseFmtScnArgs(
}
break;
case CLOCK_FORMAT_LOCALE:
- resOpts->localeObj = objv[i+1];
+ opts->localeObj = objv[i+1];
break;
case CLOCK_FORMAT_TIMEZONE:
- resOpts->timezoneObj = objv[i+1];
+ opts->timezoneObj = objv[i+1];
break;
case CLOCK_FORMAT_BASE:
- resOpts->baseObj = objv[i+1];
+ opts->baseObj = objv[i+1];
break;
}
saw |= 1 << optionIndex;
@@ -2780,11 +2824,30 @@ _ClockParseFmtScnArgs(
return TCL_ERROR;
}
if (gmtFlag) {
- resOpts->timezoneObj = litPtr[LIT_GMT];
+ opts->timezoneObj = litPtr[LIT_GMT];
+ }
+
+ opts->clientData = clientData;
+ opts->interp = interp;
+
+ /* If time zone not specified use system time zone */
+
+ if ( opts->timezoneObj == NULL
+ || TclGetString(opts->timezoneObj) == NULL
+ || opts->timezoneObj->length == 0
+ ) {
+ opts->timezoneObj = ClockGetSystemTimeZone(clientData, interp);
+ if (opts->timezoneObj == NULL) {
+ return TCL_ERROR;
+ }
}
- resOpts->clientData = clientData;
- resOpts->interp = interp;
+ /* Setup timezone (normalize object if needed and load TZ on demand) */
+
+ opts->timezoneObj = ClockSetupTimeZone(clientData, interp, opts->timezoneObj);
+ if (opts->timezoneObj == NULL) {
+ return TCL_ERROR;
+ }
return TCL_OK;
}
@@ -2816,7 +2879,7 @@ ClockParseformatargsObjCmd(
{
ClockClientData *dataPtr = clientData;
Tcl_Obj **literals = dataPtr->literals;
- ClockFmtScnCmdArgs resOpts; /* Format, locale and timezone */
+ ClockFmtScnCmdArgs opts; /* Format, locale and timezone */
Tcl_WideInt clockVal; /* Clock value - just used to parse. */
int ret;
@@ -2837,7 +2900,7 @@ ClockParseformatargsObjCmd(
*/
ret = _ClockParseFmtScnArgs(clientData, interp, objc, objv,
- &resOpts, 0);
+ &opts, 0);
if (ret != TCL_OK) {
return ret;
}
@@ -2849,18 +2912,110 @@ ClockParseformatargsObjCmd(
if (Tcl_GetWideIntFromObj(interp, objv[1], &clockVal) != TCL_OK) {
return TCL_ERROR;
}
- if (resOpts.formatObj == NULL)
- resOpts.formatObj = literals[LIT__DEFAULT_FORMAT];
- if (resOpts.localeObj == NULL)
- resOpts.localeObj = literals[LIT_C];
- if (resOpts.timezoneObj == NULL)
- resOpts.timezoneObj = literals[LIT__NIL];
+ if (opts.formatObj == NULL)
+ opts.formatObj = literals[LIT__DEFAULT_FORMAT];
+ if (opts.localeObj == NULL)
+ opts.localeObj = literals[LIT_C];
+ if (opts.timezoneObj == NULL)
+ opts.timezoneObj = literals[LIT__NIL];
/*
* Return options as a list.
*/
- Tcl_SetObjResult(interp, Tcl_NewListObj(3, (Tcl_Obj**)&resOpts.formatObj));
+ Tcl_SetObjResult(interp, Tcl_NewListObj(3, (Tcl_Obj**)&opts.formatObj));
+ return TCL_OK;
+}
+
+/*----------------------------------------------------------------------
+ *
+ * ClockFormatObjCmd -
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+ClockFormatObjCmd(
+ ClientData clientData, /* Client data containing literal pool */
+ Tcl_Interp *interp, /* Tcl interpreter */
+ int objc, /* Parameter count */
+ Tcl_Obj *const objv[]) /* Parameter values */
+{
+ ClockClientData *dataPtr = clientData;
+
+ int ret;
+ ClockFmtScnCmdArgs opts; /* Format, locale, timezone and base */
+ Tcl_WideInt clockVal; /* Time, expressed in seconds from the Epoch */
+ DateInfo yy; /* Common structure used for parsing */
+ DateInfo *info = &yy;
+
+ if ((objc & 1) == 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, "string "
+ "?-format string? "
+ "?-gmt boolean? "
+ "?-locale LOCALE? ?-timezone ZONE?");
+ Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Extract values for the keywords.
+ */
+
+ ret = _ClockParseFmtScnArgs(clientData, interp, objc, objv,
+ &opts, 0);
+ if (ret != TCL_OK) {
+ return ret;
+ }
+
+ ret = TCL_ERROR;
+
+ if (Tcl_GetWideIntFromObj(interp, objv[1], &clockVal) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+
+ ClockInitDateInfo(info);
+ yydate.tzName = NULL;
+
+ /*
+ * Extract year, month and day from the base time for the parser to use as
+ * defaults
+ */
+
+ /* check base fields already cached (by TZ, last-second cache) */
+ if ( dataPtr->lastBase.timezoneObj == opts.timezoneObj
+ && dataPtr->lastBase.Date.seconds == clockVal) {
+ memcpy(&yydate, &dataPtr->lastBase.Date, ClockCacheableDateFieldsSize);
+ } else {
+ /* extact fields from base */
+ yydate.seconds = clockVal;
+ if (ClockGetDateFields(clientData, interp, &yydate, opts.timezoneObj,
+ GREGORIAN_CHANGE_DATE) != TCL_OK) {
+ goto done;
+ }
+ /* cache last base */
+ memcpy(&dataPtr->lastBase.Date, &yydate, ClockCacheableDateFieldsSize);
+ Tcl_SetObjRef(dataPtr->lastBase.timezoneObj, opts.timezoneObj);
+ }
+
+ /* Default format */
+ if (opts.formatObj == NULL) {
+ opts.formatObj = dataPtr->literals[LIT__DEFAULT_FORMAT];
+ }
+
+ /* Use compiled version of Format - */
+
+ ret = ClockFormat(clientData, interp, info, &opts);
+
+done:
+
+ Tcl_UnsetObjRef(yydate.tzName);
+
+ if (ret != TCL_OK) {
+ return ret;
+ }
+
return TCL_OK;
}
@@ -2879,7 +3034,6 @@ ClockScanObjCmd(
Tcl_Obj *const objv[]) /* Parameter values */
{
ClockClientData *dataPtr = clientData;
- Tcl_Obj **literals = dataPtr->literals;
int ret;
ClockFmtScnCmdArgs opts; /* Format, locale, timezone and base */
@@ -2919,29 +3073,9 @@ ClockScanObjCmd(
baseVal = (Tcl_WideInt) now.sec;
}
- /* If time zone not specified use system time zone */
- if ( opts.timezoneObj == NULL
- || TclGetString(opts.timezoneObj) == NULL
- || opts.timezoneObj->length == 0
- ) {
- opts.timezoneObj = ClockGetSystemTimeZone(clientData, interp);
- if (opts.timezoneObj == NULL) {
- return TCL_ERROR;
- }
- }
-
- /* Setup timezone (normalize object id needed and load TZ on demand) */
-
- opts.timezoneObj = ClockSetupTimeZone(clientData, interp, opts.timezoneObj);
- if (opts.timezoneObj == NULL) {
- return TCL_ERROR;
- }
-
ClockInitDateInfo(info);
yydate.tzName = NULL;
- // Tcl_SetObjRef(yydate.tzName, opts.timezoneObj);
-
/*
* Extract year, month and day from the base time for the parser to use as
* defaults
@@ -2979,20 +3113,6 @@ ClockScanObjCmd(
}
ret = ClockFreeScan(clientData, interp, info, objv[1], &opts);
}
-#if 0
- else
- if (1) {
- /* TODO: Tcled Scan proc - */
- int ret;
- Tcl_Obj *callargs[10];
- memcpy(callargs, objv, objc * sizeof(*objv));
- callargs[0] = Tcl_NewStringObj("::tcl::clock::__org_scan", -1);
- Tcl_IncrRefCount(callargs[0]);
- ret = Tcl_EvalObjv(interp, objc, callargs, 0);
- Tcl_DecrRefCount(callargs[0]);
- return ret;
- }
-#endif
else {
/* Use compiled version of Scan - */
@@ -3073,7 +3193,6 @@ ClockFreeScan(
ClockFmtScnCmdArgs *opts) /* Command options */
{
ClockClientData *dataPtr = clientData;
- // Tcl_Obj **literals = dataPtr->literals;
int ret = TCL_ERROR;
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c
index ac34e68..5469ee1 100644
--- a/generic/tclClockFmt.c
+++ b/generic/tclClockFmt.c
@@ -1091,7 +1091,7 @@ ClockGetOrParseScanFormat(
}
p = strFmt + fss->scnTokC * 2;
if (p < e) {
- if ((e - p) < fss->scnTokC) {
+ if ((unsigned int)(e - p) < fss->scnTokC) {
fss->scnTokC += (e - p);
} else {
fss->scnTokC += fss->scnTokC;
@@ -1581,6 +1581,291 @@ done:
return ret;
}
+/*
+ *----------------------------------------------------------------------
+ */
+int
+ClockFormat(
+ ClientData clientData, /* Client data containing literal pool */
+ Tcl_Interp *interp, /* Tcl interpreter */
+ register DateInfo *info, /* Date fields used for parsing & converting */
+ ClockFmtScnCmdArgs *opts) /* Command options */
+{
+ ClockClientData *dataPtr = clientData;
+ ClockFormatToken *tok;
+ ClockFormatTokenMap *map;
+
+ /* get localized format */
+ if (ClockLocalizeFormat(opts) == NULL) {
+ return TCL_ERROR;
+ }
+
+/* if ((tok = ClockGetOrParseFmtFormat(interp, opts->formatObj)) == NULL) {
+ return TCL_ERROR;
+ }
+*/
+#if 0
+ /* prepare parsing */
+
+ yyMeridian = MER24;
+
+ p = TclGetString(strObj);
+ end = p + strObj->length;
+ /* in strict mode - bypass spaces at begin / end only (not between tokens) */
+ if (opts->flags & CLF_STRICT) {
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ }
+ info->dateStart = yyInput = p;
+ info->dateEnd = end;
+
+ /* parse string */
+ for (; tok->map != NULL; tok++) {
+ map = tok->map;
+ /* bypass spaces at begin of input before parsing each token */
+ if ( !(opts->flags & CLF_STRICT)
+ && (map->type != CTOKT_SPACE && map->type != CTOKT_WORD)
+ ) {
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ }
+ yyInput = p;
+ switch (map->type)
+ {
+ case CTOKT_DIGIT:
+ if (1) {
+ int size = map->maxSize;
+ int sign = 1;
+ if (map->flags & CLF_SIGNED) {
+ if (*p == '+') { yyInput = ++p; }
+ else
+ if (*p == '-') { yyInput = ++p; sign = -1; };
+ }
+ /* greedy find digits (look for forward digits consider spaces),
+ * corresponding pre-calculated lookAhead */
+ if (size != map->minSize && tok->lookAhead) {
+ int spcnt = 0;
+ const char *pe;
+ size += tok->lookAhead;
+ x = p + size; if (x > end) { x = end; };
+ pe = x;
+ while (p < x) {
+ if (isspace(UCHAR(*p))) {
+ if (pe > p) { pe = p; };
+ if (x < end) x++;
+ p++;
+ spcnt++;
+ continue;
+ }
+ if (isdigit(UCHAR(*p))) {
+ p++;
+ continue;
+ }
+ break;
+ }
+ /* consider reserved (lookAhead) for next tokens */
+ p -= tok->lookAhead + spcnt;
+ if (p > pe) {
+ p = pe;
+ }
+ } else {
+ x = p + size; if (x > end) { x = end; };
+ while (isdigit(UCHAR(*p)) && p < x) { p++; };
+ }
+ size = p - yyInput;
+ if (size < map->minSize) {
+ /* missing input -> error */
+ goto not_match;
+ }
+ /* string 2 number, put number into info structure by offset */
+ p = yyInput; x = p + size;
+ if (!(map->flags & CLF_LOCALSEC)) {
+ if (_str2int((time_t *)(((char *)info) + map->offs),
+ p, x, sign) != TCL_OK) {
+ goto overflow;
+ }
+ p = x;
+ } else {
+ if (_str2wideInt((Tcl_WideInt *)(((char *)info) + map->offs),
+ p, x, sign) != TCL_OK) {
+ goto overflow;
+ }
+ p = x;
+ }
+ flags = (flags & ~map->clearFlags) | map->flags;
+ }
+ break;
+ case CTOKT_PARSER:
+ switch (map->parser(opts, info, tok)) {
+ case TCL_OK:
+ break;
+ case TCL_RETURN:
+ goto not_match;
+ break;
+ default:
+ goto done;
+ break;
+ };
+ p = yyInput;
+ flags = (flags & ~map->clearFlags) | map->flags;
+ break;
+ case CTOKT_SPACE:
+ /* at least one space in strict mode */
+ if (opts->flags & CLF_STRICT) {
+ if (!isspace(UCHAR(*p))) {
+ /* unmatched -> error */
+ goto not_match;
+ }
+ p++;
+ }
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ break;
+ case CTOKT_WORD:
+ x = FindWordEnd(tok, p, end);
+ if (!x) {
+ /* no match -> error */
+ goto not_match;
+ }
+ p = x;
+ continue;
+ break;
+ }
+ }
+
+ /* ignore spaces at end */
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ /* check end was reached */
+ if (p < end) {
+ /* something after last token - wrong format */
+ goto not_match;
+ }
+
+ /*
+ * Invalidate result
+ */
+
+ /* seconds token (%s) take precedence over all other tokens */
+ if ((opts->flags & CLF_EXTENDED) || !(flags & CLF_LOCALSEC)) {
+ if (flags & CLF_DATE) {
+
+ if (!(flags & CLF_JULIANDAY)) {
+ info->flags |= CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY;
+
+ /* dd precedence below ddd */
+ switch (flags & (CLF_MONTH|CLF_DAYOFYEAR|CLF_DAYOFMONTH)) {
+ case (CLF_DAYOFYEAR|CLF_DAYOFMONTH):
+ /* miss month: ddd over dd (without month) */
+ flags &= ~CLF_DAYOFMONTH;
+ case (CLF_DAYOFYEAR):
+ /* ddd over naked weekday */
+ if (!(flags & CLF_ISO8601YEAR)) {
+ flags &= ~CLF_ISO8601;
+ }
+ break;
+ case (CLF_MONTH|CLF_DAYOFYEAR|CLF_DAYOFMONTH):
+ /* both available: mmdd over ddd */
+ flags &= ~CLF_DAYOFYEAR;
+ case (CLF_MONTH|CLF_DAYOFMONTH):
+ case (CLF_DAYOFMONTH):
+ /* mmdd / dd over naked weekday */
+ if (!(flags & CLF_ISO8601YEAR)) {
+ flags &= ~CLF_ISO8601;
+ }
+ break;
+ }
+
+ /* YearWeekDay below YearMonthDay */
+ if ( (flags & CLF_ISO8601)
+ && ( (flags & (CLF_YEAR|CLF_DAYOFYEAR)) == (CLF_YEAR|CLF_DAYOFYEAR)
+ || (flags & (CLF_YEAR|CLF_DAYOFMONTH|CLF_MONTH)) == (CLF_YEAR|CLF_DAYOFMONTH|CLF_MONTH)
+ )
+ ) {
+ /* yy precedence below yyyy */
+ if (!(flags & CLF_ISO8601CENTURY) && (flags & CLF_CENTURY)) {
+ /* normally precedence of ISO is higher, but no century - so put it down */
+ flags &= ~CLF_ISO8601;
+ }
+ else
+ /* yymmdd or yyddd over naked weekday */
+ if (!(flags & CLF_ISO8601YEAR)) {
+ flags &= ~CLF_ISO8601;
+ }
+ }
+
+ if (!(flags & CLF_ISO8601)) {
+ if (yyYear < 100) {
+ if (!(flags & CLF_CENTURY)) {
+ if (yyYear >= dataPtr->yearOfCenturySwitch) {
+ yyYear -= 100;
+ }
+ yyYear += dataPtr->currentYearCentury;
+ } else {
+ yyYear += info->dateCentury * 100;
+ }
+ }
+ } else {
+ if (info->date.iso8601Year < 100) {
+ if (!(flags & CLF_ISO8601CENTURY)) {
+ if (info->date.iso8601Year >= dataPtr->yearOfCenturySwitch) {
+ info->date.iso8601Year -= 100;
+ }
+ info->date.iso8601Year += dataPtr->currentYearCentury;
+ } else {
+ info->date.iso8601Year += info->dateCentury * 100;
+ }
+ }
+ }
+ }
+ }
+
+ /* if no time - reset time */
+ if (!(flags & (CLF_TIME|CLF_LOCALSEC))) {
+ info->flags |= CLF_ASSEMBLE_SECONDS;
+ yydate.localSeconds = 0;
+ }
+
+ if (flags & CLF_TIME) {
+ info->flags |= CLF_ASSEMBLE_SECONDS;
+ yySeconds = ToSeconds(yyHour, yyMinutes,
+ yySeconds, yyMeridian);
+ } else
+ if (!(flags & CLF_LOCALSEC)) {
+ info->flags |= CLF_ASSEMBLE_SECONDS;
+ yySeconds = yydate.localSeconds % SECONDS_PER_DAY;
+ }
+ }
+
+ /* tell caller which flags were set */
+ info->flags |= flags;
+
+ ret = TCL_OK;
+ goto done;
+
+overflow:
+
+ Tcl_SetResult(interp, "requested date too large to represent",
+ TCL_STATIC);
+ Tcl_SetErrorCode(interp, "CLOCK", "dateTooLarge", NULL);
+ goto done;
+
+not_match:
+
+ Tcl_SetResult(interp, "input string does not match supplied format",
+ TCL_STATIC);
+ Tcl_SetErrorCode(interp, "CLOCK", "badInputString", NULL);
+
+done:
+
+ return ret;
+#endif
+}
+
MODULE_SCOPE void
ClockFrmScnClearCaches(void)
diff --git a/generic/tclDate.h b/generic/tclDate.h
index 9e1c506..112ed31 100644
--- a/generic/tclDate.h
+++ b/generic/tclDate.h
@@ -2,7 +2,7 @@
* tclDate.h --
*
* This header file handles common usage of clock primitives
- * between tclDate.c (yacc) and tclClock.c.
+ * between tclDate.c (yacc), tclClock.c and tclClockFmt.c.
*
* Copyright (c) 2014 Serg G. Brester (aka sebres)
*
@@ -317,22 +317,24 @@ typedef struct ClockClientData {
Tcl_Obj *timezoneObj;
TclDateFields Date;
} lastBase;
- /* Las-minute cache for fast UTC2Local conversion */
+ /* Las-period cache for fast UTC2Local conversion */
struct {
/* keys */
Tcl_Obj *timezoneObj;
int changeover;
Tcl_WideInt seconds;
+ Tcl_WideInt rangesVal[2]; /* Bounds for cached time zone offset */
/* values */
time_t tzOffset;
Tcl_Obj *tzName;
} UTC2Local;
- /* Las-minute cache for fast Local2UTC conversion */
+ /* Las-period cache for fast Local2UTC conversion */
struct {
/* keys */
Tcl_Obj *timezoneObj;
int changeover;
Tcl_WideInt localSeconds;
+ Tcl_WideInt rangesVal[2]; /* Bounds for cached time zone offset */
/* values */
time_t tzOffset;
} Local2UTC;
@@ -372,8 +374,22 @@ typedef enum _CLCKTOK_TYPE {
typedef struct ClockFmtScnStorage ClockFmtScnStorage;
+typedef struct ClockFormatTokenMap {
+ unsigned short int type;
+ unsigned short int flags;
+ unsigned short int clearFlags;
+ unsigned short int minSize;
+ unsigned short int maxSize;
+ unsigned short int offs;
+ ClockScanTokenProc *parser;
+ void *data;
+} ClockFormatTokenMap;
typedef struct ClockFormatToken {
- CLCKTOK_TYPE type;
+ ClockFormatTokenMap *map;
+ struct {
+ const char *start;
+ const char *end;
+ } tokWord;
} ClockFormatToken;
typedef struct ClockScanTokenMap {
@@ -447,10 +463,14 @@ MODULE_SCOPE ClockFmtScnStorage *
Tcl_Obj *objPtr);
MODULE_SCOPE Tcl_Obj *
ClockLocalizeFormat(ClockFmtScnCmdArgs *opts);
+
MODULE_SCOPE int ClockScan(ClientData clientData, Tcl_Interp *interp,
register DateInfo *info,
Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts);
+MODULE_SCOPE int ClockFormat(ClientData clientData, Tcl_Interp *interp,
+ register DateInfo *info, ClockFmtScnCmdArgs *opts);
+
MODULE_SCOPE void ClockFrmScnClearCaches(void);
/*
diff --git a/library/clock.tcl b/library/clock.tcl
index ca12f4a..c4e698c 100755
--- a/library/clock.tcl
+++ b/library/clock.tcl
@@ -685,20 +685,6 @@ proc ::tcl::clock::format { args } {
set locale [string tolower $locale]
set clockval [lindex $args 0]
- # Get the data for time changes in the given zone
-
- if {$timezone eq ""} {
- if {[set timezone [configure -system-tz]] eq ""} {
- set timezone [GetSystemTimeZone]
- }
- }
- if {![info exists TZData($timezone)]} {
- if {[catch {set timezone [SetupTimeZone $timezone]} retval opts]} {
- dict unset opts -errorinfo
- return -options $opts $retval
- }
- }
-
# Build a procedure to format the result. Cache the built procedure's name
# in the 'FormatProc' array to avoid losing its internal representation,
# which contains the name resolution.