summaryrefslogtreecommitdiffstats
path: root/generic/tclClock.c
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-01-10 21:58:33 (GMT)
committersebres <sebres@users.sourceforge.net>2017-01-10 21:58:33 (GMT)
commit58f947e48be58a2371a0d24f957244796306d2ba (patch)
tree3d33434ea4d17840e060eb16d05f43717ce157a0 /generic/tclClock.c
parent4cc1178952c2e7eef686282007f5046adceb1ec1 (diff)
downloadtcl-58f947e48be58a2371a0d24f957244796306d2ba.zip
tcl-58f947e48be58a2371a0d24f957244796306d2ba.tar.gz
tcl-58f947e48be58a2371a0d24f957244796306d2ba.tar.bz2
[temp-commit]: clock scan with several optimization porting, still not-ready
Diffstat (limited to 'generic/tclClock.c')
-rw-r--r--generic/tclClock.c520
1 files changed, 485 insertions, 35 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index e9a59f3..24ed095 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -14,6 +14,7 @@
*/
#include "tclInt.h"
+#include "tclDate.h"
/*
* Windows has mktime. The configurators do not check.
@@ -28,6 +29,7 @@
*/
#define JULIAN_DAY_POSIX_EPOCH 2440588
+#define GREGORIAN_CHANGE_DATE 2361222
#define SECONDS_PER_DAY 86400
#define JULIAN_SEC_POSIX_EPOCH (((Tcl_WideInt) JULIAN_DAY_POSIX_EPOCH) \
* SECONDS_PER_DAY)
@@ -69,7 +71,14 @@ typedef enum ClockLiteral {
LIT_MONTH,
LIT_SECONDS, LIT_TZNAME, LIT_TZOFFSET,
LIT_YEAR,
+ LIT_CURRENTYEARCENTURY,
+ LIT_YEAROFCENTURYSWITCH,
+ LIT_TZDATA,
+ LIT_GETSYSTEMTIMEZONE,
+ LIT_SETUPTIMEZONE,
+#if 0
LIT_FREESCAN,
+#endif
LIT__END
} ClockLiteral;
static const char *const literals[] = {
@@ -86,9 +95,18 @@ static const char *const literals[] = {
"month",
"seconds", "tzName", "tzOffset",
"year",
+ "::tcl::clock::CurrentYearCentury",
+ "::tcl::clock::YearOfCenturySwitch",
+ "::tcl::clock::TZData",
+ "::tcl::clock::GetSystemTimeZone",
+ "::tcl::clock::SetupTimeZone",
+#if 0
"::tcl::clock::FreeScan"
+#endif
};
+#define CurrentYearCentury 2000
+
/*
* Structure containing the client data for [clock]
*/
@@ -168,6 +186,10 @@ static int ClockClicksObjCmd(
static int ClockConvertlocaltoutcObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
+
+static int ClockGetDateFields(Tcl_Interp *interp,
+ TclDateFields *fields, Tcl_Obj *tzdata,
+ int changeover);
static int ClockGetdatefieldsObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
@@ -195,6 +217,10 @@ static int ClockSecondsObjCmd(
static int ClockScanObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
+static int ClockFreeScan(
+ ClientData clientData, Tcl_Interp *interp,
+ Tcl_Obj *strObj, Tcl_WideInt baseVal,
+ Tcl_Obj *timezoneObj, Tcl_Obj *locale);
static struct tm * ThreadSafeLocalTime(const time_t *);
static void TzsetIfNecessary(void);
static void ClockDeleteCmdProc(ClientData);
@@ -296,6 +322,123 @@ TclClockInit(
/*
*----------------------------------------------------------------------
+ */
+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;
+
+ return Tcl_ObjGetVar2(interp, literals[LIT_TZDATA],
+ timezoneObj, TCL_LEAVE_ERR_MSG);
+}
+/*
+ *----------------------------------------------------------------------
+ */
+static Tcl_Obj *
+ClockGetSystemTimeZone(
+ ClientData clientData, /* Opaque pointer to literal pool, etc. */
+ Tcl_Interp *interp) /* Tcl interpreter */
+{
+ ClockClientData *dataPtr = clientData;
+ Tcl_Obj **literals = dataPtr->literals;
+
+ if (Tcl_EvalObjv(interp, 1, &literals[LIT_GETSYSTEMTIMEZONE], 0) != TCL_OK) {
+ return NULL;
+ }
+ return Tcl_GetObjResult(interp);
+}
+/*
+ *----------------------------------------------------------------------
+ */
+static int
+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];
+
+ callargs[0] = literals[LIT_SETUPTIMEZONE];
+ callargs[1] = timezoneObj;
+ return Tcl_EvalObjv(interp, 2, callargs, 0);
+}
+/*
+ *----------------------------------------------------------------------
+ * 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);
+}
+
+/*
+ *----------------------------------------------------------------------
+ * [SB] TODO: make constans cacheable (once per second, etc.) ...
+ */
+inline int
+ClockCurrentYearCentury(
+ ClientData clientData, /* Opaque pointer to literal pool, etc. */
+ Tcl_Interp *interp) /* Tcl interpreter */
+{
+ ClockClientData *dataPtr = clientData;
+ Tcl_Obj **literals = dataPtr->literals;
+ int year = 2000;
+
+ Tcl_Obj * yearObj = Tcl_ObjGetVar2(interp,
+ literals[LIT_CURRENTYEARCENTURY], NULL, TCL_LEAVE_ERR_MSG);
+ Tcl_GetIntFromObj(NULL, yearObj, &year);
+ return year;
+}
+inline int
+ClockGetYearOfCenturySwitch(
+ ClientData clientData, /* Opaque pointer to literal pool, etc. */
+ Tcl_Interp *interp) /* Tcl interpreter */
+{
+ ClockClientData *dataPtr = clientData;
+ Tcl_Obj **literals = dataPtr->literals;
+ int year = 37;
+
+ Tcl_Obj * yearObj = Tcl_ObjGetVar2(interp,
+ literals[LIT_YEAROFCENTURYSWITCH], NULL, TCL_LEAVE_ERR_MSG);
+ Tcl_GetIntFromObj(NULL, yearObj, &year);
+ return year;
+}
+
+/*
+ *----------------------------------------------------------------------
*
* ClockConvertlocaltoutcObjCmd --
*
@@ -426,6 +569,8 @@ ClockGetdatefieldsObjCmd(
Tcl_Obj *const *literals = data->literals;
int changeover;
+ fields.tzName = NULL;
+
/*
* Check params.
*/
@@ -449,31 +594,14 @@ ClockGetdatefieldsObjCmd(
return TCL_ERROR;
}
- /*
- * Convert UTC time to local.
- */
+ /* Extract fields */
- if (ConvertUTCToLocal(interp, &fields, objv[2], changeover) != TCL_OK) {
+ if (ClockGetDateFields(interp, &fields, objv[2], 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);
-
-
-/************* split to use structured version from here ************/
+ /* Make dict of fields */
dict = Tcl_NewDictObj();
Tcl_DictObjPut(NULL, dict, literals[LIT_LOCALSECONDS],
@@ -511,6 +639,59 @@ ClockGetdatefieldsObjCmd(
/*
*----------------------------------------------------------------------
+ */
+int
+ClockGetDateFields(
+ Tcl_Interp *interp, /* Tcl interpreter */
+ TclDateFields *fields, /* Pointer to result fields, where
+ * fields->seconds contains date to extract */
+ Tcl_Obj *tzdata, /* Time zone data object or NULL for gmt */
+ int changeover) /* Julian Day Number */
+{
+ /*
+ * Convert UTC time to local.
+ */
+
+ if (ConvertUTCToLocal(interp, fields, tzdata, 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;
+}
+
+inline
+SetDateFieldsTimeZone(
+ TclDateFields *fields,
+ Tcl_Obj *timezoneObj)
+{
+ if (fields->tzName != timezoneObj) {
+ if (timezoneObj) {
+ Tcl_IncrRefCount(timezoneObj);
+ }
+ if (fields->tzName != NULL) {
+ Tcl_DecrRefCount(fields->tzName);
+ }
+ fields->tzName = timezoneObj;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
*
* ClockGetjuliandayfromerayearmonthdayObjCmd --
*
@@ -1013,8 +1194,7 @@ ConvertUTCToLocalUsingTable(
* Convert the time.
*/
- fields->tzName = cellv[3];
- Tcl_IncrRefCount(fields->tzName);
+ SetDateFieldsTimeZone(fields, cellv[3]);
fields->localSeconds = fields->seconds + fields->tzOffset;
return TCL_OK;
}
@@ -1107,8 +1287,7 @@ ConvertUTCToLocalUsingC(
if (diff > 0) {
sprintf(buffer+5, "%02d", diff);
}
- fields->tzName = Tcl_NewStringObj(buffer, -1);
- Tcl_IncrRefCount(fields->tzName);
+ SetDateFieldsTimeZone(fields, Tcl_NewStringObj(buffer, -1));
return TCL_OK;
}
@@ -1968,7 +2147,7 @@ ClockParseformatargsObjCmd(
Tcl_Obj *const objv[]) /* Parameter vector */
{
ClockClientData *dataPtr = clientData;
- Tcl_Obj **litPtr = dataPtr->literals;
+ Tcl_Obj **literals = dataPtr->literals;
_ClockFmtScnArgs resOpts; /* Format, locale and timezone */
Tcl_WideInt clockVal; /* Clock value - just used to parse. */
int ret;
@@ -2003,11 +2182,11 @@ ClockParseformatargsObjCmd(
return TCL_ERROR;
}
if (resOpts.formatObj == NULL)
- resOpts.formatObj = litPtr[LIT__DEFAULT_FORMAT];
+ resOpts.formatObj = literals[LIT__DEFAULT_FORMAT];
if (resOpts.localeObj == NULL)
- resOpts.localeObj = litPtr[LIT_C];
+ resOpts.localeObj = literals[LIT_C];
if (resOpts.timezoneObj == NULL)
- resOpts.timezoneObj = litPtr[LIT__NIL];
+ resOpts.timezoneObj = literals[LIT__NIL];
/*
* Return options as a list.
@@ -2032,7 +2211,7 @@ ClockScanObjCmd(
Tcl_Obj *const objv[]) /* Parameter values */
{
ClockClientData *dataPtr = clientData;
- Tcl_Obj **litPtr = dataPtr->literals;
+ Tcl_Obj **literals = dataPtr->literals;
Tcl_Time retClock;
char *string, *format = NULL;
@@ -2071,8 +2250,10 @@ ClockScanObjCmd(
baseVal = (Tcl_WideInt) now.sec;
}
- /* if free scan */
+ /* If free scan */
if (opts.formatObj == NULL) {
+#if 0
+ /* Tcled FreeScan proc - */
Tcl_Obj *callargs[5];
/* [SB] TODO: Perhaps someday we'll localize the legacy code. Right now, it's not localized. */
if (opts.localeObj != NULL) {
@@ -2081,18 +2262,287 @@ ClockScanObjCmd(
Tcl_SetErrorCode(interp, "CLOCK", "flagWithLegacyFormat", NULL);
return TCL_ERROR;
}
- callargs[0] = litPtr[LIT_FREESCAN];
+ callargs[0] = literals[LIT_FREESCAN];
callargs[1] = objv[1];
callargs[2] = opts.baseObj != NULL ? opts.baseObj : Tcl_NewWideIntObj(baseVal);
- callargs[3] = opts.timezoneObj != NULL ? opts.timezoneObj : litPtr[LIT__NIL];
- callargs[4] = opts.localeObj != NULL ? opts.localeObj : litPtr[LIT_C];
+ callargs[3] = opts.timezoneObj != NULL ? opts.timezoneObj : literals[LIT__NIL];
+ callargs[4] = opts.localeObj != NULL ? opts.localeObj : literals[LIT_C];
return Tcl_EvalObjv(interp, 5, callargs, 0);
+#else
+ /* 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_SetResult(interp,
+ "legacy [clock scan] does not support -locale", TCL_STATIC);
+ Tcl_SetErrorCode(interp, "CLOCK", "flagWithLegacyFormat", NULL);
+ return TCL_ERROR;
+ }
+ return ClockFreeScan(clientData, interp, objv[1], baseVal,
+ opts.timezoneObj, opts.localeObj);
+#endif
}
+
// ****
string = TclGetString(objv[1]);
- // **** timezone = GetSystemTimeZone()
+ // **** timezone = ClockGetSystemTimeZone(clientData, interp)
+ /*
+ if (timezoneObj == NULL) {
+ goto done;
+ }
+ */
+
+ return TCL_OK;
+}
+
+/*----------------------------------------------------------------------
+ */
+int
+ClockFreeScan(
+ ClientData clientData, /* Client data containing literal pool */
+ Tcl_Interp *interp, /* Tcl interpreter */
+ Tcl_Obj *strObj, /* String containing the time to scan */
+ Tcl_WideInt baseVal, /* Base time, expressed in seconds from the Epoch */
+ Tcl_Obj *timezoneObj, /* Default time zone in which the time will be expressed */
+ Tcl_Obj *locale) /* (Unused) Name of the locale where the time will be scanned. */
+{
+ ClockClientData *dataPtr = clientData;
+ Tcl_Obj **literals = dataPtr->literals;
+
+ DateInfo yy; /* parse structure of TclClockFreeScan */
+ TclDateFields date; /* date fields used for converting from seconds */
+ Tcl_Obj *tzdata;
+ int secondOfDay; /* Seconds of day (time only calculation) */
+ Tcl_WideInt seconds;
+ int ret = TCL_ERROR;
+ Tcl_Obj *cleanUpList = Tcl_NewObj();
+
+ date.tzName = NULL;
+
+ /* If time zone not specified use system time zone */
+ if (timezoneObj == NULL ||
+ TclGetString(timezoneObj) == NULL || timezoneObj->length == 0) {
+ timezoneObj = ClockGetSystemTimeZone(clientData, interp);
+ if (timezoneObj == NULL) {
+ goto done;
+ }
+ Tcl_ListObjAppendElement(NULL, cleanUpList, timezoneObj);
+ }
+
+ /* Get the data for time changes in the given zone */
+
+ if (ClockSetupTimeZone(clientData, interp, timezoneObj) != TCL_OK) {
+ goto done;
+ }
+
+ /*
+ * Extract year, month and day from the base time for the parser to use as
+ * defaults
+ */
+
+ tzdata = ClockGetTZData(clientData, interp, timezoneObj);
+ if (tzdata == NULL) {
+ goto done;
+ }
+ date.seconds = baseVal;
+ if (ClockGetDateFields(interp, &date, tzdata, GREGORIAN_CHANGE_DATE)
+ != TCL_OK) {
+ goto done;
+ }
+
+ secondOfDay = date.localSeconds % 86400;
+
+ /*
+ * Parse the date. The parser will fill a structure "yy" with date, time,
+ * time zone, relative month/day/seconds, relative weekday, ordinal month.
+ */
+ yy.dateInput = Tcl_GetString(strObj);
+
+ yy.dateYear = date.year;
+ yy.dateMonth = date.month;
+ yy.dateDay = date.dayOfMonth;
+
+ if (TclClockFreeScan(interp, &yy) != TCL_OK) {
+ Tcl_Obj *msg = Tcl_NewObj();
+ Tcl_AppendPrintfToObj(msg, "unable to convert date-time string \"",
+ yy.dateInput, "\":",
+ 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 (yy.dateHaveDate) {
+ if (yy.dateYear < 100) {
+ yy.dateYear += ClockCurrentYearCentury(clientData, interp);
+ if (yy.dateYear > ClockGetYearOfCenturySwitch(clientData, interp)) {
+ yy.dateYear -= 100;
+ }
+ }
+ date.era = CE;
+ date.year = yy.dateYear;
+ date.month = yy.dateMonth;
+ date.dayOfMonth = yy.dateDay;
+ if (yy.dateHaveTime == 0) {
+ yy.dateHaveTime = -1;
+ }
+ }
+
+ /*
+ * 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 (yy.dateHaveZone) {
+ int minEast = -yy.dateTimezone;
+ int dstFlag = 1 - yy.dateDSTmode;
+ timezoneObj = ClockFormatNumericTimeZone(
+ 60 * minEast + 3600 * dstFlag);
+ Tcl_ListObjAppendElement(NULL, cleanUpList, timezoneObj);
+ if (ClockSetupTimeZone(clientData, interp, timezoneObj) != TCL_OK) {
+ goto done;
+ }
+ tzdata = ClockGetTZData(clientData, interp, timezoneObj);
+ if (tzdata == NULL) {
+ goto done;
+ }
+ }
+
+ SetDateFieldsTimeZone(&date, timezoneObj);
+
+ /* Assemble date, time, zone into seconds-from-epoch */
+
+ GetJulianDayFromEraYearMonthDay(&date, GREGORIAN_CHANGE_DATE);
+
+ if (yy.dateHaveTime == -1) {
+ secondOfDay = 0;
+ }
+ else
+ if (yy.dateHaveTime) {
+ secondOfDay = ToSeconds(yy.dateHour, yy.dateMinutes,
+ yy.dateSeconds, yy.dateMeridian);
+ }
+ else
+ if ( (yy.dateHaveDay && !yy.dateHaveDate)
+ || yy.dateHaveOrdinalMonth
+ || ( yy.dateHaveRel
+ && ( yy.dateRelMonth != 0
+ || yy.dateRelDay != 0 ) )
+ ) {
+ secondOfDay = 0;
+ }
+
+ date.localSeconds =
+ -210866803200L
+ + ( 86400 * (Tcl_WideInt)date.julianDay )
+ + secondOfDay;
+
+ SetDateFieldsTimeZone(&date, timezoneObj);
+ if (ConvertLocalToUTC(interp, &date, tzdata, GREGORIAN_CHANGE_DATE)
+ != TCL_OK) {
+ goto done;
+ }
+
+ seconds = date.seconds;
+
+ /*
+ * Do relative times
+ */
+
+ if (yy.dateHaveRel) {
+
+ /* [SB] TODO: rewrite it in C: * /
+ seconds = [add $seconds \
+ yy.dateRelMonth months yy.dateRelDay days yy.dateRelMonthSecond seconds \
+ -timezone $timezone -locale $locale]
+ */
+ }
+
+ /*
+ * Do relative weekday
+ */
+
+ if (yy.dateHaveDay && !yy.dateHaveDate) {
+ TclDateFields date2;
+ date2.tzName = NULL;
+
+ SetDateFieldsTimeZone(&date2, timezoneObj);
+
+ date2.seconds = date.seconds;
+ if (ClockGetDateFields(interp, &date2, tzdata, GREGORIAN_CHANGE_DATE)
+ != TCL_OK) {
+ SetDateFieldsTimeZone(&date2, NULL);
+ goto done;
+ }
+ date2.era = CE;
+ date2.julianDay = WeekdayOnOrBefore(yy.dateDayNumber, date2.julianDay + 6)
+ + 7 * yy.dateDayOrdinal;
+ if (yy.dateDayOrdinal > 0) {
+ date2.julianDay -= 7;
+ }
+ secondOfDay = date2.localSeconds % 86400;
+ date2.localSeconds =
+ -210866803200
+ + ( 86400 * (Tcl_WideInt)date2.julianDay )
+ + secondOfDay;
+
+ SetDateFieldsTimeZone(&date2, timezoneObj);
+ if (ConvertLocalToUTC(interp, &date2, tzdata, GREGORIAN_CHANGE_DATE)
+ != TCL_OK) {
+ SetDateFieldsTimeZone(&date2, NULL);
+ goto done;
+ }
+ SetDateFieldsTimeZone(&date2, NULL);
+
+ seconds = date2.seconds;
+ }
+
+ /*
+ * Do relative month
+ */
+
+ if (yy.dateHaveOrdinalMonth) {
+ int monthDiff;
+ if (yy.dateMonthOrdinal > 0) {
+ monthDiff = yy.dateMonth - date.month;
+ if (monthDiff <= 0) {
+ monthDiff += 12;
+ }
+ yy.dateMonthOrdinal--;
+ } else {
+ monthDiff = date.month - yy.dateMonth;
+ if (monthDiff >= 0) {
+ monthDiff -= 12;
+ }
+ yy.dateMonthOrdinal++;
+ }
+ /* [SB] TODO: rewrite it in C: * /
+ seconds = [add $seconds $yy.dateMonthOrdinal years $monthDiff months \
+ -timezone $timezone -locale $locale]
+ */
+ }
+
+ ret = TCL_OK;
+
+done:
+
+ if (date.tzName != NULL) {
+ Tcl_DecrRefCount(date.tzName);
+ }
+ Tcl_DecrRefCount(cleanUpList);
+
+ if (ret != TCL_OK) {
+ return ret;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(seconds));
return TCL_OK;
}