diff options
-rw-r--r-- | generic/tclClock.c | 30 | ||||
-rw-r--r-- | generic/tclClockFmt.c | 511 | ||||
-rw-r--r-- | generic/tclDate.h | 9 | ||||
-rw-r--r-- | tests-perf/clock.perf.tcl | 75 |
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** |