summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclClock.c30
-rw-r--r--generic/tclClockFmt.c511
-rw-r--r--generic/tclDate.h9
-rw-r--r--tests-perf/clock.perf.tcl75
4 files changed, 478 insertions, 147 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index 2e2c44b..28a484f 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -70,8 +70,6 @@ TCL_DECLARE_MUTEX(clockMutex)
* Function prototypes for local procedures in this file:
*/
-static int ConvertUTCToLocal(ClientData clientData, Tcl_Interp *,
- TclDateFields *, Tcl_Obj *timezoneObj, int);
static int ConvertUTCToLocalUsingTable(Tcl_Interp *,
TclDateFields *, int, Tcl_Obj *const[],
Tcl_WideInt rangesVal[2]);
@@ -86,8 +84,6 @@ 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 *, Tcl_WideInt rangesVal[2]);
static void GetYearWeekDay(TclDateFields *, int);
static void GetGregorianEraYearDay(TclDateFields *, int);
static void GetMonthDay(TclDateFields *);
@@ -1730,7 +1726,7 @@ ConvertLocalToUTCUsingC(
*----------------------------------------------------------------------
*/
-static int
+MODULE_SCOPE int
ConvertUTCToLocal(
ClientData clientData, /* Client data of the interpreter */
Tcl_Interp *interp, /* Tcl interpreter */
@@ -1974,7 +1970,7 @@ ConvertUTCToLocalUsingC(
*----------------------------------------------------------------------
*/
-static Tcl_Obj *
+MODULE_SCOPE Tcl_Obj *
LookupLastTransition(
Tcl_Interp *interp, /* Interpreter for error messages */
Tcl_WideInt tick, /* Time from the epoch */
@@ -2950,7 +2946,7 @@ ClockFormatObjCmd(
DateFormat dateFmt; /* Common structure used for formatting */
if ((objc & 1) == 1) {
- Tcl_WrongNumArgs(interp, 1, objv, "string "
+ Tcl_WrongNumArgs(interp, 1, objv, "clockval "
"?-format string? "
"?-gmt boolean? "
"?-locale LOCALE? ?-timezone ZONE?");
@@ -2974,6 +2970,16 @@ ClockFormatObjCmd(
return TCL_ERROR;
}
+ /*
+ * seconds could be an unsigned number that overflowed. Make sure
+ * that it isn't.
+ */
+
+ if (objv[1]->typePtr == &tclBignumType) {
+ Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]);
+ return TCL_ERROR;
+ }
+
memset(&dateFmt, 0, sizeof(dateFmt));
/*
@@ -3065,6 +3071,16 @@ ClockScanObjCmd(
if (Tcl_GetWideIntFromObj(interp, opts.baseObj, &baseVal) != TCL_OK) {
return TCL_ERROR;
}
+ /*
+ * seconds could be an unsigned number that overflowed. Make sure
+ * that it isn't.
+ */
+
+ if (opts.baseObj->typePtr == &tclBignumType) {
+ Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]);
+ return TCL_ERROR;
+ }
+
} else {
Tcl_Time now;
Tcl_GetTime(&now);
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c
index f1fdf27..55e328c 100644
--- a/generic/tclClockFmt.c
+++ b/generic/tclClockFmt.c
@@ -101,6 +101,161 @@ _str2wideInt(
return TCL_OK;
}
+inline char *
+_itoaw(
+ char *buf,
+ register int val,
+ char padchar,
+ unsigned short int width)
+{
+ register char *p;
+ static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
+
+ /* positive integer */
+
+ if (val >= 0)
+ {
+ /* check resp. recalculate width */
+ while (width <= 9 && val >= wrange[width]) {
+ width++;
+ }
+ /* number to string backwards */
+ p = buf + width;
+ *p-- = '\0';
+ do {
+ register char c = (val % 10); val /= 10;
+ *p-- = '0' + c;
+ } while (val > 0);
+ /* fulling with pad-char */
+ while (p >= buf) {
+ *p-- = padchar;
+ }
+
+ return buf + width;
+ }
+ /* negative integer */
+
+ if (!width) width++;
+ /* check resp. recalculate width (regarding sign) */
+ width--;
+ while (width <= 9 && val <= -wrange[width]) {
+ width++;
+ }
+ width++;
+ /* number to string backwards */
+ p = buf + width;
+ *p-- = '\0';
+ /* differentiate platforms with -1 % 10 == 1 and -1 % 10 == -1 */
+ if (-1 % 10 == -1) {
+ do {
+ register char c = (val % 10); val /= 10;
+ *p-- = '0' - c;
+ } while (val < 0);
+ } else {
+ do {
+ register char c = (val % 10); val /= 10;
+ *p-- = '0' + c;
+ } while (val < 0);
+ }
+ /* sign by 0 padding */
+ if (padchar != '0') { *p-- = '-'; }
+ /* fulling with pad-char */
+ while (p >= buf + 1) {
+ *p-- = padchar;
+ }
+ /* sign by non 0 padding */
+ if (padchar == '0') { *p = '-'; }
+
+ return buf + width;
+}
+
+inline char *
+_witoaw(
+ char *buf,
+ register Tcl_WideInt val,
+ char padchar,
+ unsigned short int width)
+{
+ register char *p;
+ static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
+
+ /* positive integer */
+
+ if (val >= 0)
+ {
+ /* check resp. recalculate width */
+ if (val >= 10000000000L) {
+ Tcl_WideInt val2;
+ val2 = val / 10000000000L;
+ while (width <= 9 && val2 >= wrange[width]) {
+ width++;
+ }
+ width += 10;
+ } else {
+ while (width <= 9 && val >= wrange[width]) {
+ width++;
+ }
+ }
+ /* number to string backwards */
+ p = buf + width;
+ *p-- = '\0';
+ do {
+ register char c = (val % 10); val /= 10;
+ *p-- = '0' + c;
+ } while (val > 0);
+ /* fulling with pad-char */
+ while (p >= buf) {
+ *p-- = padchar;
+ }
+
+ return buf + width;
+ }
+
+ /* negative integer */
+
+ if (!width) width++;
+ /* check resp. recalculate width (regarding sign) */
+ width--;
+ if (val <= 10000000000L) {
+ Tcl_WideInt val2;
+ val2 = val / 10000000000L;
+ while (width <= 9 && val2 <= -wrange[width]) {
+ width++;
+ }
+ width += 10;
+ } else {
+ while (width <= 9 && val <= -wrange[width]) {
+ width++;
+ }
+ }
+ width++;
+ /* number to string backwards */
+ p = buf + width;
+ *p-- = '\0';
+ /* differentiate platforms with -1 % 10 == 1 and -1 % 10 == -1 */
+ if (-1 % 10 == -1) {
+ do {
+ register char c = (val % 10); val /= 10;
+ *p-- = '0' - c;
+ } while (val < 0);
+ } else {
+ do {
+ register char c = (val % 10); val /= 10;
+ *p-- = '0' + c;
+ } while (val < 0);
+ }
+ /* sign by 0 padding */
+ if (padchar != '0') { *p-- = '-'; }
+ /* fulling with pad-char */
+ while (p >= buf + 1) {
+ *p-- = padchar;
+ }
+ /* sign by non 0 padding */
+ if (padchar == '0') { *p = '-'; }
+
+ return buf + width;
+}
+
/*
*----------------------------------------------------------------------
*/
@@ -1760,51 +1915,6 @@ done:
return ret;
}
-
-
-inline char *
-_itoaw(
- char *buf,
- register int val,
- char padchar,
- unsigned short int width)
-{
- register char *p;
- static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
- unsigned short int sign = 0;
-
- /* sign = 1 by negative number */
- if (val < 0)
- {
- sign = 1;
- val = -val;
- if (!width) width++;
- }
- /* check resp. recalculate width (regarding sign) */
- width -= sign;
- while (width <= 9 && val >= wrange[width]) {
- width++;
- }
- width += sign;
- /* number to string backwards */
- p = buf + width;
- *p-- = '\0';
- do {
- *p-- = '0' + (val % 10);
- val /= 10;
- } while (val > 0);
- /* sign by 0 padding */
- if (sign && padchar != '0') { *p-- = '-'; sign = 0; }
- /* fulling with pad-char */
- while (p >= buf + sign) {
- *p-- = padchar;
- }
- /* sign by non 0 padding */
- if (sign) { *p = '-'; }
-
- return buf + width;
-}
-
inline int
FrmResultAllocate(
@@ -1833,7 +1943,7 @@ ClockFmtToken_HourAMPM_Proc(
ClockFormatToken *tok,
int *val)
{
- *val = ( ( ( *val % 86400 ) + 86400 - 3600 ) / 3600 ) % 12 + 1;
+ *val = ( ( ( *val % SECONDS_PER_DAY ) + SECONDS_PER_DAY - 3600 ) / 3600 ) % 12 + 1;
return TCL_OK;
}
@@ -1848,7 +1958,7 @@ ClockFmtToken_AMPM_Proc(
const char *s;
int len;
- if ((*val % 84600) < (84600 / 2)) {
+ if ((*val % SECONDS_PER_DAY) < (SECONDS_PER_DAY / 2)) {
mcObj = ClockMCGet(opts, MCLIT_AM);
} else {
mcObj = ClockMCGet(opts, MCLIT_PM);
@@ -1895,7 +2005,7 @@ ClockFmtToken_StarDate_Proc(
fractYear, '0', 3);
*dateFmt->output++ = '.';
dateFmt->output = _itoaw(dateFmt->output,
- dateFmt->date.localSeconds % 86400 / ( 86400 / 10 ), '0', 1);
+ dateFmt->date.localSeconds % SECONDS_PER_DAY / ( SECONDS_PER_DAY / 10 ), '0', 1);
return TCL_OK;
}
@@ -1916,9 +2026,162 @@ ClockFmtToken_WeekOfYear_Proc(
*val = ( dateFmt->date.dayOfYear - dow + 7 ) / 7;
return TCL_OK;
}
+static int
+ClockFmtToken_TimeZone_Proc(
+ ClockFmtScnCmdArgs *opts,
+ DateFormat *dateFmt,
+ ClockFormatToken *tok,
+ int *val)
+{
+ if (*tok->tokWord.start == 'z') {
+ int z = dateFmt->date.tzOffset;
+ char sign = '+';
+ if ( z < 0 ) {
+ z = -z;
+ sign = '-';
+ }
+ if (FrmResultAllocate(dateFmt, 7) != TCL_OK) { return TCL_ERROR; };
+ *dateFmt->output++ = sign;
+ dateFmt->output = _itoaw(dateFmt->output, z / 3600, '0', 2);
+ z %= 3600;
+ dateFmt->output = _itoaw(dateFmt->output, z / 60, '0', 2);
+ z %= 60;
+ if (z != 0) {
+ dateFmt->output = _itoaw(dateFmt->output, z, '0', 2);
+ }
+ } else {
+ Tcl_Obj * objPtr;
+ const char *s; int len;
+ /* convert seconds to local seconds to obtain tzName object */
+ if (ConvertUTCToLocal(opts->clientData, opts->interp,
+ &dateFmt->date, opts->timezoneObj,
+ GREGORIAN_CHANGE_DATE) != TCL_OK) {
+ return TCL_ERROR;
+ };
+ objPtr = dateFmt->date.tzName;
+ s = TclGetString(objPtr);
+ len = objPtr->length;
+ if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; };
+ memcpy(dateFmt->output, s, len + 1);
+ dateFmt->output += len;
+ }
+ return TCL_OK;
+}
+
+static int
+ClockFmtToken_LocaleERA_Proc(
+ ClockFmtScnCmdArgs *opts,
+ DateFormat *dateFmt,
+ ClockFormatToken *tok,
+ int *val)
+{
+ Tcl_Obj *mcObj;
+ const char *s;
+ int len;
+
+ if (dateFmt->date.era == BCE) {
+ mcObj = ClockMCGet(opts, MCLIT_BCE);
+ } else {
+ mcObj = ClockMCGet(opts, MCLIT_CE);
+ }
+ if (mcObj == NULL) {
+ return TCL_ERROR;
+ }
+ s = TclGetString(mcObj); len = mcObj->length;
+ if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; };
+ memcpy(dateFmt->output, s, len + 1);
+ dateFmt->output += len;
+
+ return TCL_OK;
+}
+
+static int
+ClockFmtToken_LocaleERAYear_Proc(
+ ClockFmtScnCmdArgs *opts,
+ DateFormat *dateFmt,
+ ClockFormatToken *tok,
+ int *val)
+{
+ int rowc;
+ Tcl_Obj **rowv;
+
+ if (dateFmt->localeEra == NULL) {
+ Tcl_Obj *mcObj = ClockMCGet(opts, MCLIT_LOCALE_ERAS);
+ if (mcObj == NULL) {
+ return TCL_ERROR;
+ }
+ if (TclListObjGetElements(opts->interp, mcObj, &rowc, &rowv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (rowc != 0) {
+ dateFmt->localeEra = LookupLastTransition(opts->interp,
+ dateFmt->date.localSeconds, rowc, rowv, NULL);
+ }
+ if (dateFmt->localeEra == NULL) {
+ dateFmt->localeEra = (Tcl_Obj*)1;
+ }
+ }
+
+ /* if no LOCALE_ERAS in catalog or era not found */
+ if (dateFmt->localeEra == (Tcl_Obj*)1) {
+ if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { return TCL_ERROR; };
+ if (*tok->tokWord.start == 'C') { /* %EC */
+ *val = dateFmt->date.year / 100;
+ dateFmt->output = _itoaw(dateFmt->output,
+ *val, '0', 2);
+ } else { /* %Ey */
+ *val = dateFmt->date.year % 100;
+ dateFmt->output = _itoaw(dateFmt->output,
+ *val, '0', 2);
+ }
+ } else {
+ Tcl_Obj *objPtr;
+ const char *s;
+ int len;
+ if (*tok->tokWord.start == 'C') { /* %EC */
+ if (Tcl_ListObjIndex(opts->interp, dateFmt->localeEra, 1,
+ &objPtr) != TCL_OK ) {
+ return TCL_ERROR;
+ }
+ } else { /* %Ey */
+ if (Tcl_ListObjIndex(opts->interp, dateFmt->localeEra, 2,
+ &objPtr) != TCL_OK ) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(opts->interp, objPtr, val) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *val = dateFmt->date.year - *val;
+ /* if year in locale numerals */
+ if (*val >= 0 && *val < 100) {
+ /* year as integer */
+ Tcl_Obj * mcObj = ClockMCGet(opts, MCLIT_LOCALE_NUMERALS);
+ if (mcObj == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_ListObjIndex(opts->interp, mcObj, *val, &objPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ /* year as integer */
+ if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { return TCL_ERROR; };
+ dateFmt->output = _itoaw(dateFmt->output,
+ *val, '0', 2);
+ return TCL_OK;
+ }
+ }
+ s = TclGetString(objPtr);
+ len = objPtr->length;
+ if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; };
+ memcpy(dateFmt->output, s, len + 1);
+ dateFmt->output += len;
+ }
+ return TCL_OK;
+}
+
static const char *FmtSTokenMapIndex =
- "demNbByYCHMSIklpaAuwUVgGjJsntQ";
+ "demNbByYCHMSIklpaAuwUVzgGjJsntQ";
static ClockFormatTokenMap FmtSTokenMap[] = {
/* %d */
{CFMTT_INT, "0", 2, 0, 0, 0, TclOffset(DateFormat, date.dayOfMonth), NULL},
@@ -1941,21 +2204,21 @@ static ClockFormatTokenMap FmtSTokenMap[] = {
/* %C */
{CFMTT_INT, "0", 2, 0, 100, 0, TclOffset(DateFormat, date.year), NULL},
/* %H */
- {CFMTT_INT, "0", 2, 0, 3600, 24, TclOffset(DateFormat, date.localSeconds), NULL},
+ {CFMTT_INT, "0", 2, 0, 3600, 24, TclOffset(DateFormat, date.secondOfDay), NULL},
/* %M */
- {CFMTT_INT, "0", 2, 0, 60, 60, TclOffset(DateFormat, date.localSeconds), NULL},
+ {CFMTT_INT, "0", 2, 0, 60, 60, TclOffset(DateFormat, date.secondOfDay), NULL},
/* %S */
- {CFMTT_INT, "0", 2, 0, 0, 60, TclOffset(DateFormat, date.localSeconds), NULL},
+ {CFMTT_INT, "0", 2, 0, 0, 60, TclOffset(DateFormat, date.secondOfDay), NULL},
/* %I */
- {CFMTT_INT, "0", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.localSeconds),
+ {CFMTT_INT, "0", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.secondOfDay),
ClockFmtToken_HourAMPM_Proc, NULL},
/* %k */
- {CFMTT_INT, " ", 2, 0, 3600, 24, TclOffset(DateFormat, date.localSeconds), NULL},
+ {CFMTT_INT, " ", 2, 0, 3600, 24, TclOffset(DateFormat, date.secondOfDay), NULL},
/* %l */
- {CFMTT_INT, " ", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.localSeconds),
+ {CFMTT_INT, " ", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.secondOfDay),
ClockFmtToken_HourAMPM_Proc, NULL},
/* %p %P */
- {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.localSeconds),
+ {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.secondOfDay),
ClockFmtToken_AMPM_Proc, NULL},
/* %a */
{CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, TclOffset(DateFormat, date.dayOfWeek),
@@ -1972,6 +2235,9 @@ static ClockFormatTokenMap FmtSTokenMap[] = {
ClockFmtToken_WeekOfYear_Proc, NULL},
/* %V */
{CFMTT_INT, "0", 2, 0, 0, 0, TclOffset(DateFormat, date.iso8601Week), NULL},
+ /* %z %Z */
+ {CFMTT_INT, NULL, 0, 0, 0, 0, 0,
+ ClockFmtToken_TimeZone_Proc, NULL},
/* %g */
{CFMTT_INT, "0", 2, 0, 0, 100, TclOffset(DateFormat, date.iso8601Year), NULL},
/* %G */
@@ -1981,7 +2247,7 @@ static ClockFormatTokenMap FmtSTokenMap[] = {
/* %J */
{CFMTT_INT, "0", 7, 0, 0, 0, TclOffset(DateFormat, date.julianDay), NULL},
/* %s */
- {CFMTT_WIDE, "%ld", 0, 0, 0, 0, TclOffset(DateFormat, date.seconds), NULL},
+ {CFMTT_WIDE, "0", 1, 0, 0, 0, TclOffset(DateFormat, date.seconds), NULL},
/* %n */
{CFMTT_CHAR, "\n", 0, 0, 0, 0, 0, NULL},
/* %t */
@@ -1989,103 +2255,61 @@ static ClockFormatTokenMap FmtSTokenMap[] = {
/* %Q */
{CFMTT_INT, NULL, 0, 0, 0, 0, 0,
ClockFmtToken_StarDate_Proc, NULL},
-
-#if 0
- /* %H %k %I %l */
- {CFMTT_INT, CLF_TIME, 1, 2, TclOffset(DateFormat, date.hour),
- NULL},
- /* %M */
- {CFMTT_INT, CLF_TIME, 1, 2, TclOffset(DateFormat, date.minutes),
- NULL},
- /* %S */
- {CFMTT_INT, CLF_TIME, 1, 2, TclOffset(DateFormat, date.secondOfDay),
- NULL},
- /* %p %P */
- {CTOKT_PARSER, CLF_ISO8601, 0, 0, 0,
- ClockFmtToken_amPmInd_Proc, NULL},
- /* %J */
- {CFMTT_INT, CLF_JULIANDAY, 1, 0xffff, TclOffset(DateFormat, date.julianDay),
- NULL},
- /* %j */
- {CFMTT_INT, CLF_DAYOFYEAR, 1, 3, TclOffset(DateFormat, date.dayOfYear),
- NULL},
- /* %g */
- {CFMTT_INT, CLF_ISO8601YEAR | CLF_ISO8601, 2, 2, TclOffset(DateFormat, date.iso8601Year),
- NULL},
- /* %G */
- {CFMTT_INT, CLF_ISO8601YEAR | CLF_ISO8601 | CLF_ISO8601CENTURY, 4, 4, TclOffset(DateFormat, date.iso8601Year),
- NULL},
- /* %V */
- {CFMTT_INT, CLF_ISO8601, 1, 2, TclOffset(DateFormat, date.iso8601Week),
- NULL},
- /* %a %A %u %w */
- {CTOKT_PARSER, CLF_ISO8601, 0, 0, 0,
- ClockFmtToken_DayOfWeek_Proc, NULL},
- /* %z %Z */
- {CTOKT_PARSER, CLF_OPTIONAL, 0, 0, 0,
- ClockFmtToken_TimeZone_Proc, NULL},
- /* %s */
- {CFMTT_INT, CLF_LOCALSEC | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateFormat, date.localSeconds),
- NULL},
-#endif
};
static const char *FmtSTokenMapAliasIndex[2] = {
- "hPW",
- "bpU"
+ "hPWZ",
+ "bpUz"
};
static const char *FmtETokenMapIndex =
-"";// "Ey";
+ "Ey";
static ClockFormatTokenMap FmtETokenMap[] = {
- {CFMTT_INT, "0", 2, 0, 0, 0, 0, NULL},
-#if 0
/* %EE */
- {CTOKT_PARSER, 0, 0, 0, 0, TclOffset(DateFormat, date.year),
- ClockFmtToken_LocaleERA_Proc, (void *)MCLIT_LOCALE_NUMERALS},
- /* %Ey */
- {CTOKT_PARSER, 0, 0, 0, 0, 0, /* currently no capture, parse only token */
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
-#endif
+ {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.era),
+ ClockFmtToken_LocaleERA_Proc, NULL},
+ /* %Ey %EC */
+ {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.year),
+ ClockFmtToken_LocaleERAYear_Proc, NULL},
};
static const char *FmtETokenMapAliasIndex[2] = {
- "",
- ""
+ "C",
+ "y"
};
static const char *FmtOTokenMapIndex =
-"";// "dmyHMSu";
+ "dmyHIMSuw";
static ClockFormatTokenMap FmtOTokenMap[] = {
- {CFMTT_INT, "0", 2, 0, 0, 0, 0, NULL},
-#if 0
/* %Od %Oe */
- {CTOKT_PARSER, CLF_DAYOFMONTH, 0, 0, 0, TclOffset(DateFormat, date.dayOfMonth),
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.dayOfMonth),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
/* %Om */
- {CTOKT_PARSER, CLF_MONTH, 0, 0, 0, TclOffset(DateFormat, date.month),
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.month),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
/* %Oy */
- {CTOKT_PARSER, CLF_YEAR, 0, 0, 0, TclOffset(DateFormat, date.year),
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
- /* %OH %Ok %OI %Ol */
- {CTOKT_PARSER, CLF_TIME, 0, 0, 0, TclOffset(DateFormat, date.hour),
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.year),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
+ /* %OH %Ok */
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 3600, 24, TclOffset(DateFormat, date.secondOfDay),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
+ /* %OI %Ol */
+ {CFMTT_INT, NULL, 0, CLFMT_CALC | CLFMT_LOCALE_INDX, 0, 0, TclOffset(DateFormat, date.secondOfDay),
+ ClockFmtToken_HourAMPM_Proc, (void *)MCLIT_LOCALE_NUMERALS},
/* %OM */
- {CTOKT_PARSER, CLF_TIME, 0, 0, 0, TclOffset(DateFormat, date.minutes),
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 60, 60, TclOffset(DateFormat, date.secondOfDay),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
/* %OS */
- {CTOKT_PARSER, CLF_TIME, 0, 0, 0, TclOffset(DateFormat, date.secondOfDay),
- ClockFmtToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
- /* %Ou Ow */
- {CTOKT_PARSER, CLF_ISO8601, 0, 0, 0, 0,
- ClockFmtToken_DayOfWeek_Proc, (void *)MCLIT_LOCALE_NUMERALS},
-#endif
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 60, TclOffset(DateFormat, date.secondOfDay),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
+ /* %Ou */
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.dayOfWeek),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
+ /* %Ow */
+ {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, TclOffset(DateFormat, date.dayOfWeek),
+ NULL, (void *)MCLIT_LOCALE_NUMERALS},
};
static const char *FmtOTokenMapAliasIndex[2] = {
-"", ""
-#if 0
- "ekIlw",
- "dHHHu"
-#endif
+ "ekl",
+ "dHI"
};
static ClockFormatTokenMap FmtWordTokenMap = {
@@ -2232,7 +2456,14 @@ ClockFormat(
return TCL_ERROR;
}
- dateFmt->resMem = ckalloc(MIN_FMT_RESULT_BLOCK_ALLOC); /* result container object */
+ /* prepare formatting */
+ dateFmt->date.secondOfDay = (int)(dateFmt->date.localSeconds % SECONDS_PER_DAY);
+ if (dateFmt->date.secondOfDay < 0) {
+ dateFmt->date.secondOfDay += SECONDS_PER_DAY;
+ }
+
+ /* result container object */
+ dateFmt->resMem = ckalloc(MIN_FMT_RESULT_BLOCK_ALLOC);
if (dateFmt->resMem == NULL) {
return TCL_ERROR;
}
@@ -2283,7 +2514,9 @@ ClockFormat(
if (mcObj == NULL) {
goto error;
}
- if (Tcl_ListObjIndex(opts->interp, mcObj, val, &mcObj) != TCL_OK) {
+ if ( Tcl_ListObjIndex(opts->interp, mcObj, val, &mcObj) != TCL_OK
+ || mcObj == NULL
+ ) {
goto error;
}
s = TclGetString(mcObj);
@@ -2297,7 +2530,11 @@ ClockFormat(
if (1) {
Tcl_WideInt val = *(Tcl_WideInt *)(((char *)dateFmt) + map->offs);
if (FrmResultAllocate(dateFmt, 21) != TCL_OK) { goto error; };
- dateFmt->output += sprintf(dateFmt->output, map->tostr, val);
+ if (map->width) {
+ dateFmt->output = _witoaw(dateFmt->output, val, *map->tostr, map->width);
+ } else {
+ dateFmt->output += sprintf(dateFmt->output, map->tostr, val);
+ }
}
break;
case CFMTT_CHAR:
diff --git a/generic/tclDate.h b/generic/tclDate.h
index c5d7da5..7c68b2a 100644
--- a/generic/tclDate.h
+++ b/generic/tclDate.h
@@ -109,6 +109,7 @@ typedef enum ClockMsgCtLiteral {
MCLIT_MONTHS_FULL, MCLIT_MONTHS_ABBREV, MCLIT_MONTHS_COMB,
MCLIT_DAYS_OF_WEEK_FULL, MCLIT_DAYS_OF_WEEK_ABBREV, MCLIT_DAYS_OF_WEEK_COMB,
MCLIT_AM, MCLIT_PM,
+ MCLIT_LOCALE_ERAS,
MCLIT_BCE, MCLIT_CE,
MCLIT_BCE2, MCLIT_CE2,
MCLIT_BCE3, MCLIT_CE3,
@@ -121,6 +122,7 @@ typedef enum ClockMsgCtLiteral {
pref "MONTHS_FULL", pref "MONTHS_ABBREV", pref "MONTHS_COMB", \
pref "DAYS_OF_WEEK_FULL", pref "DAYS_OF_WEEK_ABBREV", pref "DAYS_OF_WEEK_COMB", \
pref "AM", pref "PM", \
+ pref "LOCALE_ERAS", \
pref "BCE", pref "CE", \
pref "b.c.e.", pref "c.e.", \
pref "b.c.", pref "a.d.", \
@@ -387,6 +389,8 @@ typedef struct DateFormat {
char *output;
TclDateFields date;
+
+ Tcl_Obj *localeEra;
} DateFormat;
#define CLFMT_INCR (1 << 3)
@@ -445,6 +449,11 @@ typedef struct ClockFmtScnStorage {
MODULE_SCOPE time_t ToSeconds(time_t Hours, time_t Minutes,
time_t Seconds, MERIDIAN Meridian);
MODULE_SCOPE int IsGregorianLeapYear(TclDateFields *);
+MODULE_SCOPE int ConvertUTCToLocal(ClientData clientData, Tcl_Interp *,
+ TclDateFields *, Tcl_Obj *timezoneObj, int);
+MODULE_SCOPE Tcl_Obj *
+ LookupLastTransition(Tcl_Interp *, Tcl_WideInt,
+ int, Tcl_Obj *const *, Tcl_WideInt rangesVal[2]);
MODULE_SCOPE int TclClockFreeScan(Tcl_Interp *interp, DateInfo *info);
diff --git a/tests-perf/clock.perf.tcl b/tests-perf/clock.perf.tcl
index 41cc9e1..043d27f 100644
--- a/tests-perf/clock.perf.tcl
+++ b/tests-perf/clock.perf.tcl
@@ -56,7 +56,7 @@ proc _test_out_total {} {
puts [format "Total %d cases in %.2f sec.:" [llength $_(itcnt)] [expr {[llength $_(itcnt)] * $_(reptime) / 1000.0}]]
lset _(m) 0 [format %.6f [expr [join $_(ittm) +]]]
lset _(m) 2 [expr [join $_(itcnt) +]]
- lset _(m) 4 [expr {[lindex $_(m) 2] / ([llength $_(itcnt)] * $_(reptime) / 1000.0)}]
+ lset _(m) 4 [format %.3f [expr {[lindex $_(m) 2] / ([llength $_(itcnt)] * $_(reptime) / 1000.0)}]]
puts $_(m)
puts "Average:"
lset _(m) 0 [format %.6f [expr {[lindex $_(m) 0] / [llength $_(itcnt)]}]]
@@ -95,6 +95,74 @@ proc _test_run {reptime lst {outcmd {puts $_(r)}}} {
_test_out_total
}
+proc test-format {{reptime 1000}} {
+ _test_run $reptime {
+ # Format : date only (in gmt)
+ {clock format 1482525936 -format "%Y-%m-%d" -gmt 1}
+ # Format : date only (system zone)
+ {clock format 1482525936 -format "%Y-%m-%d"}
+ # Format : date only (CEST)
+ {clock format 1482525936 -format "%Y-%m-%d" -timezone :CET}
+ # Format : time only (in gmt)
+ {clock format 1482525936 -format "%H:%M" -gmt 1}
+ # Format : time only (system zone)
+ {clock format 1482525936 -format "%H:%M"}
+ # Format : time only (CEST)
+ {clock format 1482525936 -format "%H:%M" -timezone :CET}
+ # Format : time only (in gmt)
+ {clock format 1482525936 -format "%H:%M:%S" -gmt 1}
+ # Format : time only (system zone)
+ {clock format 1482525936 -format "%H:%M:%S"}
+ # Format : time only (CEST)
+ {clock format 1482525936 -format "%H:%M:%S" -timezone :CET}
+ # Format : default (in gmt)
+ {clock format 1482525936 -gmt 1}
+ # Format : default (system zone)
+ {clock format 1482525936}
+ # Format : default (CEST)
+ {clock format 1482525936 -timezone :CET}
+ # Format : ISO date-time (in gmt, numeric zone)
+ {clock format 1246379400 -format "%Y-%m-%dT%H:%M:%S %z" -gmt 1}
+ # Format : ISO date-time (system zone, CEST, numeric zone)
+ {clock format 1246379400 -format "%Y-%m-%dT%H:%M:%S %z"}
+ # Format : ISO date-time (CEST, numeric zone)
+ {clock format 1246379400 -format "%Y-%m-%dT%H:%M:%S %z" -timezone :CET}
+ # Format : ISO date-time (system zone, CEST)
+ {clock format 1246379400 -format "%Y-%m-%dT%H:%M:%S %Z"}
+ # Format : julian day with time (in gmt):
+ {clock format 1246379415 -format "%J %H:%M:%S" -gmt 1}
+ # Format : julian day with time (system zone):
+ {clock format 1246379415 -format "%J %H:%M:%S"}
+
+ # Format : locale date-time (en):
+ {clock format 1246379415 -format "%x %X" -locale en}
+ # Format : locale date-time (de):
+ {clock format 1246379415 -format "%x %X" -locale de}
+
+ # Format : locale lookup table month:
+ {clock format 1246379400 -format "%b" -locale en -gmt 1}
+ # Format : locale lookup 2 tables - month and day:
+ {clock format 1246379400 -format "%b %Od" -locale en -gmt 1}
+
+ # Format : dynamic clock value (without converter caches):
+ {set i 0; continue}
+ {clock format [incr i] -format "%Y-%m-%dT%H:%M:%S" -locale en -gmt 1}
+ {puts [clock format $i -format "%Y-%m-%dT%H:%M:%S" -locale en -gmt 1]\n; continue}
+ # Format : dynamic clock value (without any converter caches, zone range overflow):
+ {set i 0; continue}
+ {clock format [incr i 86400] -format "%Y-%m-%dT%H:%M:%S" -locale en -gmt 1}
+ {puts [clock format $i -format "%Y-%m-%dT%H:%M:%S" -locale en -gmt 1]\n; continue}
+
+ # Format : dynamic format (cacheable)
+ {clock format 1246379415 -format [string trim "%d.%m.%Y %H:%M:%S "] -gmt 1}
+
+ # Format : all (in gmt, locale en)
+ {clock format 1482525936 -format "%%a = %a | %%A = %A | %%b = %b | %%h = %h | %%B = %B | %%C = %C | %%d = %d | %%e = %e | %%g = %g | %%G = %G | %%H = %H | %%I = %I | %%j = %j | %%J = %J | %%k = %k | %%l = %l | %%m = %m | %%M = %M | %%N = %N | %%p = %p | %%P = %P | %%Q = %Q | %%s = %s | %%S = %S | %%t = %t | %%u = %u | %%U = %U | %%V = %V | %%w = %w | %%W = %W | %%y = %y | %%Y = %Y | %%z = %z | %%Z = %Z | %%n = %n | %%EE = %EE | %%EC = %EC | %%Ey = %Ey | %%n = %n | %%Od = %Od | %%Oe = %Oe | %%OH = %OH | %%Ok = %Ok | %%OI = %OI | %%Ol = %Ol | %%Om = %Om | %%OM = %OM | %%OS = %OS | %%Ou = %Ou | %%Ow = %Ow | %%Oy = %Oy" -gmt 1 -locale en}
+ # Format : all (in CET, locale de)
+ {clock format 1482525936 -format "%%a = %a | %%A = %A | %%b = %b | %%h = %h | %%B = %B | %%C = %C | %%d = %d | %%e = %e | %%g = %g | %%G = %G | %%H = %H | %%I = %I | %%j = %j | %%J = %J | %%k = %k | %%l = %l | %%m = %m | %%M = %M | %%N = %N | %%p = %p | %%P = %P | %%Q = %Q | %%s = %s | %%S = %S | %%t = %t | %%u = %u | %%U = %U | %%V = %V | %%w = %w | %%W = %W | %%y = %y | %%Y = %Y | %%z = %z | %%Z = %Z | %%n = %n | %%EE = %EE | %%EC = %EC | %%Ey = %Ey | %%n = %n | %%Od = %Od | %%Oe = %Oe | %%OH = %OH | %%Ok = %Ok | %%OI = %OI | %%Ol = %Ol | %%Om = %Om | %%OM = %OM | %%OS = %OS | %%Ou = %Ou | %%Ow = %Ow | %%Oy = %Oy" -timezone :CET -locale de}
+ }
+}
+
proc test-scan {{reptime 1000}} {
_test_run $reptime {
# Scan : date (in gmt)
@@ -146,7 +214,7 @@ proc test-scan {{reptime 1000}} {
# Scan : century, lookup table month
{clock scan {1970 Jan 2} -format {%C%y %b %d} -locale en -gmt 1}
# Scan : century, lookup table month and day (both entries are first)
- {clock scan {1970 Jan 02} -format {%C%y %b %Od} -locale en -gmt 1}
+ {clock scan {1970 Jan 01} -format {%C%y %b %Od} -locale en -gmt 1}
# Scan : century, lookup table month and day (list scan: entries with position 12 / 31)
{clock scan {2016 Dec 31} -format {%C%y %b %Od} -locale en -gmt 1}
@@ -235,8 +303,9 @@ proc test-other {{reptime 1000}} {
proc test {{reptime 1000}} {
puts ""
+ test-format $reptime
test-scan $reptime
- #test-freescan $reptime
+ test-freescan $reptime
test-other $reptime
puts \n**OK**