/* * tclDate.h -- * * This header file handles common usage of clock primitives * between tclDate.c (yacc), tclClock.c and tclClockFmt.c. * * Copyright (c) 2014 Serg G. Brester (aka sebres) * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #ifndef _TCLCLOCK_H #define _TCLCLOCK_H /* * Constants */ #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) #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 */ #define RODDENBERRY 1946 /* Another epoch (Hi, Jeff!) */ enum DateInfoFlags { CLF_OPTIONAL = 1 << 0, /* token is non mandatory */ CLF_POSIXSEC = 1 << 1, CLF_LOCALSEC = 1 << 2, CLF_JULIANDAY = 1 << 3, CLF_TIME = 1 << 4, CLF_ZONE = 1 << 5, CLF_CENTURY = 1 << 6, CLF_DAYOFMONTH = 1 << 7, CLF_DAYOFYEAR = 1 << 8, CLF_MONTH = 1 << 9, CLF_YEAR = 1 << 10, CLF_DAYOFWEEK = 1 << 11, CLF_ISO8601YEAR = 1 << 12, CLF_ISO8601WEEK = 1 << 13, CLF_ISO8601CENTURY = 1 << 14, CLF_SIGNED = 1 << 15, /* Compounds */ CLF_HAVEDATE = (CLF_DAYOFMONTH | CLF_MONTH | CLF_YEAR), CLF_DATE = (CLF_JULIANDAY | CLF_DAYOFMONTH | CLF_DAYOFYEAR | CLF_MONTH | CLF_YEAR | CLF_ISO8601YEAR | CLF_DAYOFWEEK | CLF_ISO8601WEEK), /* * Extra flags used outside of scan/format-tokens too (int, not a short). */ CLF_RELCONV = 1 << 17, CLF_ORDINALMONTH = 1 << 18, /* On demand (lazy) assemble flags */ CLF_ASSEMBLE_DATE = 1 << 28,/* assemble year, month, etc. using julianDay */ CLF_ASSEMBLE_JULIANDAY = 1 << 29, /* assemble julianDay using year, month, etc. */ CLF_ASSEMBLE_SECONDS = 1 << 30 /* assemble localSeconds (and seconds at end) */ }; #define TCL_MIN_SECONDS -0x00F0000000000000LL #define TCL_MAX_SECONDS 0x00F0000000000000LL #define TCL_INV_SECONDS (TCL_MIN_SECONDS - 1) /* * Enumeration of the string literals used in [clock] */ typedef enum ClockLiteral { LIT__NIL, LIT__DEFAULT_FORMAT, LIT_SYSTEM, LIT_CURRENT, LIT_C, LIT_BCE, 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_TZDATA, LIT_GETSYSTEMTIMEZONE, LIT_SETUPTIMEZONE, LIT_MCGET, LIT_GETSYSTEMLOCALE, LIT_GETCURRENTLOCALE, LIT_LOCALIZE_FORMAT, LIT__END } ClockLiteral; #define CLOCK_LITERAL_ARRAY(litarr) static const char *const litarr[] = { \ "", \ "%a %b %d %H:%M:%S %Z %Y", \ "system", "current", "C", \ "BCE", "CE", \ "dayOfMonth", "dayOfWeek", "dayOfYear", \ "era", ":GMT", "gregorian", \ "integer value too large to represent", \ "iso8601Week", "iso8601Year", \ "julianDay", "localSeconds", \ "month", \ "seconds", "tzName", "tzOffset", \ "year", \ "::tcl::clock::TZData", \ "::tcl::clock::GetSystemTimeZone", \ "::tcl::clock::SetupTimeZone", \ "::tcl::clock::mcget", \ "::tcl::clock::GetSystemLocale", "::tcl::clock::mclocale", \ "::tcl::clock::LocalizeFormat" \ } /* * Enumeration of the msgcat literals used in [clock] */ typedef enum ClockMsgCtLiteral { MCLIT__NIL, /* placeholder */ MCLIT_MONTHS_FULL, MCLIT_MONTHS_ABBREV, MCLIT_MONTHS_COMB, MCLIT_DAYS_OF_WEEK_FULL, MCLIT_DAYS_OF_WEEK_ABBREV, MCLIT_DAYS_OF_WEEK_COMB, MCLIT_AM, MCLIT_PM, MCLIT_LOCALE_ERAS, MCLIT_BCE, MCLIT_CE, MCLIT_BCE2, MCLIT_CE2, MCLIT_BCE3, MCLIT_CE3, MCLIT_LOCALE_NUMERALS, MCLIT__END } ClockMsgCtLiteral; #define CLOCK_LOCALE_LITERAL_ARRAY(litarr, pref) static const char *const litarr[] = { \ pref "", \ pref "MONTHS_FULL", pref "MONTHS_ABBREV", pref "MONTHS_COMB", \ pref "DAYS_OF_WEEK_FULL", pref "DAYS_OF_WEEK_ABBREV", pref "DAYS_OF_WEEK_COMB", \ pref "AM", pref "PM", \ pref "LOCALE_ERAS", \ pref "BCE", pref "CE", \ pref "b.c.e.", pref "c.e.", \ pref "b.c.", pref "a.d.", \ pref "LOCALE_NUMERALS", \ } /* * Structure containing the fields used in [clock format] and [clock scan] */ enum TclDateFieldsFlags { CLF_CTZ = (1 << 4) }; typedef struct TclDateFields { /* Cacheable fields: */ 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_WideInt julianDay; /* Julian Day Number in local time zone */ int isBce; /* 1 if BCE */ 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 */ int hour; /* Hours of day (in-between time only calculation) */ int minutes; /* Minutes of hour (in-between time only calculation) */ Tcl_WideInt secondOfMin; /* Seconds of minute (in-between time only calculation) */ Tcl_WideInt secondOfDay; /* Seconds of day (in-between time only calculation) */ int flags; /* 0 or CLF_CTZ */ /* Non cacheable fields: */ Tcl_Obj *tzName; /* Name (or corresponding DST-abbreviation) of the * time zone, if set the refCount is incremented */ } TclDateFields; #define ClockCacheableDateFieldsSize \ offsetof(TclDateFields, tzName) /* * Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* * Structure contains return parsed fields. */ typedef struct DateInfo { const char *dateStart; const char *dateInput; const char *dateEnd; TclDateFields date; int flags; /* Signals parts of date/time get found */ int errFlags; /* Signals error (part of date/time found twice) */ MERIDIAN dateMeridian; int dateTimezone; int dateDSTmode; Tcl_WideInt dateRelMonth; Tcl_WideInt dateRelDay; Tcl_WideInt dateRelSeconds; int dateMonthOrdinalIncr; int dateMonthOrdinal; int dateDayOrdinal; Tcl_WideInt *dateRelPointer; int dateSpaceCount; int dateDigitCount; int dateCentury; Tcl_Obj *messages; /* Error messages */ const char* separatrix; /* String separating messages */ } DateInfo; #define yydate (info->date) /* Date fields used for converting */ #define yyDay (info->date.dayOfMonth) #define yyMonth (info->date.month) #define yyYear (info->date.year) #define yyHour (info->date.hour) #define yyMinutes (info->date.minutes) #define yySeconds (info->date.secondOfMin) #define yySecondOfDay (info->date.secondOfDay) #define yyDSTmode (info->dateDSTmode) #define yyDayOrdinal (info->dateDayOrdinal) #define yyDayOfWeek (info->date.dayOfWeek) #define yyMonthOrdinalIncr (info->dateMonthOrdinalIncr) #define yyMonthOrdinal (info->dateMonthOrdinal) #define yyTimezone (info->dateTimezone) #define yyMeridian (info->dateMeridian) #define yyRelMonth (info->dateRelMonth) #define yyRelDay (info->dateRelDay) #define yyRelSeconds (info->dateRelSeconds) #define yyRelPointer (info->dateRelPointer) #define yyInput (info->dateInput) #define yyDigitCount (info->dateDigitCount) #define yySpaceCount (info->dateSpaceCount) static inline void ClockInitDateInfo( DateInfo *info) { memset(info, 0, sizeof(DateInfo)); } /* * Structure containing the command arguments supplied to [clock format] and [clock scan] */ enum ClockFmtScnCmdArgsFlags { CLF_VALIDATE_S1 = (1 << 0), CLF_VALIDATE_S2 = (1 << 1), CLF_VALIDATE = (CLF_VALIDATE_S1|CLF_VALIDATE_S2), CLF_EXTENDED = (1 << 4), CLF_STRICT = (1 << 8), CLF_LOCALE_USED = (1 << 15) }; typedef struct ClockClientData ClockClientData; typedef struct ClockFmtScnCmdArgs { ClockClientData *dataPtr; /* Pointer to literal pool, etc. */ Tcl_Interp *interp; /* Tcl interpreter */ Tcl_Obj *formatObj; /* Format */ Tcl_Obj *localeObj; /* Name of the locale where the time will be expressed. */ Tcl_Obj *timezoneObj; /* Default time zone in which the time will be expressed */ Tcl_Obj *baseObj; /* Base (scan and add) or clockValue (format) */ int flags; /* Flags control scanning */ Tcl_Obj *mcDictObj; /* Current dictionary of tcl::clock package for given localeObj*/ } ClockFmtScnCmdArgs; /* Last-period cache for fast UTC to local and backwards conversion */ typedef struct ClockLastTZOffs { /* keys */ Tcl_Obj *timezoneObj; int changeover; Tcl_WideInt localSeconds; Tcl_WideInt rangesVal[2]; /* Bounds for cached time zone offset */ /* values */ int tzOffset; Tcl_Obj *tzName; /* Name (abbreviation) of this area in TZ */ } ClockLastTZOffs; /* * Structure containing the client data for [clock] */ typedef struct ClockClientData { size_t refCount; /* Number of live references. */ Tcl_Obj **literals; /* Pool of object literals (common, locale independent). */ Tcl_Obj **mcLiterals; /* Msgcat object literals with mc-keys for search with locale. */ Tcl_Obj **mcLitIdxs; /* Msgcat object indices prefixed with _IDX_, * used for quick dictionary search */ Tcl_Obj *mcDicts; /* Msgcat collection, contains weak pointers to locale * catalogs, and owns it references (onetime referenced) */ /* Cache for current clock parameters, imparted via "configure" */ size_t lastTZEpoch; int currentYearCentury; int yearOfCenturySwitch; int validMinYear; int validMaxYear; double maxJDN; Tcl_Obj *systemTimeZone; Tcl_Obj *systemSetupTZData; Tcl_Obj *gmtSetupTimeZoneUnnorm; Tcl_Obj *gmtSetupTimeZone; Tcl_Obj *gmtSetupTZData; Tcl_Obj *gmtTZName; Tcl_Obj *lastSetupTimeZoneUnnorm; Tcl_Obj *lastSetupTimeZone; Tcl_Obj *lastSetupTZData; Tcl_Obj *prevSetupTimeZoneUnnorm; Tcl_Obj *prevSetupTimeZone; Tcl_Obj *prevSetupTZData; Tcl_Obj *defaultLocale; Tcl_Obj *defaultLocaleDict; Tcl_Obj *currentLocale; Tcl_Obj *currentLocaleDict; Tcl_Obj *lastUsedLocaleUnnorm; Tcl_Obj *lastUsedLocale; Tcl_Obj *lastUsedLocaleDict; Tcl_Obj *prevUsedLocaleUnnorm; Tcl_Obj *prevUsedLocale; Tcl_Obj *prevUsedLocaleDict; /* Cache for last base (last-second fast convert if base/tz not changed) */ struct { Tcl_Obj *timezoneObj; TclDateFields date; } lastBase; /* Last-period cache for fast UTC to Local and backwards conversion */ ClockLastTZOffs lastTZOffsCache[2]; int defFlags; /* Default flags (from configure), ATM * only CLF_VALIDATE supported */ } ClockClientData; #define ClockDefaultYearCentury 2000 #define ClockDefaultCenturySwitch 38 /* * Clock scan and format facilities. */ #ifndef TCL_MEM_DEBUG # define CLOCK_FMT_SCN_STORAGE_GC_SIZE 32 #else # define CLOCK_FMT_SCN_STORAGE_GC_SIZE 0 #endif #define CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE 2 typedef struct ClockScanToken ClockScanToken; typedef int ClockScanTokenProc( ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok); typedef enum _CLCKTOK_TYPE { CTOKT_INT = 1, CTOKT_WIDE, CTOKT_PARSER, CTOKT_SPACE, CTOKT_WORD, CTOKT_CHAR, CFMTT_PROC } CLCKTOK_TYPE; typedef struct ClockScanTokenMap { unsigned short type; unsigned short flags; unsigned short clearFlags; unsigned short minSize; unsigned short maxSize; unsigned short offs; ClockScanTokenProc *parser; const void *data; } ClockScanTokenMap; struct ClockScanToken { const ClockScanTokenMap *map; struct { const char *start; const char *end; } tokWord; unsigned short endDistance; unsigned short lookAhMin; unsigned short lookAhMax; unsigned short lookAhTok; }; #define MIN_FMT_RESULT_BLOCK_ALLOC 80 #define MIN_FMT_RESULT_BLOCK_DELTA 0 /* Maximal permitted threshold (buffer size > result size) in percent, * to directly return the buffer without reallocate */ #define MAX_FMT_RESULT_THRESHOLD 2 typedef struct DateFormat { char *resMem; char *resEnd; char *output; TclDateFields date; Tcl_Obj *localeEra; } DateFormat; enum ClockFormatTokenMapFlags { CLFMT_INCR = (1 << 3), CLFMT_DECR = (1 << 4), CLFMT_CALC = (1 << 5), CLFMT_LOCALE_INDX = (1 << 8) }; typedef struct ClockFormatToken ClockFormatToken; typedef int ClockFormatTokenProc( ClockFmtScnCmdArgs *opts, DateFormat *dateFmt, ClockFormatToken *tok, int *val); typedef struct ClockFormatTokenMap { unsigned short type; const char *tostr; unsigned short width; unsigned short flags; unsigned short divider; unsigned short divmod; unsigned short offs; ClockFormatTokenProc *fmtproc; void *data; } ClockFormatTokenMap; struct ClockFormatToken { const ClockFormatTokenMap *map; struct { const char *start; const char *end; } tokWord; }; typedef struct ClockFmtScnStorage ClockFmtScnStorage; struct ClockFmtScnStorage { int objRefCount; /* Reference count shared across threads */ ClockScanToken *scnTok; unsigned scnTokC; unsigned scnSpaceCount; /* Count of mandatory spaces used in format */ ClockFormatToken *fmtTok; unsigned fmtTokC; #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 ClockFmtScnStorage *nextPtr; ClockFmtScnStorage *prevPtr; #endif size_t fmtMinAlloc; #if 0 Tcl_HashEntry hashEntry /* ClockFmtScnStorage is a derivate of Tcl_HashEntry, * stored by offset +sizeof(self) */ #endif }; /* * Clock macros. */ /* * Extracts Julian day and seconds of the day from posix seconds (tm). */ #define ClockExtractJDAndSODFromSeconds(jd, sod, tm) \ do { \ jd = (tm + JULIAN_SEC_POSIX_EPOCH); \ if (jd >= SECONDS_PER_DAY || jd <= -SECONDS_PER_DAY) { \ jd /= SECONDS_PER_DAY; \ sod = (int)(tm % SECONDS_PER_DAY); \ } else { \ sod = (int)jd, jd = 0; \ } \ if (sod < 0) { \ sod += SECONDS_PER_DAY; \ /* JD is affected, if switched into negative (avoid 24 hours difference) */ \ if (jd <= 0) { \ jd--; \ } \ } \ } while(0) /* * Prototypes of module functions. */ MODULE_SCOPE int ToSeconds(int Hours, int Minutes, int Seconds, MERIDIAN Meridian); MODULE_SCOPE int IsGregorianLeapYear(TclDateFields *); MODULE_SCOPE void GetJulianDayFromEraYearWeekDay( TclDateFields *fields, int changeover); MODULE_SCOPE void GetJulianDayFromEraYearMonthDay( TclDateFields *fields, int changeover); MODULE_SCOPE void GetJulianDayFromEraYearDay( TclDateFields *fields, int changeover); MODULE_SCOPE int ConvertUTCToLocal(ClockClientData *dataPtr, Tcl_Interp *, TclDateFields *, Tcl_Obj *timezoneObj, int); MODULE_SCOPE Tcl_Obj * LookupLastTransition(Tcl_Interp *, Tcl_WideInt, Tcl_Size, Tcl_Obj *const *, Tcl_WideInt *rangesVal); MODULE_SCOPE int TclClockFreeScan(Tcl_Interp *interp, DateInfo *info); /* tclClock.c module declarations */ MODULE_SCOPE Tcl_Obj * ClockSetupTimeZone(ClockClientData *dataPtr, Tcl_Interp *interp, Tcl_Obj *timezoneObj); MODULE_SCOPE Tcl_Obj * ClockMCDict(ClockFmtScnCmdArgs *opts); MODULE_SCOPE Tcl_Obj * ClockMCGet(ClockFmtScnCmdArgs *opts, int mcKey); MODULE_SCOPE Tcl_Obj * ClockMCGetIdx(ClockFmtScnCmdArgs *opts, int mcKey); MODULE_SCOPE int ClockMCSetIdx(ClockFmtScnCmdArgs *opts, int mcKey, Tcl_Obj *valObj); /* tclClockFmt.c module declarations */ MODULE_SCOPE char * TclItoAw(char *buf, int val, char padchar, unsigned short width); MODULE_SCOPE int TclAtoWIe(Tcl_WideInt *out, const char *p, const char *e, int sign); MODULE_SCOPE Tcl_Obj* ClockFrmObjGetLocFmtKey(Tcl_Interp *interp, Tcl_Obj *objPtr); MODULE_SCOPE ClockFmtScnStorage *Tcl_GetClockFrmScnFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr); MODULE_SCOPE Tcl_Obj * ClockLocalizeFormat(ClockFmtScnCmdArgs *opts); MODULE_SCOPE int ClockScan(DateInfo *info, Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts); MODULE_SCOPE int ClockFormat(DateFormat *dateFmt, ClockFmtScnCmdArgs *opts); MODULE_SCOPE void ClockFrmScnClearCaches(void); MODULE_SCOPE void ClockFrmScnFinalize(); #endif /* _TCLCLOCK_H */