/*
* 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 */