diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2017-06-02 08:12:38 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2017-06-02 08:12:38 (GMT) |
commit | f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5 (patch) | |
tree | 1601cdbe0f43c015bfcb743565108c36f488e67b /generic/tclClock.c | |
parent | 1a543aa367940f7b7f4f8c6a8e83f673e2715611 (diff) | |
parent | 3ae95af52ca24414d723b827fc99cc1a2b94f778 (diff) | |
download | tcl-f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5.zip tcl-f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5.tar.gz tcl-f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5.tar.bz2 |
Merge core-8-6-branch. This removes the work currently being done in "sebres-8-6-clock-speedup-cr1" branch, but that will be merged again as soon as the work is done.
All other changes in "trunk" since then (e.g. the INST_STR_CONCAT1 performance improvement, and the removal of SunOS-4) are retained.
Diffstat (limited to 'generic/tclClock.c')
-rw-r--r-- | generic/tclClock.c | 2596 |
1 files changed, 289 insertions, 2307 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c index c38af6b..d44e9dc 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -8,16 +8,12 @@ * Copyright 1991-1995 Karl Lehenbauer and Mark Diekhans. * Copyright (c) 1995 Sun Microsystems, Inc. * Copyright (c) 2004 by Kevin B. Kenny. All rights reserved. - * Copyright (c) 2015 by Sergey G. Brester aka sebres. All rights reserved. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tclStrIdxTree.h" -#include "tclDate.h" -#include "tclCompile.h" /* * Windows has mktime. The configurators do not check. @@ -28,6 +24,21 @@ #endif /* + * Constants + */ + +#define JULIAN_DAY_POSIX_EPOCH 2440588 +#define SECONDS_PER_DAY 86400 +#define JULIAN_SEC_POSIX_EPOCH (((Tcl_WideInt) JULIAN_DAY_POSIX_EPOCH) \ + * SECONDS_PER_DAY) +#define FOUR_CENTURIES 146097 /* days */ +#define JDAY_1_JAN_1_CE_JULIAN 1721424 +#define JDAY_1_JAN_1_CE_GREGORIAN 1721426 +#define ONE_CENTURY_GREGORIAN 36524 /* days */ +#define FOUR_YEARS 1461 /* days */ +#define ONE_YEAR 365 /* days */ + +/* * Table of the days in each month, leap and common years */ @@ -44,13 +55,70 @@ static const int daysInPriorMonths[2][13] = { * Enumeration of the string literals used in [clock] */ -CLOCK_LITERAL_ARRAY(Literals); +typedef enum ClockLiteral { + LIT__NIL, + LIT__DEFAULT_FORMAT, + LIT_BCE, LIT_C, + LIT_CANNOT_USE_GMT_AND_TIMEZONE, + LIT_CE, + LIT_DAYOFMONTH, LIT_DAYOFWEEK, LIT_DAYOFYEAR, + LIT_ERA, LIT_GMT, LIT_GREGORIAN, + LIT_INTEGER_VALUE_TOO_LARGE, + LIT_ISO8601WEEK, LIT_ISO8601YEAR, + LIT_JULIANDAY, LIT_LOCALSECONDS, + LIT_MONTH, + LIT_SECONDS, LIT_TZNAME, LIT_TZOFFSET, + LIT_YEAR, + LIT__END +} ClockLiteral; +static const char *const literals[] = { + "", + "%a %b %d %H:%M:%S %Z %Y", + "BCE", "C", + "cannot use -gmt and -timezone in same call", + "CE", + "dayOfMonth", "dayOfWeek", "dayOfYear", + "era", ":GMT", "gregorian", + "integer value too large to represent", + "iso8601Week", "iso8601Year", + "julianDay", "localSeconds", + "month", + "seconds", "tzName", "tzOffset", + "year" +}; + +/* + * Structure containing the client data for [clock] + */ + +typedef struct ClockClientData { + size_t refCount; /* Number of live references. */ + Tcl_Obj **literals; /* Pool of object literals. */ +} ClockClientData; -/* Msgcat literals for exact match (mcKey) */ -CLOCK_LOCALE_LITERAL_ARRAY(MsgCtLiterals, ""); -/* Msgcat index literals prefixed with _IDX_, used for quick dictionary search */ -CLOCK_LOCALE_LITERAL_ARRAY(MsgCtLitIdxs, "_IDX_"); +/* + * Structure containing the fields used in [clock format] and [clock scan] + */ +typedef struct TclDateFields { + Tcl_WideInt seconds; /* Time expressed in seconds from the Posix + * epoch */ + Tcl_WideInt localSeconds; /* Local time expressed in nominal seconds + * from the Posix epoch */ + int tzOffset; /* Time zone offset in seconds east of + * Greenwich */ + Tcl_Obj *tzName; /* Time zone name */ + int julianDay; /* Julian Day Number in local time zone */ + enum {BCE=1, CE=0} era; /* Era */ + int gregorian; /* Flag == 1 if the date is Gregorian */ + int year; /* Year of the era */ + int dayOfYear; /* Day of the year (1 January == 1) */ + int month; /* Month number */ + int dayOfMonth; /* Day of the month */ + int iso8601Year; /* ISO8601 week-based year */ + int iso8601Week; /* ISO8601 week number */ + int dayOfWeek; /* Day of the week */ +} TclDateFields; static const char *const eras[] = { "CE", "BCE", NULL }; /* @@ -71,23 +139,26 @@ TCL_DECLARE_MUTEX(clockMutex) * Function prototypes for local procedures in this file: */ +static int ConvertUTCToLocal(Tcl_Interp *, + TclDateFields *, Tcl_Obj *, int); static int ConvertUTCToLocalUsingTable(Tcl_Interp *, - TclDateFields *, int, Tcl_Obj *const[], - Tcl_WideInt rangesVal[2]); + TclDateFields *, int, Tcl_Obj *const[]); static int ConvertUTCToLocalUsingC(Tcl_Interp *, TclDateFields *, int); -static int ConvertLocalToUTC(ClientData clientData, Tcl_Interp *, - TclDateFields *, Tcl_Obj *timezoneObj, int); +static int ConvertLocalToUTC(Tcl_Interp *, + TclDateFields *, Tcl_Obj *, int); static int ConvertLocalToUTCUsingTable(Tcl_Interp *, - TclDateFields *, int, Tcl_Obj *const[], - Tcl_WideInt rangesVal[2]); + TclDateFields *, int, Tcl_Obj *const[]); 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 *); static void GetYearWeekDay(TclDateFields *, int); static void GetGregorianEraYearDay(TclDateFields *, int); static void GetMonthDay(TclDateFields *); +static void GetJulianDayFromEraYearWeekDay(TclDateFields *, int); +static void GetJulianDayFromEraYearMonthDay(TclDateFields *, int); +static int IsGregorianLeapYear(TclDateFields *); static int WeekdayOnOrBefore(int, int); static int ClockClicksObjCmd( ClientData clientData, Tcl_Interp *interp, @@ -95,10 +166,6 @@ static int ClockClicksObjCmd( static int ClockConvertlocaltoutcObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); - -static int ClockGetDateFields(ClientData clientData, - Tcl_Interp *interp, TclDateFields *fields, - Tcl_Obj *timezoneObj, int changeover); static int ClockGetdatefieldsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -117,28 +184,13 @@ static int ClockMicrosecondsObjCmd( static int ClockMillisecondsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -static int ClockSecondsObjCmd( - ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int ClockFormatObjCmd( - ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int ClockScanObjCmd( +static int ClockParseformatargsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -static int ClockScanCommit( - ClientData clientData, register DateInfo *info, - register ClockFmtScnCmdArgs *opts); -static int ClockFreeScan( - register DateInfo *info, - Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts); -static int ClockCalcRelTime( - register DateInfo *info, ClockFmtScnCmdArgs *opts); -static int ClockAddObjCmd( +static int ClockSecondsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static struct tm * ThreadSafeLocalTime(const time_t *); -static unsigned long TzsetGetEpoch(void); static void TzsetIfNecessary(void); static void ClockDeleteCmdProc(ClientData); @@ -150,32 +202,22 @@ struct ClockCommand { const char *name; /* The tail of the command name. The full name * is "::tcl::clock::<name>". When NULL marks * the end of the table. */ - Tcl_ObjCmdProc *objCmdProc; /* Function that implements the command. This + Tcl_ObjCmdProc *objCmdProc; /* Function that implements the command. This * will always have the ClockClientData sent * to it, but may well ignore this data. */ - CompileProc *compileProc; /* The compiler for the command. */ - ClientData clientData; /* Any clientData to give the command (if NULL - * a reference to ClockClientData will be sent) */ }; static const struct ClockCommand clockCommands[] = { - {"add", ClockAddObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"clicks", ClockClicksObjCmd, TclCompileClockClicksCmd, NULL}, - {"format", ClockFormatObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"getenv", ClockGetenvObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"microseconds", ClockMicrosecondsObjCmd,TclCompileClockReadingCmd, INT2PTR(1)}, - {"milliseconds", ClockMillisecondsObjCmd,TclCompileClockReadingCmd, INT2PTR(2)}, - {"scan", ClockScanObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"seconds", ClockSecondsObjCmd, TclCompileClockReadingCmd, INT2PTR(3)}, - {"configure", ClockConfigureObjCmd, NULL, NULL}, - {"Oldscan", TclClockOldscanObjCmd, NULL, NULL}, - {"ConvertLocalToUTC", ClockConvertlocaltoutcObjCmd, NULL, NULL}, - {"GetDateFields", ClockGetdatefieldsObjCmd, NULL, NULL}, - {"GetJulianDayFromEraYearMonthDay", - ClockGetjuliandayfromerayearmonthdayObjCmd, NULL, NULL}, - {"GetJulianDayFromEraYearWeekDay", - ClockGetjuliandayfromerayearweekdayObjCmd, NULL, NULL}, - {NULL, NULL, NULL, NULL} + { "getenv", ClockGetenvObjCmd }, + { "Oldscan", TclClockOldscanObjCmd }, + { "ConvertLocalToUTC", ClockConvertlocaltoutcObjCmd }, + { "GetDateFields", ClockGetdatefieldsObjCmd }, + { "GetJulianDayFromEraYearMonthDay", + ClockGetjuliandayfromerayearmonthdayObjCmd }, + { "GetJulianDayFromEraYearWeekDay", + ClockGetjuliandayfromerayearweekdayObjCmd }, + { "ParseFormatArgs", ClockParseformatargsObjCmd }, + { NULL, NULL } }; /* @@ -204,10 +246,22 @@ TclClockInit( char cmdName[50]; /* Buffer large enough to hold the string *::tcl::clock::GetJulianDayFromEraYearMonthDay * plus a terminating NUL. */ - Command *cmdPtr; ClockClientData *data; int i; + /* Structure of the 'clock' ensemble */ + + static const EnsembleImplMap clockImplMap[] = { + {"add", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0}, + {"clicks", ClockClicksObjCmd, TclCompileClockClicksCmd, NULL, NULL, 0}, + {"format", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0}, + {"microseconds", ClockMicrosecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(1), 0}, + {"milliseconds", ClockMillisecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(2), 0}, + {"scan", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL , 0}, + {"seconds", ClockSecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(3), 0}, + {NULL, NULL, NULL, NULL, NULL, 0} + }; + /* * Safe interps get [::clock] as alias to a master, so do not need their * own copies of the support routines. @@ -225,918 +279,27 @@ TclClockInit( data->refCount = 0; data->literals = ckalloc(LIT__END * sizeof(Tcl_Obj*)); for (i = 0; i < LIT__END; ++i) { - Tcl_InitObjRef(data->literals[i], Tcl_NewStringObj(Literals[i], -1)); + data->literals[i] = Tcl_NewStringObj(literals[i], -1); + Tcl_IncrRefCount(data->literals[i]); } - data->mcLiterals = NULL; - data->mcLitIdxs = NULL; - data->LastTZEpoch = 0; - data->currentYearCentury = ClockDefaultYearCentury; - data->yearOfCenturySwitch = ClockDefaultCenturySwitch; - data->SystemTimeZone = NULL; - data->SystemSetupTZData = NULL; - data->GMTSetupTimeZone = NULL; - data->GMTSetupTZData = NULL; - data->AnySetupTimeZone = NULL; - data->AnySetupTZData = NULL; - data->LastUnnormSetupTimeZone = NULL; - data->LastSetupTimeZone = NULL; - data->LastSetupTZData = NULL; - - data->CurrentLocale = NULL; - data->CurrentLocaleDict = NULL; - data->LastUnnormUsedLocale = NULL; - data->LastUsedLocale = NULL; - data->LastUsedLocaleDict = NULL; - - data->lastBase.timezoneObj = NULL; - data->UTC2Local.timezoneObj = NULL; - data->UTC2Local.tzName = NULL; - data->Local2UTC.timezoneObj = NULL; /* * Install the commands. + * TODO - Let Tcl_MakeEnsemble do this? */ #define TCL_CLOCK_PREFIX_LEN 14 /* == strlen("::tcl::clock::") */ memcpy(cmdName, "::tcl::clock::", TCL_CLOCK_PREFIX_LEN); for (clockCmdPtr=clockCommands ; clockCmdPtr->name!=NULL ; clockCmdPtr++) { - ClientData clientData; - strcpy(cmdName + TCL_CLOCK_PREFIX_LEN, clockCmdPtr->name); - if (!(clientData = clockCmdPtr->clientData)) { - clientData = data; - data->refCount++; - } - cmdPtr = (Command *)Tcl_CreateObjCommand(interp, cmdName, - clockCmdPtr->objCmdProc, clientData, - clockCmdPtr->clientData ? NULL : ClockDeleteCmdProc); - cmdPtr->compileProc = clockCmdPtr->compileProc ? - clockCmdPtr->compileProc : TclCompileBasicMin0ArgCmd; - } -} - -/* - *---------------------------------------------------------------------- - * - * ClockConfigureClear -- - * - * Clean up cached resp. run-time storages used in clock commands. - * - * Shared usage for clean-up (ClockDeleteCmdProc) and "configure -clear". - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -ClockConfigureClear( - ClockClientData *data) -{ - ClockFrmScnClearCaches(); - - data->LastTZEpoch = 0; - Tcl_UnsetObjRef(data->SystemTimeZone); - Tcl_UnsetObjRef(data->SystemSetupTZData); - Tcl_UnsetObjRef(data->GMTSetupTimeZone); - Tcl_UnsetObjRef(data->GMTSetupTZData); - Tcl_UnsetObjRef(data->AnySetupTimeZone); - Tcl_UnsetObjRef(data->AnySetupTZData); - Tcl_UnsetObjRef(data->LastUnnormSetupTimeZone); - Tcl_UnsetObjRef(data->LastSetupTimeZone); - Tcl_UnsetObjRef(data->LastSetupTZData); - - Tcl_UnsetObjRef(data->CurrentLocale); - Tcl_UnsetObjRef(data->CurrentLocaleDict); - Tcl_UnsetObjRef(data->LastUnnormUsedLocale); - Tcl_UnsetObjRef(data->LastUsedLocale); - Tcl_UnsetObjRef(data->LastUsedLocaleDict); - - Tcl_UnsetObjRef(data->lastBase.timezoneObj); - Tcl_UnsetObjRef(data->UTC2Local.timezoneObj); - Tcl_UnsetObjRef(data->UTC2Local.tzName); - Tcl_UnsetObjRef(data->Local2UTC.timezoneObj); -} - -/* - *---------------------------------------------------------------------- - * - * ClockDeleteCmdProc -- - * - * Remove a reference to the clock client data, and clean up memory - * when it's all gone. - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ -static void -ClockDeleteCmdProc( - ClientData clientData) /* Opaque pointer to the client data */ -{ - ClockClientData *data = clientData; - int i; - - if (data->refCount-- <= 1) { - for (i = 0; i < LIT__END; ++i) { - Tcl_DecrRefCount(data->literals[i]); - } - if (data->mcLiterals != NULL) { - for (i = 0; i < MCLIT__END; ++i) { - Tcl_DecrRefCount(data->mcLiterals[i]); - } - data->mcLiterals = NULL; - } - if (data->mcLitIdxs != NULL) { - for (i = 0; i < MCLIT__END; ++i) { - Tcl_DecrRefCount(data->mcLitIdxs[i]); - } - data->mcLitIdxs = NULL; - } - - ClockConfigureClear(data); - - ckfree(data->literals); - ckfree(data); - } -} - -/* - *---------------------------------------------------------------------- - * - * NormTimezoneObj -- - * - * Normalizes the timezone object (used for caching puposes). - * - * If already cached time zone could be found, returns this - * object (last setup or last used, system (current) or gmt). - * - * Results: - * Normalized tcl object pointer. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -NormTimezoneObj( - ClockClientData *dataPtr, /* Client data containing literal pool */ - Tcl_Obj *timezoneObj) -{ - const char *tz; - if ( timezoneObj == dataPtr->LastUnnormSetupTimeZone - && dataPtr->LastSetupTimeZone != NULL - ) { - return dataPtr->LastSetupTimeZone; - } - if ( timezoneObj == dataPtr->LastSetupTimeZone - || timezoneObj == dataPtr->literals[LIT_GMT] - || timezoneObj == dataPtr->SystemTimeZone - || timezoneObj == dataPtr->AnySetupTimeZone - ) { - return timezoneObj; - } - - tz = TclGetString(timezoneObj); - if (dataPtr->AnySetupTimeZone != NULL && - (timezoneObj == dataPtr->AnySetupTimeZone - || strcmp(tz, TclGetString(dataPtr->AnySetupTimeZone)) == 0 - ) - ) { - timezoneObj = dataPtr->AnySetupTimeZone; - } - else - if (dataPtr->SystemTimeZone != NULL && - (timezoneObj == dataPtr->SystemTimeZone - || strcmp(tz, TclGetString(dataPtr->SystemTimeZone)) == 0 - ) - ) { - timezoneObj = dataPtr->SystemTimeZone; - } - else - if ( - strcmp(tz, Literals[LIT_GMT]) == 0 - ) { - timezoneObj = dataPtr->literals[LIT_GMT]; - } - return timezoneObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockGetSystemLocale -- - * - * Returns system locale. - * - * Executes ::tcl::clock::GetSystemLocale in given interpreter. - * - * Results: - * Returns system locale tcl object. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -ClockGetSystemLocale( - ClockClientData *dataPtr, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp) /* Tcl interpreter */ -{ - if (Tcl_EvalObjv(interp, 1, &dataPtr->literals[LIT_GETSYSTEMLOCALE], 0) != TCL_OK) { - return NULL; - } - - return Tcl_GetObjResult(interp); -} -/* - *---------------------------------------------------------------------- - * - * ClockGetCurrentLocale -- - * - * Returns current locale. - * - * Executes ::tcl::clock::mclocale in given interpreter. - * - * Results: - * Returns current locale tcl object. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -ClockGetCurrentLocale( - ClockClientData *dataPtr, /* Client data containing literal pool */ - Tcl_Interp *interp) /* Tcl interpreter */ -{ - if (Tcl_EvalObjv(interp, 1, &dataPtr->literals[LIT_GETCURRENTLOCALE], 0) != TCL_OK) { - return NULL; + data->refCount++; + Tcl_CreateObjCommand(interp, cmdName, clockCmdPtr->objCmdProc, data, + ClockDeleteCmdProc); } - Tcl_SetObjRef(dataPtr->CurrentLocale, Tcl_GetObjResult(interp)); - Tcl_UnsetObjRef(dataPtr->CurrentLocaleDict); + /* Make the clock ensemble */ - return dataPtr->CurrentLocale; -} - -/* - *---------------------------------------------------------------------- - * - * NormLocaleObj -- - * - * Normalizes the locale object (used for caching puposes). - * - * If already cached locale could be found, returns this - * object (current, system (OS) or last used locales). - * - * Results: - * Normalized tcl object pointer. - * - *---------------------------------------------------------------------- - */ - -static Tcl_Obj * -NormLocaleObj( - ClockClientData *dataPtr, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *localeObj, - Tcl_Obj **mcDictObj) -{ - const char *loc; - if ( localeObj == NULL || localeObj == dataPtr->CurrentLocale - || localeObj == dataPtr->literals[LIT_C] - || localeObj == dataPtr->literals[LIT_CURRENT] - ) { - if (dataPtr->CurrentLocale == NULL) { - ClockGetCurrentLocale(dataPtr, interp); - } - *mcDictObj = dataPtr->CurrentLocaleDict; - return dataPtr->CurrentLocale; - } - if ( localeObj == dataPtr->LastUsedLocale - || localeObj == dataPtr->LastUnnormUsedLocale - ) { - *mcDictObj = dataPtr->LastUsedLocaleDict; - return dataPtr->LastUsedLocale; - } - - loc = TclGetString(localeObj); - if ( dataPtr->CurrentLocale != NULL - && ( localeObj == dataPtr->CurrentLocale - || (localeObj->length == dataPtr->CurrentLocale->length - && strcmp(loc, TclGetString(dataPtr->CurrentLocale)) == 0 - ) - ) - ) { - *mcDictObj = dataPtr->CurrentLocaleDict; - localeObj = dataPtr->CurrentLocale; - } - else - if ( dataPtr->LastUsedLocale != NULL - && ( localeObj == dataPtr->LastUsedLocale - || (localeObj->length == dataPtr->LastUsedLocale->length - && strcmp(loc, TclGetString(dataPtr->LastUsedLocale)) == 0 - ) - ) - ) { - *mcDictObj = dataPtr->LastUsedLocaleDict; - Tcl_SetObjRef(dataPtr->LastUnnormUsedLocale, localeObj); - localeObj = dataPtr->LastUsedLocale; - } - else - if ( - (localeObj->length == 1 /* C */ - && strncasecmp(loc, Literals[LIT_C], localeObj->length) == 0) - || (localeObj->length == 7 /* current */ - && strncasecmp(loc, Literals[LIT_CURRENT], localeObj->length) == 0) - ) { - if (dataPtr->CurrentLocale == NULL) { - ClockGetCurrentLocale(dataPtr, interp); - } - *mcDictObj = dataPtr->CurrentLocaleDict; - localeObj = dataPtr->CurrentLocale; - } - else - if ( - (localeObj->length == 6 /* system */ - && strncasecmp(loc, Literals[LIT_SYSTEM], localeObj->length) == 0) - ) { - Tcl_SetObjRef(dataPtr->LastUnnormUsedLocale, localeObj); - localeObj = ClockGetSystemLocale(dataPtr, interp); - Tcl_SetObjRef(dataPtr->LastUsedLocale, localeObj); - *mcDictObj = NULL; - } - else - { - *mcDictObj = NULL; - } - return localeObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCDict -- - * - * Retrieves a localized storage dictionary object for the given - * locale object. - * - * This corresponds with call `::tcl::clock::mcget locale`. - * Cached representation stored in options (for further access). - * - * Results: - * Tcl-object contains smart reference to msgcat dictionary. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockMCDict(ClockFmtScnCmdArgs *opts) -{ - ClockClientData *dataPtr = opts->clientData; - - /* if dict not yet retrieved */ - if (opts->mcDictObj == NULL) { - - /* if locale was not yet used */ - if ( !(opts->flags & CLF_LOCALE_USED) ) { - - opts->localeObj = NormLocaleObj(opts->clientData, opts->interp, - opts->localeObj, &opts->mcDictObj); - - if (opts->localeObj == NULL) { - Tcl_SetObjResult(opts->interp, - Tcl_NewStringObj("locale not specified and no default locale set", -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "badOption", NULL); - return NULL; - } - opts->flags |= CLF_LOCALE_USED; - - /* check locale literals already available (on demand creation) */ - if (dataPtr->mcLiterals == NULL) { - int i; - dataPtr->mcLiterals = ckalloc(MCLIT__END * sizeof(Tcl_Obj*)); - for (i = 0; i < MCLIT__END; ++i) { - Tcl_InitObjRef(dataPtr->mcLiterals[i], - Tcl_NewStringObj(MsgCtLiterals[i], -1)); - } - } - } - - if (opts->mcDictObj == NULL) { - Tcl_Obj *callargs[2]; - /* get msgcat dictionary - ::tcl::clock::mcget locale */ - callargs[0] = dataPtr->literals[LIT_MCGET]; - callargs[1] = opts->localeObj; - - if (Tcl_EvalObjv(opts->interp, 2, callargs, 0) != TCL_OK) { - return NULL; - } - - opts->mcDictObj = Tcl_GetObjResult(opts->interp); - /* be sure that object reference not increases (dict changeable) */ - if (opts->mcDictObj->refCount > 0) { - /* smart reference (shared dict as object with no ref-counter) */ - opts->mcDictObj = Tcl_DictObjSmartRef(opts->interp, opts->mcDictObj); - } - if ( opts->localeObj == dataPtr->CurrentLocale ) { - Tcl_SetObjRef(dataPtr->CurrentLocaleDict, opts->mcDictObj); - } else if ( opts->localeObj == dataPtr->LastUsedLocale ) { - Tcl_SetObjRef(dataPtr->LastUsedLocaleDict, opts->mcDictObj); - } else { - Tcl_SetObjRef(dataPtr->LastUsedLocale, opts->localeObj); - Tcl_UnsetObjRef(dataPtr->LastUnnormUsedLocale); - Tcl_SetObjRef(dataPtr->LastUsedLocaleDict, opts->mcDictObj); - } - Tcl_ResetResult(opts->interp); - } - } - - return opts->mcDictObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCGet -- - * - * Retrieves a msgcat value for the given literal integer mcKey - * from localized storage (corresponding given locale object) - * by mcLiterals[mcKey] (e. g. MONTHS_FULL). - * - * Results: - * Tcl-object contains localized value. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockMCGet( - ClockFmtScnCmdArgs *opts, - int mcKey) -{ - ClockClientData *dataPtr = opts->clientData; - - Tcl_Obj *valObj = NULL; - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - return NULL; - } - - Tcl_DictObjGet(opts->interp, opts->mcDictObj, - dataPtr->mcLiterals[mcKey], &valObj); - - return valObj; /* or NULL in obscure case if Tcl_DictObjGet failed */ -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCGetIdx -- - * - * Retrieves an indexed msgcat value for the given literal integer mcKey - * from localized storage (corresponding given locale object) - * by mcLitIdxs[mcKey] (e. g. _IDX_MONTHS_FULL). - * - * Results: - * Tcl-object contains localized indexed value. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockMCGetIdx( - ClockFmtScnCmdArgs *opts, - int mcKey) -{ - ClockClientData *dataPtr = opts->clientData; - - Tcl_Obj *valObj = NULL; - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - return NULL; - } - - /* try to get indices object */ - if (dataPtr->mcLitIdxs == NULL) { - return NULL; - } - - if (Tcl_DictObjGet(NULL, opts->mcDictObj, - dataPtr->mcLitIdxs[mcKey], &valObj) != TCL_OK - ) { - return NULL; - } - - return valObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCSetIdx -- - * - * Sets an indexed msgcat value for the given literal integer mcKey - * in localized storage (corresponding given locale object) - * by mcLitIdxs[mcKey] (e. g. _IDX_MONTHS_FULL). - * - * Results: - * Returns a standard Tcl result. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE int -ClockMCSetIdx( - ClockFmtScnCmdArgs *opts, - int mcKey, Tcl_Obj *valObj) -{ - ClockClientData *dataPtr = opts->clientData; - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - return TCL_ERROR; - } - - /* if literal storage for indices not yet created */ - if (dataPtr->mcLitIdxs == NULL) { - int i; - dataPtr->mcLitIdxs = ckalloc(MCLIT__END * sizeof(Tcl_Obj*)); - for (i = 0; i < MCLIT__END; ++i) { - Tcl_InitObjRef(dataPtr->mcLitIdxs[i], - Tcl_NewStringObj(MsgCtLitIdxs[i], -1)); - } - } - - return Tcl_DictObjPut(opts->interp, opts->mcDictObj, - dataPtr->mcLitIdxs[mcKey], valObj); -} - -/* - *---------------------------------------------------------------------- - * - * ClockConfigureObjCmd -- - * - * This function is invoked to process the Tcl "clock configure" command. - * - * Usage: - * ::tcl::clock::configure ?-option ?value?? - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -ClockConfigureObjCmd( - ClientData clientData, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[]) /* Parameter vector */ -{ - ClockClientData *dataPtr = clientData; - - static const char *const options[] = { - "-system-tz", "-setup-tz", "-default-locale", - "-clear", - "-year-century", "-century-switch", - NULL - }; - enum optionInd { - CLOCK_SYSTEM_TZ, CLOCK_SETUP_TZ, CLOCK_CURRENT_LOCALE, - CLOCK_CLEAR_CACHE, - CLOCK_YEAR_CENTURY, CLOCK_CENTURY_SWITCH, - CLOCK_SETUP_GMT, CLOCK_SETUP_NOP - }; - int optionIndex; /* Index of an option. */ - int i; - - for (i = 1; i < objc; i++) { - if (Tcl_GetIndexFromObj(interp, objv[i++], options, - "option", 0, &optionIndex) != TCL_OK) { - Tcl_SetErrorCode(interp, "CLOCK", "badOption", - Tcl_GetString(objv[i-1]), NULL); - return TCL_ERROR; - } - switch (optionIndex) { - case CLOCK_SYSTEM_TZ: - if (1) { - /* validate current tz-epoch */ - unsigned long lastTZEpoch = TzsetGetEpoch(); - if (i < objc) { - if (dataPtr->SystemTimeZone != objv[i]) { - Tcl_SetObjRef(dataPtr->SystemTimeZone, objv[i]); - Tcl_UnsetObjRef(dataPtr->SystemSetupTZData); - } - dataPtr->LastTZEpoch = lastTZEpoch; - } - if (i+1 >= objc && dataPtr->SystemTimeZone != NULL - && dataPtr->LastTZEpoch == lastTZEpoch) { - Tcl_SetObjResult(interp, dataPtr->SystemTimeZone); - } - } - break; - case CLOCK_SETUP_TZ: - if (i < objc) { - /* differentiate GMT and system zones, because used often */ - Tcl_Obj *timezoneObj = NormTimezoneObj(dataPtr, objv[i]); - Tcl_SetObjRef(dataPtr->LastUnnormSetupTimeZone, objv[i]); - if (dataPtr->LastSetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->LastSetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->LastSetupTZData); - } - if (timezoneObj == dataPtr->literals[LIT_GMT]) { - optionIndex = CLOCK_SETUP_GMT; - } else if (timezoneObj == dataPtr->SystemTimeZone) { - optionIndex = CLOCK_SETUP_NOP; - } - switch (optionIndex) { - case CLOCK_SETUP_GMT: - if (i < objc) { - if (dataPtr->GMTSetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->GMTSetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->GMTSetupTZData); - } - } - break; - case CLOCK_SETUP_TZ: - if (i < objc) { - if (dataPtr->AnySetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->AnySetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->AnySetupTZData); - } - } - break; - } - } - if (i+1 >= objc && dataPtr->LastSetupTimeZone != NULL) { - Tcl_SetObjResult(interp, dataPtr->LastSetupTimeZone); - } - break; - case CLOCK_CURRENT_LOCALE: - if (i < objc) { - if (dataPtr->CurrentLocale != objv[i]) { - Tcl_SetObjRef(dataPtr->CurrentLocale, objv[i]); - Tcl_UnsetObjRef(dataPtr->CurrentLocaleDict); - } - } - if (i+1 >= objc && dataPtr->CurrentLocale != NULL) { - Tcl_SetObjResult(interp, dataPtr->CurrentLocale); - } - break; - case CLOCK_YEAR_CENTURY: - if (i < objc) { - int year; - if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) { - return TCL_ERROR; - } - dataPtr->currentYearCentury = year; - if (i+1 >= objc) { - Tcl_SetObjResult(interp, objv[i]); - } - continue; - } - if (i+1 >= objc) { - Tcl_SetObjResult(interp, - Tcl_NewIntObj(dataPtr->currentYearCentury)); - } - break; - case CLOCK_CENTURY_SWITCH: - if (i < objc) { - int year; - if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) { - return TCL_ERROR; - } - dataPtr->yearOfCenturySwitch = year; - Tcl_SetObjResult(interp, objv[i]); - continue; - } - if (i+1 >= objc) { - Tcl_SetObjResult(interp, - Tcl_NewIntObj(dataPtr->yearOfCenturySwitch)); - } - break; - case CLOCK_CLEAR_CACHE: - ClockConfigureClear(dataPtr); - break; - } - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ClockGetTZData -- - * - * Retrieves tzdata table for given normalized timezone. - * - * Results: - * Returns a tcl object with tzdata. - * - * Side effects: - * The tzdata can be cached in ClockClientData structure. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -ClockGetTZData( - ClientData clientData, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *timezoneObj) /* Name of the timezone */ -{ - ClockClientData *dataPtr = clientData; - Tcl_Obj **literals = dataPtr->literals; - Tcl_Obj *ret, **out = NULL; - - /* if cached (if already setup this one) */ - if ( dataPtr->LastSetupTZData != NULL - && ( timezoneObj == dataPtr->LastSetupTimeZone - || timezoneObj == dataPtr->LastUnnormSetupTimeZone - ) - ) { - return dataPtr->LastSetupTZData; - } - - /* differentiate GMT and system zones, because used often */ - /* simple caching, because almost used the tz-data of last timezone - */ - if (timezoneObj == dataPtr->SystemTimeZone) { - if (dataPtr->SystemSetupTZData != NULL) { - return dataPtr->SystemSetupTZData; - } - out = &dataPtr->SystemSetupTZData; - } - else - if (timezoneObj == dataPtr->GMTSetupTimeZone) { - if (dataPtr->GMTSetupTZData != NULL) { - return dataPtr->GMTSetupTZData; - } - out = &dataPtr->GMTSetupTZData; - } - else - if (timezoneObj == dataPtr->AnySetupTimeZone) { - if (dataPtr->AnySetupTZData != NULL) { - return dataPtr->AnySetupTZData; - } - out = &dataPtr->AnySetupTZData; - } - - ret = Tcl_ObjGetVar2(interp, literals[LIT_TZDATA], - timezoneObj, TCL_LEAVE_ERR_MSG); - - /* cache using corresponding slot and as last used */ - if (out != NULL) { - Tcl_SetObjRef(*out, ret); - } - Tcl_SetObjRef(dataPtr->LastSetupTZData, ret); - if (dataPtr->LastSetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->LastSetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->LastUnnormSetupTimeZone); - } - return ret; -} - -/* - *---------------------------------------------------------------------- - * - * ClockGetSystemTimeZone -- - * - * Returns system (current) timezone. - * - * If system zone not yet cached, it executes ::tcl::clock::GetSystemTimeZone - * in given interpreter and caches its result. - * - * Results: - * Returns normalized timezone object. - * - *---------------------------------------------------------------------- - */ - -static Tcl_Obj * -ClockGetSystemTimeZone( - ClientData clientData, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp) /* Tcl interpreter */ -{ - ClockClientData *dataPtr = clientData; - Tcl_Obj **literals; - - /* if known (cached and same epoch) - return now */ - if (dataPtr->SystemTimeZone != NULL - && dataPtr->LastTZEpoch == TzsetGetEpoch()) { - return dataPtr->SystemTimeZone; - } - - Tcl_UnsetObjRef(dataPtr->SystemTimeZone); - Tcl_UnsetObjRef(dataPtr->SystemSetupTZData); - - literals = dataPtr->literals; - - if (Tcl_EvalObjv(interp, 1, &literals[LIT_GETSYSTEMTIMEZONE], 0) != TCL_OK) { - return NULL; - } - if (dataPtr->SystemTimeZone == NULL) { - Tcl_SetObjRef(dataPtr->SystemTimeZone, Tcl_GetObjResult(interp)); - } - return dataPtr->SystemTimeZone; -} - -/* - *---------------------------------------------------------------------- - * - * ClockSetupTimeZone -- - * - * Sets up the timezone. Loads tzdata, etc. - * - * Results: - * Returns normalized timezone object. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockSetupTimeZone( - ClientData clientData, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *timezoneObj) -{ - ClockClientData *dataPtr = clientData; - Tcl_Obj **literals = dataPtr->literals; - Tcl_Obj *callargs[2]; - - /* if cached (if already setup this one) */ - if ( dataPtr->LastSetupTimeZone != NULL - && ( timezoneObj == dataPtr->LastSetupTimeZone - || timezoneObj == dataPtr->LastUnnormSetupTimeZone - ) - ) { - return dataPtr->LastSetupTimeZone; - } - - /* differentiate GMT and system zones, because used often and already set */ - timezoneObj = NormTimezoneObj(dataPtr, timezoneObj); - if ( timezoneObj == dataPtr->GMTSetupTimeZone - || timezoneObj == dataPtr->SystemTimeZone - || timezoneObj == dataPtr->AnySetupTimeZone - ) { - return timezoneObj; - } - - callargs[0] = literals[LIT_SETUPTIMEZONE]; - callargs[1] = timezoneObj; - - if (Tcl_EvalObjv(interp, 2, callargs, 0) == TCL_OK) { - return dataPtr->LastSetupTimeZone; - } - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * ClockFormatNumericTimeZone -- - * - * Formats a time zone as +hhmmss - * - * Parameters: - * z - Time zone in seconds east of Greenwich - * - * Results: - * Returns the time zone object (formatted in a numeric form) - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -ClockFormatNumericTimeZone(int z) { - char sign = '+'; - int h, m; - if ( z < 0 ) { - z = -z; - sign = '-'; - } - h = z / 3600; - z %= 3600; - m = z / 60; - z %= 60; - if (z != 0) { - return Tcl_ObjPrintf("%c%02d%02d%02d", sign, h, m, z); - } - return Tcl_ObjPrintf("%c%02d%02d", sign, h, m); + TclMakeEnsemble(interp, "clock", clockImplMap); } /* @@ -1148,11 +311,11 @@ ClockFormatNumericTimeZone(int z) { * is available. * * Usage: - * ::tcl::clock::ConvertUTCToLocal dictionary timezone changeover + * ::tcl::clock::ConvertUTCToLocal dictionary tzdata changeover * * Parameters: * dict - Dictionary containing a 'localSeconds' entry. - * timezone - Time zone + * tzdata - Time zone data * changeover - Julian Day of the adoption of the Gregorian calendar. * * Results: @@ -1182,13 +345,12 @@ ClockConvertlocaltoutcObjCmd( int created = 0; int status; - fields.tzName = NULL; /* * Check params and convert time. */ if (objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "dict timezone changeover"); + Tcl_WrongNumArgs(interp, 1, objv, "dict tzdata changeover"); return TCL_ERROR; } dict = objv[1]; @@ -1201,10 +363,10 @@ ClockConvertlocaltoutcObjCmd( "found in dictionary", -1)); return TCL_ERROR; } - if ((TclGetWideIntFromObj(interp, secondsObj, + if ((Tcl_GetWideIntFromObj(interp, secondsObj, &fields.localSeconds) != TCL_OK) || (TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK) - || ConvertLocalToUTC(clientData, interp, &fields, objv[2], changeover)) { + || ConvertLocalToUTC(interp, &fields, objv[2], changeover)) { return TCL_ERROR; } @@ -1238,11 +400,12 @@ ClockConvertlocaltoutcObjCmd( * formatting a date, and populates a dictionary with them. * * Usage: - * ::tcl::clock::GetDateFields seconds timezone changeover + * ::tcl::clock::GetDateFields seconds tzdata changeover * * Parameters: * seconds - Time expressed in seconds from the Posix epoch. - * timezone - Time zone in which time is to be expressed. + * tzdata - Time zone data of the time zone in which time is to be + * expressed. * changeover - Julian Day Number at which the current locale adopted * the Gregorian calendar * @@ -1271,17 +434,15 @@ ClockGetdatefieldsObjCmd( Tcl_Obj *const *literals = data->literals; int changeover; - fields.tzName = NULL; - /* * Check params. */ if (objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "seconds timezone changeover"); + Tcl_WrongNumArgs(interp, 1, objv, "seconds tzdata changeover"); return TCL_ERROR; } - if (TclGetWideIntFromObj(interp, objv[1], &fields.seconds) != TCL_OK + if (Tcl_GetWideIntFromObj(interp, objv[1], &fields.seconds) != TCL_OK || TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK) { return TCL_ERROR; } @@ -1296,14 +457,28 @@ ClockGetdatefieldsObjCmd( return TCL_ERROR; } - /* Extract fields */ + /* + * Convert UTC time to local. + */ - if (ClockGetDateFields(clientData, interp, &fields, objv[2], - changeover) != TCL_OK) { + if (ConvertUTCToLocal(interp, &fields, objv[2], changeover) != TCL_OK) { return TCL_ERROR; } - /* Make dict of fields */ + /* + * Extract Julian day. + */ + + fields.julianDay = (int) ((fields.localSeconds + JULIAN_SEC_POSIX_EPOCH) + / SECONDS_PER_DAY); + + /* + * Convert to Julian or Gregorian calendar. + */ + + GetGregorianEraYearDay(&fields, changeover); + GetMonthDay(&fields); + GetYearWeekDay(&fields, changeover); dict = Tcl_NewDictObj(); Tcl_DictObjPut(NULL, dict, literals[LIT_LOCALSECONDS], @@ -1342,58 +517,6 @@ ClockGetdatefieldsObjCmd( /* *---------------------------------------------------------------------- * - * ClockGetDateFields -- - * - * Converts given UTC time (seconds in a TclDateFields structure) - * to local time and determines the values that clock routines will - * use in scanning or formatting a date. - * - * Results: - * Date-time values are stored in structure "fields". - * Returns a standard Tcl result. - * - *---------------------------------------------------------------------- - */ - -int -ClockGetDateFields( - ClientData clientData, /* Client data of the interpreter */ - Tcl_Interp *interp, /* Tcl interpreter */ - TclDateFields *fields, /* Pointer to result fields, where - * fields->seconds contains date to extract */ - Tcl_Obj *timezoneObj, /* Time zone object or NULL for gmt */ - int changeover) /* Julian Day Number */ -{ - /* - * Convert UTC time to local. - */ - - if (ConvertUTCToLocal(clientData, interp, fields, timezoneObj, - changeover) != TCL_OK) { - return TCL_ERROR; - } - - /* - * Extract Julian day. - */ - - fields->julianDay = (int) ((fields->localSeconds + JULIAN_SEC_POSIX_EPOCH) - / SECONDS_PER_DAY); - - /* - * Convert to Julian or Gregorian calendar. - */ - - GetGregorianEraYearDay(fields, changeover); - GetMonthDay(fields); - GetYearWeekDay(fields, changeover); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * * ClockGetjuliandayfromerayearmonthdayObjCmd -- * * Tcl command that converts a time from era-year-month-day to a Julian @@ -1468,8 +591,6 @@ ClockGetjuliandayfromerayearmonthdayObjCmd( int status; int era = 0; - fields.tzName = NULL; - /* * Check params. */ @@ -1554,8 +675,6 @@ ClockGetjuliandayfromerayearweekdayObjCmd( int status; int era = 0; - fields.tzName = NULL; - /* * Check params. */ @@ -1623,70 +742,18 @@ ClockGetjuliandayfromerayearweekdayObjCmd( static int ConvertLocalToUTC( - ClientData clientData, /* Client data of the interpreter */ Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ - Tcl_Obj *timezoneObj, /* Time zone */ + Tcl_Obj *tzdata, /* Time zone data */ int changeover) /* Julian Day of the Gregorian transition */ { - ClockClientData *dataPtr = clientData; - 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) { - fields->seconds = fields->localSeconds; - fields->tzOffset = 0; - return TCL_OK; - } - - /* - * Check cacheable conversion could be used - * (last-period Local2UTC cache within the same TZ) - */ - seconds = fields->localSeconds - dataPtr->Local2UTC.tzOffset; - if ( timezoneObj == dataPtr->Local2UTC.timezoneObj - && ( fields->localSeconds == dataPtr->Local2UTC.localSeconds - || ( 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 = 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; - } /* * Unpack the tz data. */ - tzdata = ClockGetTZData(clientData, interp, timezoneObj); - if (tzdata == NULL) { - return TCL_ERROR; - } - if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) { return TCL_ERROR; } @@ -1697,26 +764,10 @@ ConvertLocalToUTC( */ if (rowc == 0) { - dataPtr->Local2UTC.rangesVal[0] = 0; - dataPtr->Local2UTC.rangesVal[1] = 0; - - if (ConvertLocalToUTCUsingC(interp, fields, changeover) != TCL_OK) { - return TCL_ERROR; - }; + return ConvertLocalToUTCUsingC(interp, fields, changeover); } else { - if (ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv, - dataPtr->Local2UTC.rangesVal) != TCL_OK) { - return TCL_ERROR; - }; + return ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv); } - - /* Cache the last conversion */ - Tcl_SetObjRef(dataPtr->Local2UTC.timezoneObj, timezoneObj); - dataPtr->Local2UTC.localSeconds = fields->localSeconds; - dataPtr->Local2UTC.changeover = changeover; - dataPtr->Local2UTC.tzOffset = fields->tzOffset; - - return TCL_OK; } /* @@ -1742,8 +793,7 @@ 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_WideInt rangesVal[2]) /* Return bounds for time period */ + Tcl_Obj *const rowv[]) /* Points at which time changes */ { Tcl_Obj *row; int cellc; @@ -1767,8 +817,7 @@ ConvertLocalToUTCUsingTable( fields->tzOffset = 0; fields->seconds = fields->localSeconds; while (!found) { - row = LookupLastTransition(interp, fields->seconds, rowc, rowv, - rangesVal); + row = LookupLastTransition(interp, fields->seconds, rowc, rowv); if ((row == NULL) || TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK @@ -1793,41 +842,6 @@ ConvertLocalToUTCUsingTable( } fields->tzOffset = have[i]; fields->seconds = fields->localSeconds - fields->tzOffset; - -#if 0 - /* currently unused, test purposes only */ - /* - * Convert back from UTC, if local times are different - wrong local time - * (local time seems to be in between DST-hole). - */ - if (fields->tzOffset) { - - int corrOffset; - Tcl_WideInt backCompVal; - /* check DST-hole interval contains UTC time */ - TclGetWideIntFromObj(NULL, cellv[0], &backCompVal); - if ( fields->seconds >= backCompVal - fields->tzOffset - && fields->seconds <= backCompVal + fields->tzOffset - ) { - row = LookupLastTransition(interp, fields->seconds, rowc, rowv); - if (row == NULL || - TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK || - TclGetIntFromObj(interp, cellv[1], &corrOffset) != TCL_OK) { - return TCL_ERROR; - } - if (fields->localSeconds != fields->seconds + corrOffset) { - Tcl_Panic("wrong local time %ld by LocalToUTC conversion," - " local time seems to be in between DST-hole", - fields->localSeconds); - /* correcting offset * / - fields->tzOffset -= corrOffset; - fields->seconds += fields->tzOffset; - */ - } - } - } -#endif - return TCL_OK; } @@ -1929,61 +943,20 @@ ConvertLocalToUTCUsingC( *---------------------------------------------------------------------- */ -MODULE_SCOPE int +static int ConvertUTCToLocal( - ClientData clientData, /* Client data of the interpreter */ Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ - Tcl_Obj *timezoneObj, /* Time zone */ + Tcl_Obj *tzdata, /* Time zone data */ int changeover) /* Julian Day of the Gregorian transition */ { - ClockClientData *dataPtr = clientData; - Tcl_Obj *tzdata; /* Time zone data */ int rowc; /* Number of rows in tzdata */ Tcl_Obj **rowv; /* Pointers to the rows */ - /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */ - if (timezoneObj == dataPtr->GMTSetupTimeZone - && dataPtr->GMTSetupTimeZone != NULL - && dataPtr->GMTSetupTZData != NULL - ) { - fields->localSeconds = fields->seconds; - fields->tzOffset = 0; - if ( TclListObjGetElements(interp, dataPtr->GMTSetupTZData, &rowc, &rowv) != TCL_OK - || Tcl_ListObjIndex(interp, rowv[0], 3, &fields->tzName) != TCL_OK) { - return TCL_ERROR; - } - Tcl_IncrRefCount(fields->tzName); - return TCL_OK; - } - - /* - * Check cacheable conversion could be used - * (last-period UTC2Local cache within the same TZ) - */ - if ( timezoneObj == dataPtr->UTC2Local.timezoneObj - && ( fields->seconds == dataPtr->UTC2Local.seconds - || ( fields->seconds >= dataPtr->UTC2Local.rangesVal[0] - && fields->seconds < dataPtr->UTC2Local.rangesVal[1]) - ) - && changeover == dataPtr->UTC2Local.changeover - ) { - /* the same time zone and offset (UTC time inside the last minute) */ - Tcl_SetObjRef(fields->tzName, dataPtr->UTC2Local.tzName); - fields->tzOffset = dataPtr->UTC2Local.tzOffset; - fields->localSeconds = fields->seconds + fields->tzOffset; - return TCL_OK; - } - /* * Unpack the tz data. */ - tzdata = ClockGetTZData(clientData, interp, timezoneObj); - if (tzdata == NULL) { - return TCL_ERROR; - } - if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) { return TCL_ERROR; } @@ -1994,26 +967,10 @@ ConvertUTCToLocal( */ if (rowc == 0) { - dataPtr->UTC2Local.rangesVal[0] = 0; - dataPtr->UTC2Local.rangesVal[1] = 0; - - if (ConvertUTCToLocalUsingC(interp, fields, changeover) != TCL_OK) { - return TCL_ERROR; - } + return ConvertUTCToLocalUsingC(interp, fields, changeover); } else { - if (ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv, - dataPtr->UTC2Local.rangesVal) != TCL_OK) { - return TCL_ERROR; - } + return ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv); } - - /* Cache the last conversion */ - Tcl_SetObjRef(dataPtr->UTC2Local.timezoneObj, timezoneObj); - dataPtr->UTC2Local.seconds = fields->seconds; - dataPtr->UTC2Local.changeover = changeover; - dataPtr->UTC2Local.tzOffset = fields->tzOffset; - Tcl_SetObjRef(dataPtr->UTC2Local.tzName, fields->tzName); - return TCL_OK; } /* @@ -2040,8 +997,7 @@ 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_WideInt rangesVal[2]) /* Return bounds for time period */ + Tcl_Obj *const rowv[]) /* Rows of the conversion table */ { Tcl_Obj *row; /* Row containing the current information */ int cellc; /* Count of cells in the row (must be 4) */ @@ -2051,7 +1007,7 @@ ConvertUTCToLocalUsingTable( * Look up the nearest transition time. */ - row = LookupLastTransition(interp, fields->seconds, rowc, rowv, rangesVal); + row = LookupLastTransition(interp, fields->seconds, rowc, rowv); if (row == NULL || TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK || TclGetIntFromObj(interp, cellv[1], &fields->tzOffset) != TCL_OK) { @@ -2062,7 +1018,8 @@ ConvertUTCToLocalUsingTable( * Convert the time. */ - Tcl_SetObjRef(fields->tzName, cellv[3]); + fields->tzName = cellv[3]; + Tcl_IncrRefCount(fields->tzName); fields->localSeconds = fields->seconds + fields->tzOffset; return TCL_OK; } @@ -2155,7 +1112,8 @@ ConvertUTCToLocalUsingC( if (diff > 0) { sprintf(buffer+5, "%02d", diff); } - Tcl_SetObjRef(fields->tzName, Tcl_NewStringObj(buffer, -1)); + fields->tzName = Tcl_NewStringObj(buffer, -1); + Tcl_IncrRefCount(fields->tzName); return TCL_OK; } @@ -2173,25 +1131,24 @@ ConvertUTCToLocalUsingC( *---------------------------------------------------------------------- */ -MODULE_SCOPE Tcl_Obj * +static Tcl_Obj * 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_WideInt rangesVal[2]) /* Return bounds for time period */ + Tcl_Obj *const *rowv) /* Rows in tzdata */ { - int l = 0; + int l; int u; Tcl_Obj *compObj; - Tcl_WideInt compVal, fromVal = tick, toVal = tick; + Tcl_WideInt compVal; /* * Examine the first row to make sure we're in bounds. */ if (Tcl_ListObjIndex(interp, rowv[0], 0, &compObj) != TCL_OK - || TclGetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { + || Tcl_GetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { return NULL; } @@ -2201,36 +1158,28 @@ LookupLastTransition( */ if (tick < compVal) { - goto done; + return rowv[0]; } /* * Binary-search to find the transition. */ + l = 0; u = rowc-1; while (l < u) { int m = (l + u + 1) / 2; if (Tcl_ListObjIndex(interp, rowv[m], 0, &compObj) != TCL_OK || - TclGetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { + Tcl_GetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { return NULL; } 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]; } @@ -2261,8 +1210,6 @@ GetYearWeekDay( TclDateFields temp; int dayOfFiscalYear; - temp.tzName = NULL; - /* * Find the given date, minus three days, plus one year. That date's * iso8601 year is an upper bound on the ISO8601 year of the given date. @@ -2469,7 +1416,7 @@ GetMonthDay( *---------------------------------------------------------------------- */ -MODULE_SCOPE void +static void GetJulianDayFromEraYearWeekDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Julian Day Number of the Gregorian @@ -2479,8 +1426,6 @@ GetJulianDayFromEraYearWeekDay( * given year */ TclDateFields firstWeek; - firstWeek.tzName = NULL; - /* * Find January 4 in the ISO8601 year, which will always be in week 1. */ @@ -2522,7 +1467,7 @@ GetJulianDayFromEraYearWeekDay( *---------------------------------------------------------------------- */ -MODULE_SCOPE void +static void GetJulianDayFromEraYearMonthDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Gregorian transition date as a Julian Day */ @@ -2576,9 +1521,9 @@ GetJulianDayFromEraYearMonthDay( * See above bug for details. The casts are necessary. */ if (ym1 >= 0) - ym1o4 = ym1 / 4; + ym1o4 = ym1 / 4; else { - ym1o4 = - (int) (((unsigned int) -ym1) / 4); + ym1o4 = - (int) (((unsigned int) -ym1) / 4); } #endif if (ym1 % 4 < 0) { @@ -2618,61 +1563,6 @@ GetJulianDayFromEraYearMonthDay( /* *---------------------------------------------------------------------- * - * GetJulianDayFromEraYearDay -- - * - * Given era, year, and dayOfYear (in TclDateFields), and the - * Gregorian transition date, computes the Julian Day Number. - * - * Results: - * None. - * - * Side effects: - * Stores day number in 'julianDay' - * - *---------------------------------------------------------------------- - */ - - -MODULE_SCOPE void -GetJulianDayFromEraYearDay( - TclDateFields *fields, /* Date to convert */ - int changeover) /* Gregorian transition date as a Julian Day */ -{ - int year, ym1; - - /* Get absolute year number from the civil year */ - if (fields->era == BCE) { - year = 1 - fields->year; - } else { - year = fields->year; - } - - ym1 = year - 1; - - /* Try the Gregorian calendar first. */ - fields->gregorian = 1; - fields->julianDay = - 1721425 - + fields->dayOfYear - + ( 365 * ym1 ) - + ( ym1 / 4 ) - - ( ym1 / 100 ) - + ( ym1 / 400 ); - - /* If the date is before the Gregorian change, use the Julian calendar. */ - - if ( fields->julianDay < changeover ) { - fields->gregorian = 0; - fields->julianDay = - 1721423 - + fields->dayOfYear - + ( 365 * ym1 ) - + ( ym1 / 4 ); - } -} -/* - *---------------------------------------------------------------------- - * * IsGregorianLeapYear -- * * Tests whether a given year is a leap year, in either Julian or @@ -2684,14 +1574,16 @@ GetJulianDayFromEraYearDay( *---------------------------------------------------------------------- */ -MODULE_SCOPE int +static int IsGregorianLeapYear( TclDateFields *fields) /* Date to test */ { - int year = fields->year; + int year; if (fields->era == BCE) { - year = 1 - year; + year = 1 - fields->year; + } else { + year = fields->year; } if (year%4 != 0) { return 0; @@ -2865,7 +1757,7 @@ ClockClicksObjCmd( } break; default: - Tcl_WrongNumArgs(interp, 0, NULL, "clock clicks ?-switch?"); + Tcl_WrongNumArgs(interp, 1, objv, "?-switch?"); return TCL_ERROR; } @@ -2882,7 +1774,8 @@ ClockClicksObjCmd( #endif break; case CLICKS_MICROS: - clicks = TclpGetMicroseconds(); + Tcl_GetTime(&now); + clicks = ((Tcl_WideInt) now.sec * 1000000) + now.usec; break; } @@ -2918,7 +1811,7 @@ ClockMillisecondsObjCmd( Tcl_Time now; if (objc != 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock milliseconds"); + Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } Tcl_GetTime(&now); @@ -2952,1043 +1845,134 @@ ClockMicrosecondsObjCmd( int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { + Tcl_Time now; + if (objc != 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock microseconds"); + Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetMicroseconds())); + Tcl_GetTime(&now); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj( + ((Tcl_WideInt) now.sec * 1000000) + now.usec)); return TCL_OK; } -static inline void -ClockInitFmtScnArgs( - ClientData clientData, - Tcl_Interp *interp, - ClockFmtScnCmdArgs *opts) -{ - memset(opts, 0, sizeof(*opts)); - opts->clientData = clientData; - opts->interp = interp; -} - /* *----------------------------------------------------------------------------- * - * ClockParseFmtScnArgs -- + * ClockParseformatargsObjCmd -- * - * Parses the arguments for [clock scan] and [clock format]. + * Parses the arguments for [clock format]. * * Results: - * Returns a standard Tcl result, and stores parsed options - * (format, the locale, timezone and base) in structure "opts". + * Returns a standard Tcl result, whose value is a four-element list + * comprising the time format, the locale, and the timezone. + * + * This function exists because the loop that parses the [clock format] + * options is a known performance "hot spot", and is implemented in an effort + * to speed that particular code up. * *----------------------------------------------------------------------------- */ -#define CLC_FMT_ARGS (0) -#define CLC_SCN_ARGS (1 << 0) -#define CLC_ADD_ARGS (1 << 1) - static int -ClockParseFmtScnArgs( - register - ClockFmtScnCmdArgs *opts, /* Result vector: format, locale, timezone... */ - TclDateFields *date, /* Extracted date-time corresponding base - * (by scan or add) resp. clockval (by format) */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[], /* Parameter vector */ - int flags /* Flags, differentiates between format, scan, add */ -) { - Tcl_Interp *interp = opts->interp; - ClockClientData *dataPtr = opts->clientData; +ClockParseformatargsObjCmd( + ClientData clientData, /* Client data containing literal pool */ + Tcl_Interp *interp, /* Tcl interpreter */ + int objc, /* Parameter count */ + Tcl_Obj *const objv[]) /* Parameter vector */ +{ + ClockClientData *dataPtr = clientData; + Tcl_Obj **litPtr = dataPtr->literals; + Tcl_Obj *results[3]; /* Format, locale and timezone */ +#define formatObj results[0] +#define localeObj results[1] +#define timezoneObj results[2] int gmtFlag = 0; - static const char *const options[] = { + static const char *const options[] = { /* Command line options expected */ "-format", "-gmt", "-locale", - "-timezone", "-base", NULL - }; + "-timezone", NULL }; enum optionInd { - CLC_ARGS_FORMAT, CLC_ARGS_GMT, CLC_ARGS_LOCALE, - CLC_ARGS_TIMEZONE, CLC_ARGS_BASE + CLOCK_FORMAT_FORMAT, CLOCK_FORMAT_GMT, CLOCK_FORMAT_LOCALE, + CLOCK_FORMAT_TIMEZONE }; int optionIndex; /* Index of an option. */ int saw = 0; /* Flag == 1 if option was seen already. */ + Tcl_WideInt clockVal; /* Clock value - just used to parse. */ int i; - Tcl_WideInt baseVal; /* Base time, expressed in seconds from the Epoch */ - /* clock value (as current base) */ - if ( !(flags & (CLC_SCN_ARGS)) ) { - opts->baseObj = objv[1]; - saw |= (1 << CLC_ARGS_BASE); + /* + * Args consist of a time followed by keyword-value pairs. + */ + + if (objc < 2 || (objc % 2) != 0) { + Tcl_WrongNumArgs(interp, 0, objv, + "clock format clockval ?-format string? " + "?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?"); + Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); + return TCL_ERROR; } /* * Extract values for the keywords. */ + formatObj = litPtr[LIT__DEFAULT_FORMAT]; + localeObj = litPtr[LIT_C]; + timezoneObj = litPtr[LIT__NIL]; for (i = 2; i < objc; i+=2) { - /* bypass integers (offsets) by "clock add" */ - if (flags & CLC_ADD_ARGS) { - Tcl_WideInt num; - if (TclGetWideIntFromObj(NULL, objv[i], &num) == TCL_OK) { - continue; - } - } - /* get option */ - if (Tcl_GetIndexFromObj(interp, objv[i], options, - "option", 0, &optionIndex) != TCL_OK) { - goto badOption; - } - /* if already specified */ - if (saw & (1 << optionIndex)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": doubly present", - TclGetString(objv[i])) - ); - goto badOption; + if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, + &optionIndex) != TCL_OK) { + Tcl_SetErrorCode(interp, "CLOCK", "badOption", + Tcl_GetString(objv[i]), NULL); + return TCL_ERROR; } switch (optionIndex) { - case CLC_ARGS_FORMAT: - if (flags & CLC_ADD_ARGS) { - goto badOptionMsg; - } - opts->formatObj = objv[i+1]; + case CLOCK_FORMAT_FORMAT: + formatObj = objv[i+1]; break; - case CLC_ARGS_GMT: + case CLOCK_FORMAT_GMT: if (Tcl_GetBooleanFromObj(interp, objv[i+1], &gmtFlag) != TCL_OK){ return TCL_ERROR; } break; - case CLC_ARGS_LOCALE: - opts->localeObj = objv[i+1]; - break; - case CLC_ARGS_TIMEZONE: - opts->timezoneObj = objv[i+1]; + case CLOCK_FORMAT_LOCALE: + localeObj = objv[i+1]; break; - case CLC_ARGS_BASE: - if ( !(flags & (CLC_SCN_ARGS)) ) { - goto badOptionMsg; - } - opts->baseObj = objv[i+1]; + case CLOCK_FORMAT_TIMEZONE: + timezoneObj = objv[i+1]; break; } - saw |= (1 << optionIndex); + saw |= 1 << optionIndex; } /* * Check options. */ - if ((saw & (1 << CLC_ARGS_GMT)) - && (saw & (1 << CLC_ARGS_TIMEZONE))) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot use -gmt and -timezone in same call", -1)); - Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", NULL); - return TCL_ERROR; - } - if (gmtFlag) { - opts->timezoneObj = dataPtr->literals[LIT_GMT]; - } - - /* If time zone not specified use system time zone */ - - if ( opts->timezoneObj == NULL - || TclGetString(opts->timezoneObj) == NULL - || opts->timezoneObj->length == 0 - ) { - opts->timezoneObj = ClockGetSystemTimeZone(opts->clientData, interp); - if (opts->timezoneObj == NULL) { - return TCL_ERROR; - } - } - - /* Setup timezone (normalize object if needed and load TZ on demand) */ - - opts->timezoneObj = ClockSetupTimeZone(opts->clientData, interp, opts->timezoneObj); - if (opts->timezoneObj == NULL) { - return TCL_ERROR; - } - - /* Base (by scan or add) or clock value (by format) */ - - if (opts->baseObj != NULL) { - register Tcl_Obj *baseObj = opts->baseObj; - /* bypass integer recognition if looks like option "-now" */ - if ( - (baseObj->length == 4 && baseObj->bytes && *(baseObj->bytes+1) == 'n') || - TclGetWideIntFromObj(NULL, baseObj, &baseVal) != TCL_OK - ) { - - /* we accept "-now" as current date-time */ - static const char *const nowOpts[] = { - "-now", NULL - }; - int idx; - if (Tcl_GetIndexFromObj(NULL, baseObj, nowOpts, "seconds or -now", - TCL_EXACT, &idx) == TCL_OK - ) { - goto baseNow; - } - - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "expected integer but got \"%s\"", - Tcl_GetString(baseObj))); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); - i = 1; - goto badOption; - } - /* - * seconds could be an unsigned number that overflowed. Make sure - * that it isn't. - */ - - if (baseObj->typePtr == &tclBignumType) { - Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]); - return TCL_ERROR; - } - - } else { - -baseNow: - { - Tcl_Time now; - Tcl_GetTime(&now); - baseVal = (Tcl_WideInt) now.sec; - } - } - - /* - * 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 == baseVal) { - memcpy(date, &dataPtr->lastBase.Date, ClockCacheableDateFieldsSize); - } else { - /* extact fields from base */ - date->seconds = baseVal; - if (ClockGetDateFields(opts->clientData, interp, date, opts->timezoneObj, - GREGORIAN_CHANGE_DATE) != TCL_OK) { /* TODO - GREGORIAN_CHANGE_DATE should be locale-dependent */ - return TCL_ERROR; - } - /* cache last base */ - memcpy(&dataPtr->lastBase.Date, date, ClockCacheableDateFieldsSize); - Tcl_SetObjRef(dataPtr->lastBase.timezoneObj, opts->timezoneObj); - } - - return TCL_OK; - -badOptionMsg: - - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": unexpected for command \"%s\"", - TclGetString(objv[i]), TclGetString(objv[0])) - ); - -badOption: - - Tcl_SetErrorCode(interp, "CLOCK", "badOption", - i < objc ? Tcl_GetString(objv[i]) : NULL, NULL); - - return TCL_ERROR; -} - -/*---------------------------------------------------------------------- - * - * ClockFormatObjCmd -- , clock format -- - * - * This function is invoked to process the Tcl "clock format" command. - * - * Formats a count of seconds since the Posix Epoch as a time of day. - * - * The 'clock format' command formats times of day for output. Refer - * to the user documentation to see what it does. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -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 */ - DateFormat dateFmt; /* Common structure used for formatting */ - - /* even number of arguments */ - if ((objc & 1) == 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock format clockval|-now " - "?-format string? " - "?-gmt boolean? " - "?-locale LOCALE? ?-timezone ZONE?"); - Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); + if (Tcl_GetWideIntFromObj(interp, objv[1], &clockVal) != TCL_OK) { return TCL_ERROR; } - - memset(&dateFmt, 0, sizeof(dateFmt)); - - /* - * Extract values for the keywords. - */ - - ClockInitFmtScnArgs(clientData, interp, &opts); - ret = ClockParseFmtScnArgs(&opts, &dateFmt.date, objc, objv, - CLC_FMT_ARGS); - if (ret != TCL_OK) { - goto done; - } - - /* Default format */ - if (opts.formatObj == NULL) { - opts.formatObj = dataPtr->literals[LIT__DEFAULT_FORMAT]; - } - - /* Use compiled version of Format - */ - - ret = ClockFormat(&dateFmt, &opts); - -done: - - Tcl_UnsetObjRef(dateFmt.date.tzName); - - if (ret != TCL_OK) { - return ret; - } - - return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockScanObjCmd -- , clock scan -- - * - * This function is invoked to process the Tcl "clock scan" command. - * - * Inputs a count of seconds since the Posix Epoch as a time of day. - * - * The 'clock scan' command scans times of day on input. Refer to the - * user documentation to see what it does. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -ClockScanObjCmd( - ClientData clientData, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[]) /* Parameter values */ -{ - int ret; - ClockFmtScnCmdArgs opts; /* Format, locale, timezone and base */ - DateInfo yy; /* Common structure used for parsing */ - DateInfo *info = &yy; - - /* even number of arguments */ - if ((objc & 1) == 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock scan string " - "?-base seconds? " - "?-format string? " - "?-gmt boolean? " - "?-locale LOCALE? ?-timezone ZONE?"); - Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); + if ((saw & (1 << CLOCK_FORMAT_GMT)) + && (saw & (1 << CLOCK_FORMAT_TIMEZONE))) { + Tcl_SetObjResult(interp, litPtr[LIT_CANNOT_USE_GMT_AND_TIMEZONE]); + Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", NULL); return TCL_ERROR; } - - ClockInitDateInfo(&yy); - - /* - * Extract values for the keywords. - */ - - ClockInitFmtScnArgs(clientData, interp, &opts); - ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv, - CLC_SCN_ARGS); - if (ret != TCL_OK) { - goto done; - } - - /* seconds are in localSeconds (relative base date), so reset time here */ - yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; - - /* If free scan */ - if (opts.formatObj == NULL) { - /* Use compiled version of FreeScan - */ - - /* [SB] TODO: Perhaps someday we'll localize the legacy code. Right now, it's not localized. */ - if (opts.localeObj != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("legacy [clock scan] does not support -locale", -1)); - Tcl_SetErrorCode(interp, "CLOCK", "flagWithLegacyFormat", NULL); - return TCL_ERROR; - } - ret = ClockFreeScan(&yy, objv[1], &opts); - } - else { - /* Use compiled version of Scan - */ - - ret = ClockScan(&yy, objv[1], &opts); - } - - /* Convert date info structure into UTC seconds */ - - if (ret == TCL_OK) { - ret = ClockScanCommit(clientData, &yy, &opts); - } - -done: - - Tcl_UnsetObjRef(yy.date.tzName); - - if (ret != TCL_OK) { - return ret; - } - - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(yy.date.seconds)); - return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockScanCommit -- - * - * Converts date info structure into UTC seconds. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -ClockScanCommit( - ClientData clientData, /* Client data containing literal pool */ - register DateInfo *info, /* Clock scan info structure */ - register - ClockFmtScnCmdArgs *opts) /* Format, locale, timezone and base */ -{ - /* If needed assemble julianDay using year, month, etc. */ - if (info->flags & CLF_ASSEMBLE_JULIANDAY) { - if ((info->flags & CLF_ISO8601)) { - GetJulianDayFromEraYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); - } - else - if (!(info->flags & CLF_DAYOFYEAR)) { - GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); - } else { - GetJulianDayFromEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - } - } - - /* some overflow checks, if not extended */ - if (!(opts->flags & CLF_EXTENDED)) { - if (yydate.julianDay > 5373484) { - Tcl_SetObjResult(opts->interp, Tcl_NewStringObj( - "requested date too large to represent", -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", NULL); - return TCL_ERROR; - } - } - - /* Local seconds to UTC (stored in yydate.seconds) */ - - if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY)) { - yydate.localSeconds = - -210866803200L - + ( SECONDS_PER_DAY * (Tcl_WideInt)yydate.julianDay ) - + ( yySeconds % SECONDS_PER_DAY ); - } - - if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY|CLF_LOCALSEC)) { - if (ConvertLocalToUTC(clientData, opts->interp, &yydate, opts->timezoneObj, - GREGORIAN_CHANGE_DATE) != TCL_OK) { - return TCL_ERROR; - } - } - - /* Increment UTC seconds with relative time */ - - yydate.seconds += yyRelSeconds; - - return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockFreeScan -- - * - * Used by ClockScanObjCmd for free scanning without format. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -ClockFreeScan( - register - DateInfo *info, /* Date fields used for parsing & converting - * simultaneously a yy-parse structure of the - * TclClockFreeScan */ - Tcl_Obj *strObj, /* String containing the time to scan */ - ClockFmtScnCmdArgs *opts) /* Command options */ -{ - Tcl_Interp *interp = opts->interp; - ClockClientData *dataPtr = opts->clientData; - - int ret = TCL_ERROR; - - /* - * Parse the date. The parser will fill a structure "info" with date, - * time, time zone, relative month/day/seconds, relative weekday, ordinal - * month. - * Notice that many yy-defines point to values in the "info" or "date" - * structure, e. g. yySeconds -> info->date.secondOfDay or - * yySeconds -> info->date.month (same as yydate.month) - */ - yyInput = Tcl_GetString(strObj); - - if (TclClockFreeScan(interp, info) != TCL_OK) { - Tcl_Obj *msg = Tcl_NewObj(); - Tcl_AppendPrintfToObj(msg, "unable to convert date-time string \"%s\": %s", - Tcl_GetString(strObj), TclGetString(Tcl_GetObjResult(interp))); - Tcl_SetObjResult(interp, msg); - goto done; - } - - /* - * If the caller supplied a date in the string, update the date with - * the value. If the caller didn't specify a time with the date, default to - * midnight. - */ - - if (yyHaveDate) { - if (yyYear < 100) { - if (yyYear >= dataPtr->yearOfCenturySwitch) { - yyYear -= 100; - } - yyYear += dataPtr->currentYearCentury; - } - yydate.era = CE; - if (yyHaveTime == 0) { - yyHaveTime = -1; - } - info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; - } - - /* - * If the caller supplied a time zone in the string, make it into a time - * zone indicator of +-hhmm and setup this time zone. - */ - - if (yyHaveZone) { - Tcl_Obj *tzObjStor = NULL; - int minEast = -yyTimezone; - int dstFlag = 1 - yyDSTmode; - tzObjStor = ClockFormatNumericTimeZone( - 60 * minEast + 3600 * dstFlag); - Tcl_IncrRefCount(tzObjStor); - - opts->timezoneObj = ClockSetupTimeZone(dataPtr, interp, tzObjStor); - - Tcl_DecrRefCount(tzObjStor); - if (opts->timezoneObj == NULL) { - goto done; - } - - // Tcl_SetObjRef(yydate.tzName, opts->timezoneObj); - - info->flags |= CLF_ASSEMBLE_SECONDS; - } - - /* - * Assemble date, time, zone into seconds-from-epoch - */ - - if (yyHaveTime == -1) { - yySeconds = 0; - info->flags |= CLF_ASSEMBLE_SECONDS; - } - else - if (yyHaveTime) { - yySeconds = ToSeconds(yyHour, yyMinutes, - yySeconds, yyMeridian); - info->flags |= CLF_ASSEMBLE_SECONDS; - } - else - if ( (yyHaveDay && !yyHaveDate) - || yyHaveOrdinalMonth - || ( yyHaveRel - && ( yyRelMonth != 0 - || yyRelDay != 0 ) ) - ) { - yySeconds = 0; - info->flags |= CLF_ASSEMBLE_SECONDS; - } - else { - yySeconds = yydate.localSeconds % SECONDS_PER_DAY; - } - - /* - * Do relative times - */ - - ret = ClockCalcRelTime(info, opts); - - /* Free scanning completed - date ready */ - -done: - - return ret; -} - -/*---------------------------------------------------------------------- - * - * ClockCalcRelTime -- - * - * Used for calculating of relative times. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ -int -ClockCalcRelTime( - register - DateInfo *info, /* Date fields used for converting */ - ClockFmtScnCmdArgs *opts) /* Command options */ -{ - /* - * Because some calculations require in-between conversion of the - * julian day, we can repeat this processing multiple times - */ -repeat_rel: - - if (yyHaveRel) { - - /* - * Relative conversion normally possible in UTC time only, because - * of possible wrong local time increment if ignores in-between DST-hole. - * (see test-cases clock-34.53, clock-34.54). - * So increment date in julianDay, but time inside day in UTC (seconds). - */ - - /* add months (or years in months) */ - - if (yyRelMonth != 0) { - int m, h; - - /* if needed extract year, month, etc. again */ - if (info->flags & CLF_ASSEMBLE_DATE) { - GetGregorianEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - GetMonthDay(&yydate); - GetYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_DATE; - } - - /* add the requisite number of months */ - yyMonth += yyRelMonth - 1; - yyYear += yyMonth / 12; - m = yyMonth % 12; - yyMonth = m + 1; - - /* if the day doesn't exist in the current month, repair it */ - h = hath[IsGregorianLeapYear(&yydate)][m]; - if (yyDay > h) { - yyDay = h; - } - - /* on demand (lazy) assemble julianDay using new year, month, etc. */ - info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; - - yyRelMonth = 0; - } - - /* add days (or other parts aligned to days) */ - if (yyRelDay) { - - /* assemble julianDay using new year, month, etc. */ - if (info->flags & CLF_ASSEMBLE_JULIANDAY) { - GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_JULIANDAY; - } - yydate.julianDay += yyRelDay; - - /* julianDay was changed, on demand (lazy) extract year, month, etc. again */ - info->flags |= CLF_ASSEMBLE_DATE|CLF_ASSEMBLE_SECONDS; - - yyRelDay = 0; - } - - /* relative time (seconds), if exceeds current date, do the day conversion and - * leave rest of the increment in yyRelSeconds to add it hereafter in UTC seconds */ - if (yyRelSeconds) { - int newSecs = yySeconds + yyRelSeconds; - - /* if seconds increment outside of current date, increment day */ - if (newSecs / SECONDS_PER_DAY != yySeconds / SECONDS_PER_DAY) { - - yyRelDay += newSecs / SECONDS_PER_DAY; - yySeconds = 0; - yyRelSeconds = newSecs % SECONDS_PER_DAY; - - goto repeat_rel; - } - } - - yyHaveRel = 0; - } - - /* - * Do relative (ordinal) month - */ - - if (yyHaveOrdinalMonth) { - int monthDiff; - - /* if needed extract year, month, etc. again */ - if (info->flags & CLF_ASSEMBLE_DATE) { - GetGregorianEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - GetMonthDay(&yydate); - GetYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_DATE; - } - - if (yyMonthOrdinalIncr > 0) { - monthDiff = yyMonthOrdinal - yyMonth; - if (monthDiff <= 0) { - monthDiff += 12; - } - yyMonthOrdinalIncr--; - } else { - monthDiff = yyMonth - yyMonthOrdinal; - if (monthDiff >= 0) { - monthDiff -= 12; - } - yyMonthOrdinalIncr++; - } - - /* process it further via relative times */ - yyHaveRel++; - yyYear += yyMonthOrdinalIncr; - yyRelMonth += monthDiff; - yyHaveOrdinalMonth = 0; - - info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; - - goto repeat_rel; + if (gmtFlag) { + timezoneObj = litPtr[LIT_GMT]; } /* - * Do relative weekday + * Return options as a list. */ - if (yyHaveDay && !yyHaveDate) { - - /* if needed assemble julianDay now */ - if (info->flags & CLF_ASSEMBLE_JULIANDAY) { - GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_JULIANDAY; - } - - yydate.era = CE; - yydate.julianDay = WeekdayOnOrBefore(yyDayNumber, yydate.julianDay + 6) - + 7 * yyDayOrdinal; - if (yyDayOrdinal > 0) { - yydate.julianDay -= 7; - } - info->flags |= CLF_ASSEMBLE_DATE|CLF_ASSEMBLE_SECONDS; - } - + Tcl_SetObjResult(interp, Tcl_NewListObj(3, results)); return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockWeekdaysOffs -- - * - * Get offset in days for the number of week days corresponding the - * given day of week (skipping Saturdays and Sundays). - * - * - * Results: - * Returns a day increment adjusted the given weekdays - * - *---------------------------------------------------------------------- - */ - -static inline int -ClockWeekdaysOffs( - register int dayOfWeek, - register int offs) -{ - register int weeks, resDayOfWeek; - - /* offset in days */ - weeks = offs / 5; - offs = offs % 5; - /* compiler fix for negative offs - wrap (0, -1) -> (-1, 4) */ - if (offs < 0) { - weeks--; - offs = 5 + offs; - } - offs += 7 * weeks; - - /* resulting day of week */ - { - register int day = (offs % 7); - /* compiler fix for negative offs - wrap (0, -1) -> (-1, 6) */ - if (day < 0) { - day = 7 + day; - } - resDayOfWeek = dayOfWeek + day; - } - - /* adjust if we start from a weekend */ - if (dayOfWeek > 5) { - int adj = 5 - dayOfWeek; - offs += adj; - resDayOfWeek += adj; - } - - /* adjust if we end up on a weekend */ - if (resDayOfWeek > 5) { - offs += 2; - } - - return offs; -} - - - -/*---------------------------------------------------------------------- - * - * ClockAddObjCmd -- , clock add -- - * - * Adds an offset to a given time. - * - * Refer to the user documentation to see what it exactly does. - * - * Syntax: - * clock add clockval ?count unit?... ?-option value? - * - * Parameters: - * clockval -- Starting time value - * count -- Amount of a unit of time to add - * unit -- Unit of time to add, must be one of: - * years year months month weeks week - * days day hours hour minutes minute - * seconds second - * - * Options: - * -gmt BOOLEAN - * Flag synonymous with '-timezone :GMT' - * -timezone ZONE - * Name of the time zone in which calculations are to be done. - * -locale NAME - * Name of the locale in which calculations are to be done. - * Used to determine the Gregorian change date. - * - * Results: - * Returns a standard Tcl result with the given time adjusted - * by the given offset(s) in order. - * - * Notes: - * It is possible that adding a number of months or years will adjust the - * day of the month as well. For instance, the time at one month after - * 31 January is either 28 or 29 February, because February has fewer - * than 31 days. - * - *---------------------------------------------------------------------- - */ - -int -ClockAddObjCmd( - 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 */ - DateInfo yy; /* Common structure used for parsing */ - DateInfo *info = &yy; - - /* add "week" to units also (because otherwise ambiguous) */ - static const char *const units[] = { - "years", "months", "week", "weeks", - "days", "weekdays", - "hours", "minutes", "seconds", - NULL - }; - enum unitInd { - CLC_ADD_YEARS, CLC_ADD_MONTHS, CLC_ADD_WEEK, CLC_ADD_WEEKS, - CLC_ADD_DAYS, CLC_ADD_WEEKDAYS, - CLC_ADD_HOURS, CLC_ADD_MINUTES, CLC_ADD_SECONDS - }; - int unitIndex; /* Index of an option. */ - int i; - Tcl_WideInt offs; - - /* even number of arguments */ - if ((objc & 1) == 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock add clockval|-now ?number units?..." - "?-gmt boolean? " - "?-locale LOCALE? ?-timezone ZONE?"); - Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); - return TCL_ERROR; - } - - ClockInitDateInfo(&yy); - - /* - * Extract values for the keywords. - */ - - ClockInitFmtScnArgs(clientData, interp, &opts); - ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv, - CLC_ADD_ARGS); - if (ret != TCL_OK) { - goto done; - } - - /* time together as seconds of the day */ - yySeconds = yydate.localSeconds % SECONDS_PER_DAY; - /* seconds are in localSeconds (relative base date), so reset time here */ - yyHour = 0; yyMinutes = 0; yyMeridian = MER24; - - ret = TCL_ERROR; - - /* - * Find each offset and process date increment - */ - - for (i = 2; i < objc; i+=2) { - /* bypass not integers (options, allready processed above) */ - if (TclGetWideIntFromObj(NULL, objv[i], &offs) != TCL_OK) { - continue; - } - if (objv[i]->typePtr == &tclBignumType) { - Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]); - goto done; - } - /* get unit */ - if (Tcl_GetIndexFromObj(interp, objv[i+1], units, "unit", 0, - &unitIndex) != TCL_OK) { - goto done; - } - - /* nothing to do if zero quantity */ - if (!offs) { - continue; - } - - /* if in-between conversion needed (already have relative date/time), - * correct date info, because the date may be changed, - * so refresh it now */ - - if ( yyHaveRel - && ( unitIndex == CLC_ADD_WEEKDAYS - /* some months can be shorter as another */ - || yyRelMonth || yyRelDay - /* day changed */ - || yySeconds + yyRelSeconds > SECONDS_PER_DAY - || yySeconds + yyRelSeconds < 0 - ) - ) { - if (ClockCalcRelTime(info, &opts) != TCL_OK) { - goto done; - } - } - - /* process increment by offset + unit */ - yyHaveRel++; - switch (unitIndex) { - case CLC_ADD_YEARS: - yyRelMonth += offs * 12; - break; - case CLC_ADD_MONTHS: - yyRelMonth += offs; - break; - case CLC_ADD_WEEK: - case CLC_ADD_WEEKS: - yyRelDay += offs * 7; - break; - case CLC_ADD_DAYS: - yyRelDay += offs; - break; - case CLC_ADD_WEEKDAYS: - /* add number of week days (skipping Saturdays and Sundays) - * to a relative days value. */ - offs = ClockWeekdaysOffs(yy.date.dayOfWeek, offs); - yyRelDay += offs; - break; - case CLC_ADD_HOURS: - yyRelSeconds += offs * 60 * 60; - break; - case CLC_ADD_MINUTES: - yyRelSeconds += offs * 60; - break; - case CLC_ADD_SECONDS: - yyRelSeconds += offs; - break; - } - } - - /* - * Do relative times (if not yet already processed interim): - */ - - if (yyHaveRel) { - if (ClockCalcRelTime(info, &opts) != TCL_OK) { - goto done; - } - } - - /* Convert date info structure into UTC seconds */ - - ret = ClockScanCommit(clientData, &yy, &opts); - -done: - - Tcl_UnsetObjRef(yy.date.tzName); - - if (ret != TCL_OK) { - return ret; - } - - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(yy.date.seconds)); - return TCL_OK; +#undef timezoneObj +#undef localeObj +#undef formatObj } /*---------------------------------------------------------------------- @@ -4019,7 +2003,7 @@ ClockSecondsObjCmd( Tcl_Time now; if (objc != 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock seconds"); + Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } Tcl_GetTime(&now); @@ -4030,7 +2014,7 @@ ClockSecondsObjCmd( /* *---------------------------------------------------------------------- * - * TzsetGetEpoch --, TzsetIfNecessary -- + * TzsetIfNecessary -- * * Calls the tzset() library function if the contents of the TZ * environment variable has changed. @@ -4044,37 +2028,15 @@ ClockSecondsObjCmd( *---------------------------------------------------------------------- */ -static unsigned long -TzsetGetEpoch(void) +static void +TzsetIfNecessary(void) { - static char* tzWas = INT2PTR(-1); /* Previous value of TZ, protected by - * clockMutex. */ - static long tzLastRefresh = 0; /* Used for latency before next refresh */ - static size_t tzWasEpoch = 0; /* Epoch, signals that TZ changed */ - static size_t tzEnvEpoch = 0; /* Last env epoch, for faster signaling, - that TZ changed via TCL */ - - const char *tzIsNow; /* Current value of TZ */ + static char* tzWas = INT2PTR(-1); /* Previous value of TZ, protected by + * clockMutex. */ + const char *tzIsNow; /* Current value of TZ */ - /* - * Prevent performance regression on some platforms by resolving of system time zone: - * small latency for check whether environment was changed (once per second) - * no latency if environment was chaned with tcl-env (compare both epoch values) - */ - Tcl_Time now; - Tcl_GetTime(&now); - if (now.sec == tzLastRefresh && tzEnvEpoch == TclEnvEpoch) { - return tzWasEpoch; - } - tzEnvEpoch = TclEnvEpoch; - tzLastRefresh = now.sec; - - /* check in lock */ Tcl_MutexLock(&clockMutex); - tzIsNow = getenv("TCL_TZ"); - if (tzIsNow == NULL) { - tzIsNow = getenv("TZ"); - } + tzIsNow = getenv("TZ"); if (tzIsNow != NULL && (tzWas == NULL || tzWas == INT2PTR(-1) || strcmp(tzIsNow, tzWas) != 0)) { tzset(); @@ -4083,22 +2045,42 @@ TzsetGetEpoch(void) } tzWas = ckalloc(strlen(tzIsNow) + 1); strcpy(tzWas, tzIsNow); - tzWasEpoch++; } else if (tzIsNow == NULL && tzWas != NULL) { tzset(); if (tzWas != INT2PTR(-1)) ckfree(tzWas); tzWas = NULL; - tzWasEpoch++; } Tcl_MutexUnlock(&clockMutex); - - return tzWasEpoch; } + +/* + *---------------------------------------------------------------------- + * + * ClockDeleteCmdProc -- + * + * Remove a reference to the clock client data, and clean up memory + * when it's all gone. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ static void -TzsetIfNecessary(void) +ClockDeleteCmdProc( + ClientData clientData) /* Opaque pointer to the client data */ { - TzsetGetEpoch(); + ClockClientData *data = clientData; + int i; + + if (data->refCount-- <= 1) { + for (i = 0; i < LIT__END; ++i) { + Tcl_DecrRefCount(data->literals[i]); + } + ckfree(data->literals); + ckfree(data); + } } /* |