Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | TIP 688: clock command revision and speedup |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | core-8-branch |
Files: | files | file ages | folders |
SHA3-256: |
7137ea11e9e343f61d0fabaeaab6081a |
User & Date: | jan.nijtmans 2024-04-01 18:36:35 |
Context
2024-04-02
| ||
11:13 | Merge 8.6 check-in: 333bdb34cc user: jan.nijtmans tags: core-8-branch | |
2024-04-01
| ||
19:56 | TIP 688: clock command revision and speedup check-in: dd916eacf3 user: jan.nijtmans tags: trunk, main | |
19:00 | TIP 688: clock command revision and speedup check-in: 279e584045 user: jan.nijtmans tags: bug-ac7592e73c10d04b | |
18:36 | TIP 688: clock command revision and speedup check-in: 7137ea11e9 user: jan.nijtmans tags: core-8-branch | |
2024-03-30
| ||
14:48 | Merge-mark check-in: e9afc777b6 user: jan.nijtmans tags: core-8-branch | |
2024-03-26
| ||
17:37 | added missing test coverage for clock ensemble in safe-interpreter (shared from parent interpreter) Closed-Leaf check-in: 12d00c9a0b user: sebres tags: tip-688 | |
Changes
Changes to doc/clock.n.
︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 1 January 1970, 00:00 UTC. Note that the count of seconds does not include any leap seconds; seconds are counted as if each UTC day has exactly 86400 seconds. Tcl responds to leap seconds by speeding or slowing its clock by a tiny fraction for some minutes until it is back in sync with UTC; its data model does not represent minutes that have 59 or 61 seconds. .TP \fIunit\fR One of the words, \fBseconds\fR, \fBminutes\fR, \fBhours\fR, \fBdays\fR, \fBweekdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR. Used in conjunction with \fIcount\fR to identify an interval of time, for example, \fI3 seconds\fR or \fI1 year\fR. .SS "OPTIONS" .TP | > > > > > > > > > | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 1 January 1970, 00:00 UTC. Note that the count of seconds does not include any leap seconds; seconds are counted as if each UTC day has exactly 86400 seconds. Tcl responds to leap seconds by speeding or slowing its clock by a tiny fraction for some minutes until it is back in sync with UTC; its data model does not represent minutes that have 59 or 61 seconds. .TP \fI\-now\fR Instead of \fItimeVal\fR a non-integer option \fI\-now\fR can be used as replacement for today, which is simply interpolated to the runt-time as value of \fBclock seconds\fR. For example: .sp \fBclock format -now -f %a; # current day of the week\fR .sp \fBclock add -now 1 month; # next month\fR .TP \fIunit\fR One of the words, \fBseconds\fR, \fBminutes\fR, \fBhours\fR, \fBdays\fR, \fBweekdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR. Used in conjunction with \fIcount\fR to identify an interval of time, for example, \fI3 seconds\fR or \fI1 year\fR. .SS "OPTIONS" .TP |
︙ | ︙ | |||
384 385 386 387 388 389 390 | .PP The date is determined according to the fields that are present in the preprocessed format string. In order of preference: .IP [1] If the string contains a \fB%s\fR format group, representing seconds from the epoch, that group is used to determine the date. .IP [2] | | > | > > > | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | .PP The date is determined according to the fields that are present in the preprocessed format string. In order of preference: .IP [1] If the string contains a \fB%s\fR format group, representing seconds from the epoch, that group is used to determine the date. .IP [2] If the string contains a \fB%J\fR, \fB%EJ\fR or \fB%Ej\fR format groups, representing the Calendar or Astronomical Julian Day Number, that groups are used to determine the date. Note, that in case of \fB%EJ\fR or \fB%Ej\fR format groups, representing the Julian Date with time fraction, this groups may be used to determine the date and time. .IP [3] If the string contains a complete set of format groups specifying century, year, month, and day of month; century, year, and day of year; or ISO8601 fiscal year, week of year, and day of week; those groups are combined and used to determine the date. If more than one complete set is present, the one at the rightmost position in the string is used. |
︙ | ︙ | |||
536 537 538 539 540 541 542 543 544 545 546 547 548 549 | string of the same meaning in the locale, to indicate whether \fB%Y\fR refers to years before or after Year 1 of the Common Era. On input, accepts the string \fBB.C.E.\fR, \fBB.C.\fR, \fBC.E.\fR, \fBA.D.\fR, or the abbreviation appropriate to the current locale, and uses it to fix whether \fB%Y\fR refers to years before or after Year 1 of the Common Era. .TP \fB%Ex\fR On output, produces a locale-dependent representation of the date in the locale's alternative calendar. On input, matches whatever \fB%Ex\fR produces. The locale's alternative calendar need not be the Gregorian calendar. .TP \fB%EX\fR | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | string of the same meaning in the locale, to indicate whether \fB%Y\fR refers to years before or after Year 1 of the Common Era. On input, accepts the string \fBB.C.E.\fR, \fBB.C.\fR, \fBC.E.\fR, \fBA.D.\fR, or the abbreviation appropriate to the current locale, and uses it to fix whether \fB%Y\fR refers to years before or after Year 1 of the Common Era. .TP \fB%Ej\fR On output, produces a string of digits giving the Astronomical Julian Date or Astronomical Julian Day Number (JDN/JD). In opposite to calendar julian day \fB%J\fR, it starts the day at noon. On input, accepts a string of digits (or floating point with the time fraction) and interprets it as an Astronomical Julian Day Number (JDN/JD). The Astronomical Julian Date is a count of the number of calendar days that have elapsed since 1 January, 4713 BCE of the proleptic Julian calendar, which contains also the time fraction (after floating point). The epoch time of 1 January 1970 corresponds to Astronomical JDN 2440587.5. This value corresponds the julian day used in sqlite-database, and is the same as result of \fBselect julianday(:seconds, 'unixepoch')\fR. .TP \fB%EJ\fR On output, produces a string of digits giving the Calendar Julian Date. In opposite to julian day \fB%J\fR format group, it produces float number. In opposite to astronomical julian day \fB%Ej\fR group, it starts at midnight. On input, accepts a string of digits (or floating point with the time fraction) and interprets it as a Calendar Julian Day Number. The Calendar Julian Date is a count of the number of calendar days that have elapsed since 1 January, 4713 BCE of the proleptic Julian calendar, which contains also the time fraction (after floating point). The epoch time of 1 January 1970 corresponds to Astronomical JDN 2440588. .TP \fB%Es\fR This affects similar to \fB%s\fR, but in opposition to \fB%s\fR it parses or formats local seconds (not the posix seconds). Because \fB%s\fR has the same precedence as \fB%s\fR (uniquely determines a point in time), it overrides all other input formats. .TP \fB%Ex\fR On output, produces a locale-dependent representation of the date in the locale's alternative calendar. On input, matches whatever \fB%Ex\fR produces. The locale's alternative calendar need not be the Gregorian calendar. .TP \fB%EX\fR |
︙ | ︙ | |||
585 586 587 588 589 590 591 | (12-11) on a 12-hour clock. On input, accepts such a number. .TP \fB%j\fR On output, produces a three-digit number giving the day of the year (001-366). On input, accepts such a number. .TP \fB%J\fR | | | 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | (12-11) on a 12-hour clock. On input, accepts such a number. .TP \fB%j\fR On output, produces a three-digit number giving the day of the year (001-366). On input, accepts such a number. .TP \fB%J\fR On output, produces a string of digits giving the calendar Julian Day Number. On input, accepts a string of digits and interprets it as a Julian Day Number. The Julian Day Number is a count of the number of calendar days that have elapsed since 1 January, 4713 BCE of the proleptic Julian calendar. The epoch time of 1 January 1970 corresponds to Julian Day Number 2440588. .TP \fB%k\fR |
︙ | ︙ | |||
730 731 732 733 734 735 736 | that \fB%Y\fR does not yield a year appropriate for use with the ISO8601 week number \fB%V\fR; programs should use \fB%G\fR for that purpose. .TP \fB%z\fR On output, produces the current time zone, expressed in hours and minutes east (+hhmm) or west (\-hhmm) of Greenwich. On input, accepts a time zone specifier (see \fBTIME ZONES\fR below) that will be used to | | > > | | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 | that \fB%Y\fR does not yield a year appropriate for use with the ISO8601 week number \fB%V\fR; programs should use \fB%G\fR for that purpose. .TP \fB%z\fR On output, produces the current time zone, expressed in hours and minutes east (+hhmm) or west (\-hhmm) of Greenwich. On input, accepts a time zone specifier (see \fBTIME ZONES\fR below) that will be used to determine the time zone (this token is optionally applicable on input, so the value is not mandatory and can be missing in input). .TP \fB%Z\fR On output, produces the current time zone's name, possibly translated to the given locale. On input, accepts a time zone specifier (see \fBTIME ZONES\fR below) that will be used to determine the time zone (token is also like \fB%z\fR optionally applicable on input). This option should, in general, be used on input only when parsing RFC822 dates. Other uses are fraught with ambiguity; for instance, the string \fBBST\fR may represent British Summer Time or Brazilian Standard Time. It is recommended that date/time strings for use by computers use numeric time zones instead. .TP \fB%%\fR On output, produces a literal |
︙ | ︙ | |||
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | Using that time as the base, day-of-week specifications are added. Next, relative specifications are used. If a date or day is specified, and no absolute or relative time is given, midnight is used. Finally, a correction is applied so that the correct hour of the day is produced after allowing for daylight savings time differences and the correct date is given when going from the end of a long month to a short month. .SH "SEE ALSO" msgcat(n) .SH KEYWORDS clock, date, time .SH "COPYRIGHT" Copyright \(co 2004 Kevin B. Kenny <[email protected]>. All rights reserved. '\" Local Variables: '\" mode: nroff '\" End: | > > > > > > > > > > > > > > > > > > | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | Using that time as the base, day-of-week specifications are added. Next, relative specifications are used. If a date or day is specified, and no absolute or relative time is given, midnight is used. Finally, a correction is applied so that the correct hour of the day is produced after allowing for daylight savings time differences and the correct date is given when going from the end of a long month to a short month. .PP The precedence of the applying of single tokens resp. which sequence will be used by calculating of the time is complex, e. g. heavily dependent on the precision of type of the token. .sp In example below the second date-string contains "next January", therefore it results in next year but in January. And third date-string besides "January" contains also additionally "Fri", so it results in the nearest Friday. Thus both win before "385 days" resp. make it more precise, because of higher precision of this token types. .CS % clock format [clock scan "5 years 18 months 385 days" -base 0 -gmt 1] -gmt 1 Thu Jul 21 00:00:00 GMT 1977 % clock format [clock scan "5 years 18 months 385 days next January" -base 0 -gmt 1] -gmt 1 Sat Jan 21 00:00:00 GMT 1978 % clock format [clock scan "5 years 18 months 385 days next January Fri" -base 0 -gmt 1] -gmt 1 Fri Jan 27 00:00:00 GMT 1978 .CE .SH "SEE ALSO" msgcat(n) .SH KEYWORDS clock, date, time .SH "COPYRIGHT" Copyright \(co 2004 Kevin B. Kenny <[email protected]>. All rights reserved. '\" Local Variables: '\" mode: nroff '\" End: |
Changes to generic/tclClock.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* * tclClock.c -- * * Contains the time and date related commands. This code is derived from * the time and date facilities of TclX, by Mark Diekhans and Karl * Lehenbauer. * * Copyright © 1991-1995 Karl Lehenbauer & Mark Diekhans. * Copyright © 1995 Sun Microsystems, Inc. * Copyright © 2004 Kevin B. Kenny. 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 "tclTomMath.h" /* * Windows has mktime. The configurators do not check. */ #ifdef _WIN32 #define HAVE_MKTIME 1 #endif | > > > < < < < < < < < < < < < < < < > > > > < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < | < < | < | < < < > < < < < < < < < < < < < < < < < < < < < < | > | | | > | | < < < > > > > > > | > > > > > > > > | > > > | > | > > > > > > | > > | > | | | | < > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | /* * tclClock.c -- * * Contains the time and date related commands. This code is derived from * the time and date facilities of TclX, by Mark Diekhans and Karl * Lehenbauer. * * Copyright © 1991-1995 Karl Lehenbauer & Mark Diekhans. * Copyright © 1995 Sun Microsystems, Inc. * Copyright © 2004 Kevin B. Kenny. All rights reserved. * Copyright © 2015 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 "tclTomMath.h" #include "tclStrIdxTree.h" #include "tclDate.h" /* * Windows has mktime. The configurators do not check. */ #ifdef _WIN32 #define HAVE_MKTIME 1 #endif /* * Table of the days in each month, leap and common years */ static const int hath[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static const int daysInPriorMonths[2][13] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} }; /* * Enumeration of the string literals used in [clock] */ CLOCK_LITERAL_ARRAY(Literals); /* 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_"); static const char *const eras[] = { "CE", "BCE", NULL }; /* * Thread specific data block holding a 'struct tm' for the 'gmtime' and * 'localtime' library calls. */ static Tcl_ThreadDataKey tmKey; /* * Mutex protecting 'gmtime', 'localtime' and 'mktime' calls and the statics * in the date parsing code. */ TCL_DECLARE_MUTEX(clockMutex) /* * Function prototypes for local procedures in this file: */ static int ConvertUTCToLocalUsingTable(Tcl_Interp *, TclDateFields *, Tcl_Size, Tcl_Obj *const[], Tcl_WideInt *rangesVal); static int ConvertUTCToLocalUsingC(Tcl_Interp *, TclDateFields *, int); static int ConvertLocalToUTC(void *clientData, Tcl_Interp *, TclDateFields *, Tcl_Obj *timezoneObj, int); static int ConvertLocalToUTCUsingTable(Tcl_Interp *, TclDateFields *, int, Tcl_Obj *const[], Tcl_WideInt *rangesVal); static int ConvertLocalToUTCUsingC(Tcl_Interp *, TclDateFields *, int); static int ClockConfigureObjCmd(void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static void GetYearWeekDay(TclDateFields *, int); static void GetGregorianEraYearDay(TclDateFields *, int); static void GetMonthDay(TclDateFields *); static Tcl_WideInt WeekdayOnOrBefore(int, Tcl_WideInt); static Tcl_ObjCmdProc ClockClicksObjCmd; static Tcl_ObjCmdProc ClockConvertlocaltoutcObjCmd; static int ClockGetDateFields(void *clientData, Tcl_Interp *interp, TclDateFields *fields, Tcl_Obj *timezoneObj, int changeover); static Tcl_ObjCmdProc ClockGetdatefieldsObjCmd; static Tcl_ObjCmdProc ClockGetjuliandayfromerayearmonthdayObjCmd; static Tcl_ObjCmdProc ClockGetjuliandayfromerayearweekdayObjCmd; static Tcl_ObjCmdProc ClockGetenvObjCmd; static Tcl_ObjCmdProc ClockMicrosecondsObjCmd; static Tcl_ObjCmdProc ClockMillisecondsObjCmd; static Tcl_ObjCmdProc ClockSecondsObjCmd; static Tcl_ObjCmdProc ClockFormatObjCmd; static Tcl_ObjCmdProc ClockScanObjCmd; static int ClockScanCommit( DateInfo *info, ClockFmtScnCmdArgs *opts); static int ClockFreeScan( DateInfo *info, Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts); static int ClockCalcRelTime( DateInfo *info); static Tcl_ObjCmdProc ClockAddObjCmd; static int ClockValidDate( DateInfo *, ClockFmtScnCmdArgs *, int stage); static struct tm * ThreadSafeLocalTime(const time_t *); static size_t TzsetIfNecessary(void); static void ClockDeleteCmdProc(void *); static Tcl_ObjCmdProc ClockSafeCatchCmd; /* * Structure containing description of "native" clock commands to create. */ 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 * will always have the ClockClientData sent * to it, but may well ignore this data. */ CompileProc *compileProc; /* The compiler for the command. */ void *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)}, {"ConvertLocalToUTC", ClockConvertlocaltoutcObjCmd, NULL, NULL}, {"GetDateFields", ClockGetdatefieldsObjCmd, NULL, NULL}, {"GetJulianDayFromEraYearMonthDay", ClockGetjuliandayfromerayearmonthdayObjCmd, NULL, NULL}, {"GetJulianDayFromEraYearWeekDay", ClockGetjuliandayfromerayearweekdayObjCmd, NULL, NULL}, {"catch", ClockSafeCatchCmd, TclCompileBasicMin1ArgCmd, NULL}, {NULL, NULL, NULL, NULL} }; /* *---------------------------------------------------------------------- * * TclClockInit -- * |
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 | TclClockInit( Tcl_Interp *interp) /* Tcl interpreter */ { const struct ClockCommand *clockCmdPtr; char cmdName[50]; /* Buffer large enough to hold the string *::tcl::clock::GetJulianDayFromEraYearMonthDay * plus a terminating NUL. */ ClockClientData *data; int i; | > < < < < < < < < < < < < < > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > | > > > > > < > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 | TclClockInit( Tcl_Interp *interp) /* Tcl interpreter */ { const struct ClockCommand *clockCmdPtr; char cmdName[50]; /* Buffer large enough to hold the string *::tcl::clock::GetJulianDayFromEraYearMonthDay * plus a terminating NUL. */ Command *cmdPtr; ClockClientData *data; int i; /* * Safe interps get [::clock] as alias to a parent, so do not need their * own copies of the support routines. */ if (Tcl_IsSafe(interp)) { return; } /* * Create the client data, which is a refcounted literal pool. */ data = (ClockClientData *)ckalloc(sizeof(ClockClientData)); data->refCount = 0; data->literals = (Tcl_Obj **)ckalloc(LIT__END * sizeof(Tcl_Obj*)); for (i = 0; i < LIT__END; ++i) { TclInitObjRef(data->literals[i], Tcl_NewStringObj(Literals[i], -1)); } data->mcLiterals = NULL; data->mcLitIdxs = NULL; data->mcDicts = NULL; data->lastTZEpoch = 0; data->currentYearCentury = ClockDefaultYearCentury; data->yearOfCenturySwitch = ClockDefaultCenturySwitch; data->validMinYear = INT_MIN; data->validMaxYear = INT_MAX; /* corresponds max of JDN in sqlite - 9999-12-31 23:59:59 per default */ data->maxJDN = 5373484.499999994; data->systemTimeZone = NULL; data->systemSetupTZData = NULL; data->gmtSetupTimeZoneUnnorm = NULL; data->gmtSetupTimeZone = NULL; data->gmtSetupTZData = NULL; data->gmtTZName = NULL; data->lastSetupTimeZoneUnnorm = NULL; data->lastSetupTimeZone = NULL; data->lastSetupTZData = NULL; data->prevSetupTimeZoneUnnorm = NULL; data->prevSetupTimeZone = NULL; data->prevSetupTZData = NULL; data->defaultLocale = NULL; data->defaultLocaleDict = NULL; data->currentLocale = NULL; data->currentLocaleDict = NULL; data->lastUsedLocaleUnnorm = NULL; data->lastUsedLocale = NULL; data->lastUsedLocaleDict = NULL; data->prevUsedLocaleUnnorm = NULL; data->prevUsedLocale = NULL; data->prevUsedLocaleDict = NULL; data->lastBase.timezoneObj = NULL; memset(&data->lastTZOffsCache, 0, sizeof(data->lastTZOffsCache)); data->defFlags = 0; /* * Install the commands. */ #define TCL_CLOCK_PREFIX_LEN 14 /* == strlen("::tcl::clock::") */ memcpy(cmdName, "::tcl::clock::", TCL_CLOCK_PREFIX_LEN); for (clockCmdPtr=clockCommands ; clockCmdPtr->name!=NULL ; clockCmdPtr++) { void *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; } cmdPtr = (Command *)Tcl_CreateObjCommand(interp, "::tcl::unsupported::clock::configure", ClockConfigureObjCmd, data, ClockDeleteCmdProc); data->refCount++; cmdPtr->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; TclUnsetObjRef(data->systemTimeZone); TclUnsetObjRef(data->systemSetupTZData); TclUnsetObjRef(data->gmtSetupTimeZoneUnnorm); TclUnsetObjRef(data->gmtSetupTimeZone); TclUnsetObjRef(data->gmtSetupTZData); TclUnsetObjRef(data->gmtTZName); TclUnsetObjRef(data->lastSetupTimeZoneUnnorm); TclUnsetObjRef(data->lastSetupTimeZone); TclUnsetObjRef(data->lastSetupTZData); TclUnsetObjRef(data->prevSetupTimeZoneUnnorm); TclUnsetObjRef(data->prevSetupTimeZone); TclUnsetObjRef(data->prevSetupTZData); TclUnsetObjRef(data->defaultLocale); data->defaultLocaleDict = NULL; TclUnsetObjRef(data->currentLocale); data->currentLocaleDict = NULL; TclUnsetObjRef(data->lastUsedLocaleUnnorm); TclUnsetObjRef(data->lastUsedLocale); data->lastUsedLocaleDict = NULL; TclUnsetObjRef(data->prevUsedLocaleUnnorm); TclUnsetObjRef(data->prevUsedLocale); data->prevUsedLocaleDict = NULL; TclUnsetObjRef(data->lastBase.timezoneObj); TclUnsetObjRef(data->lastTZOffsCache[0].timezoneObj); TclUnsetObjRef(data->lastTZOffsCache[0].tzName); TclUnsetObjRef(data->lastTZOffsCache[1].timezoneObj); TclUnsetObjRef(data->lastTZOffsCache[1].tzName); TclUnsetObjRef(data->mcDicts); } /* *---------------------------------------------------------------------- * * ClockDeleteCmdProc -- * * Remove a reference to the clock client data, and clean up memory * when it's all gone. * * Results: * None. * *---------------------------------------------------------------------- */ static void ClockDeleteCmdProc( void *clientData) /* Opaque pointer to the client data */ { ClockClientData *data = (ClockClientData *)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); } } /* *---------------------------------------------------------------------- * * SavePrevTimezoneObj -- * * Used to store previously used/cached time zone (makes it reusable). * * This enables faster switch between time zones (e. g. to convert from one to another). * * Results: * None. * *---------------------------------------------------------------------- */ static inline void SavePrevTimezoneObj( ClockClientData *dataPtr) /* Client data containing literal pool */ { Tcl_Obj *timezoneObj = dataPtr->lastSetupTimeZone; if (timezoneObj && timezoneObj != dataPtr->prevSetupTimeZone) { TclSetObjRef(dataPtr->prevSetupTimeZoneUnnorm, dataPtr->lastSetupTimeZoneUnnorm); TclSetObjRef(dataPtr->prevSetupTimeZone, timezoneObj); TclSetObjRef(dataPtr->prevSetupTZData, dataPtr->lastSetupTZData); } } /* *---------------------------------------------------------------------- * * 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 Tcl_Obj * NormTimezoneObj( ClockClientData *dataPtr, /* Client data containing literal pool */ Tcl_Obj *timezoneObj, /* Name of zone to find */ int *loaded) /* Used to recognized TZ was loaded */ { const char *tz; *loaded = 1; if ( timezoneObj == dataPtr->lastSetupTimeZoneUnnorm && dataPtr->lastSetupTimeZone != NULL ) { return dataPtr->lastSetupTimeZone; } if ( timezoneObj == dataPtr->prevSetupTimeZoneUnnorm && dataPtr->prevSetupTimeZone != NULL ) { return dataPtr->prevSetupTimeZone; } if (timezoneObj == dataPtr->gmtSetupTimeZoneUnnorm && dataPtr->gmtSetupTimeZone != NULL ) { return dataPtr->literals[LIT_GMT]; } if ( timezoneObj == dataPtr->lastSetupTimeZone || timezoneObj == dataPtr->prevSetupTimeZone || timezoneObj == dataPtr->gmtSetupTimeZone || timezoneObj == dataPtr->systemTimeZone ) { return timezoneObj; } tz = TclGetString(timezoneObj); if (dataPtr->lastSetupTimeZone != NULL && strcmp(tz, TclGetString(dataPtr->lastSetupTimeZone)) == 0 ) { TclSetObjRef(dataPtr->lastSetupTimeZoneUnnorm, timezoneObj); return dataPtr->lastSetupTimeZone; } if (dataPtr->prevSetupTimeZone != NULL && strcmp(tz, TclGetString(dataPtr->prevSetupTimeZone)) == 0 ) { TclSetObjRef(dataPtr->prevSetupTimeZoneUnnorm, timezoneObj); return dataPtr->prevSetupTimeZone; } if (dataPtr->systemTimeZone != NULL && strcmp(tz, TclGetString(dataPtr->systemTimeZone)) == 0 ) { return dataPtr->systemTimeZone; } if (strcmp(tz, Literals[LIT_GMT]) == 0) { TclSetObjRef(dataPtr->gmtSetupTimeZoneUnnorm, timezoneObj); if (dataPtr->gmtSetupTimeZone == NULL) { *loaded = 0; } return dataPtr->literals[LIT_GMT]; } /* unknown/unloaded tz - recache/revalidate later as last-setup if needed */ *loaded = 0; 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; } TclSetObjRef(dataPtr->currentLocale, Tcl_GetObjResult(interp)); dataPtr->currentLocaleDict = NULL; Tcl_ResetResult(interp); return dataPtr->currentLocale; } /* *---------------------------------------------------------------------- * * SavePrevLocaleObj -- * * Used to store previously used/cached locale (makes it reusable). * * This enables faster switch between locales (e. g. to convert from one to another). * * Results: * None. * *---------------------------------------------------------------------- */ static inline void SavePrevLocaleObj( ClockClientData *dataPtr) /* Client data containing literal pool */ { Tcl_Obj *localeObj = dataPtr->lastUsedLocale; if (localeObj && localeObj != dataPtr->prevUsedLocale) { TclSetObjRef(dataPtr->prevUsedLocaleUnnorm, dataPtr->lastUsedLocaleUnnorm); TclSetObjRef(dataPtr->prevUsedLocale, localeObj); /* mcDicts owns reference to dict */ dataPtr->prevUsedLocaleDict = dataPtr->lastUsedLocaleDict; } } /* *---------------------------------------------------------------------- * * 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, *loc2; if ( localeObj == NULL || localeObj == dataPtr->literals[LIT_C] || localeObj == dataPtr->defaultLocale ) { *mcDictObj = dataPtr->defaultLocaleDict; return dataPtr->defaultLocale ? dataPtr->defaultLocale : dataPtr->literals[LIT_C]; } if ( localeObj == dataPtr->currentLocale || localeObj == dataPtr->literals[LIT_CURRENT] ) { if (dataPtr->currentLocale == NULL) { ClockGetCurrentLocale(dataPtr, interp); } *mcDictObj = dataPtr->currentLocaleDict; return dataPtr->currentLocale; } if ( localeObj == dataPtr->lastUsedLocale || localeObj == dataPtr->lastUsedLocaleUnnorm ) { *mcDictObj = dataPtr->lastUsedLocaleDict; return dataPtr->lastUsedLocale; } if ( localeObj == dataPtr->prevUsedLocale || localeObj == dataPtr->prevUsedLocaleUnnorm ) { *mcDictObj = dataPtr->prevUsedLocaleDict; return dataPtr->prevUsedLocale; } loc = TclGetString(localeObj); if ( dataPtr->currentLocale != NULL && ( localeObj == dataPtr->currentLocale || (localeObj->length == dataPtr->currentLocale->length && strcasecmp(loc, TclGetString(dataPtr->currentLocale)) == 0 ) ) ) { *mcDictObj = dataPtr->currentLocaleDict; return dataPtr->currentLocale; } if ( dataPtr->lastUsedLocale != NULL && ( localeObj == dataPtr->lastUsedLocale || (localeObj->length == dataPtr->lastUsedLocale->length && strcasecmp(loc, TclGetString(dataPtr->lastUsedLocale)) == 0 ) ) ) { *mcDictObj = dataPtr->lastUsedLocaleDict; TclSetObjRef(dataPtr->lastUsedLocaleUnnorm, localeObj); return dataPtr->lastUsedLocale; } if ( dataPtr->prevUsedLocale != NULL && ( localeObj == dataPtr->prevUsedLocale || (localeObj->length == dataPtr->prevUsedLocale->length && strcasecmp(loc, TclGetString(dataPtr->prevUsedLocale)) == 0 ) ) ) { *mcDictObj = dataPtr->prevUsedLocaleDict; TclSetObjRef(dataPtr->prevUsedLocaleUnnorm, localeObj); return dataPtr->prevUsedLocale; } if ( (localeObj->length == 1 /* C */ && strcasecmp(loc, Literals[LIT_C]) == 0) || (dataPtr->defaultLocale && (loc2 = TclGetString(dataPtr->defaultLocale)) && localeObj->length == dataPtr->defaultLocale->length && strcasecmp(loc, loc2) == 0) ) { *mcDictObj = dataPtr->defaultLocaleDict; return dataPtr->defaultLocale ? dataPtr->defaultLocale : dataPtr->literals[LIT_C]; } if ( localeObj->length == 7 /* current */ && strcasecmp(loc, Literals[LIT_CURRENT]) == 0 ) { if (dataPtr->currentLocale == NULL) { ClockGetCurrentLocale(dataPtr, interp); } *mcDictObj = dataPtr->currentLocaleDict; return dataPtr->currentLocale; } if ( (localeObj->length == 6 /* system */ && strcasecmp(loc, Literals[LIT_SYSTEM]) == 0) ) { SavePrevLocaleObj(dataPtr); TclSetObjRef(dataPtr->lastUsedLocaleUnnorm, localeObj); localeObj = ClockGetSystemLocale(dataPtr, interp); TclSetObjRef(dataPtr->lastUsedLocale, localeObj); *mcDictObj = NULL; return localeObj; } *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. * *---------------------------------------------------------------------- */ Tcl_Obj * ClockMCDict(ClockFmtScnCmdArgs *opts) { ClockClientData *dataPtr = (ClockClientData *)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((ClockClientData *)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", (char *)NULL); return NULL; } opts->flags |= CLF_LOCALE_USED; /* check locale literals already available (on demand creation) */ if (dataPtr->mcLiterals == NULL) { int i; dataPtr->mcLiterals = (Tcl_Obj **)ckalloc(MCLIT__END * sizeof(Tcl_Obj*)); for (i = 0; i < MCLIT__END; ++i) { TclInitObjRef(dataPtr->mcLiterals[i], Tcl_NewStringObj(MsgCtLiterals[i], -1)); } } } /* check or obtain mcDictObj (be sure it's modifiable) */ if (opts->mcDictObj == NULL || opts->mcDictObj->refCount > 1) { int ref = 1; /* first try to find locale catalog dict */ if (dataPtr->mcDicts == NULL) { TclSetObjRef(dataPtr->mcDicts, Tcl_NewDictObj()); } Tcl_DictObjGet(NULL, dataPtr->mcDicts, opts->localeObj, &opts->mcDictObj); if (opts->mcDictObj == NULL) { /* get msgcat dictionary - ::tcl::clock::mcget locale */ Tcl_Obj *callargs[2]; 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); Tcl_ResetResult(opts->interp); ref = 0; /* new object is not yet referenced */ } /* be sure that object reference doesn't increase (dict changeable) */ if (opts->mcDictObj->refCount > ref) { /* smart reference (shared dict as object with no ref-counter) */ opts->mcDictObj = TclDictObjSmartRef(opts->interp, opts->mcDictObj); } /* create exactly one reference to catalog / make it searchable for future */ Tcl_DictObjPut(NULL, dataPtr->mcDicts, opts->localeObj, opts->mcDictObj); if ( opts->localeObj == dataPtr->literals[LIT_C] || opts->localeObj == dataPtr->defaultLocale ) { dataPtr->defaultLocaleDict = opts->mcDictObj; } if ( opts->localeObj == dataPtr->currentLocale ) { dataPtr->currentLocaleDict = opts->mcDictObj; } else if ( opts->localeObj == dataPtr->lastUsedLocale ) { dataPtr->lastUsedLocaleDict = opts->mcDictObj; } else { SavePrevLocaleObj(dataPtr); TclSetObjRef(dataPtr->lastUsedLocale, opts->localeObj); TclUnsetObjRef(dataPtr->lastUsedLocaleUnnorm); dataPtr->lastUsedLocaleDict = opts->mcDictObj; } } } 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. * *---------------------------------------------------------------------- */ Tcl_Obj * ClockMCGet( ClockFmtScnCmdArgs *opts, int mcKey) { ClockClientData *dataPtr = (ClockClientData *)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 = (ClockClientData *)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. * *---------------------------------------------------------------------- */ int ClockMCSetIdx( ClockFmtScnCmdArgs *opts, int mcKey, Tcl_Obj *valObj) { ClockClientData *dataPtr = (ClockClientData *)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 = (Tcl_Obj **)ckalloc(MCLIT__END * sizeof(Tcl_Obj*)); for (i = 0; i < MCLIT__END; ++i) { TclInitObjRef(dataPtr->mcLitIdxs[i], Tcl_NewStringObj(MsgCtLitIdxs[i], -1)); } } return Tcl_DictObjPut(opts->interp, opts->mcDictObj, dataPtr->mcLitIdxs[mcKey], valObj); } static void TimezoneLoaded( ClockClientData *dataPtr, Tcl_Obj *timezoneObj, /* Name of zone was loaded */ Tcl_Obj *tzUnnormObj) /* Name of zone was loaded */ { /* don't overwrite last-setup with GMT (special case) */ if (timezoneObj == dataPtr->literals[LIT_GMT]) { /* mark GMT zone loaded */ if (dataPtr->gmtSetupTimeZone == NULL) { TclSetObjRef(dataPtr->gmtSetupTimeZone, dataPtr->literals[LIT_GMT]); } TclSetObjRef(dataPtr->gmtSetupTimeZoneUnnorm, tzUnnormObj); return; } /* last setup zone loaded */ if (dataPtr->lastSetupTimeZone != timezoneObj) { SavePrevTimezoneObj(dataPtr); TclSetObjRef(dataPtr->lastSetupTimeZone, timezoneObj); TclUnsetObjRef(dataPtr->lastSetupTZData); } TclSetObjRef(dataPtr->lastSetupTimeZoneUnnorm, tzUnnormObj); } /* *---------------------------------------------------------------------- * * ClockConfigureObjCmd -- * * This function is invoked to process the Tcl "::clock::configure" (internal) command. * * Usage: * ::tcl::unsupported::clock::configure ?-option ?value?? * * Results: * Returns a standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ClockConfigureObjCmd( void *clientData, /* Client data containing literal pool */ Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Parameter count */ Tcl_Obj *const objv[]) /* Parameter vector */ { ClockClientData *dataPtr = (ClockClientData *)clientData; static const char *const options[] = { "-system-tz", "-setup-tz", "-default-locale", "-current-locale", "-clear", "-year-century", "-century-switch", "-min-year", "-max-year", "-max-jdn", "-validate", "-init-complete", NULL }; enum optionInd { CLOCK_SYSTEM_TZ, CLOCK_SETUP_TZ, CLOCK_DEFAULT_LOCALE, CLOCK_CURRENT_LOCALE, CLOCK_CLEAR_CACHE, CLOCK_YEAR_CENTURY, CLOCK_CENTURY_SWITCH, CLOCK_MIN_YEAR, CLOCK_MAX_YEAR, CLOCK_MAX_JDN, CLOCK_VALIDATE, CLOCK_INIT_COMPLETE }; 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]), (char *)NULL); return TCL_ERROR; } switch (optionIndex) { case CLOCK_SYSTEM_TZ: { /* validate current tz-epoch */ size_t lastTZEpoch = TzsetIfNecessary(); if (i < objc) { if (dataPtr->systemTimeZone != objv[i]) { TclSetObjRef(dataPtr->systemTimeZone, objv[i]); TclUnsetObjRef(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) { int loaded; Tcl_Obj *timezoneObj = NormTimezoneObj(dataPtr, objv[i], &loaded); if (!loaded) { TimezoneLoaded(dataPtr, timezoneObj, objv[i]); } Tcl_SetObjResult(interp, timezoneObj); } else if (i+1 >= objc && dataPtr->lastSetupTimeZone != NULL) { Tcl_SetObjResult(interp, dataPtr->lastSetupTimeZone); } break; case CLOCK_DEFAULT_LOCALE: if (i < objc) { if (dataPtr->defaultLocale != objv[i]) { TclSetObjRef(dataPtr->defaultLocale, objv[i]); dataPtr->defaultLocaleDict = NULL; } } if (i+1 >= objc) { Tcl_SetObjResult(interp, dataPtr->defaultLocale ? dataPtr->defaultLocale : dataPtr->literals[LIT_C]); } break; case CLOCK_CURRENT_LOCALE: if (i < objc) { if (dataPtr->currentLocale != objv[i]) { TclSetObjRef(dataPtr->currentLocale, objv[i]); dataPtr->currentLocaleDict = NULL; } } 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_NewWideIntObj(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_NewWideIntObj(dataPtr->yearOfCenturySwitch)); } break; case CLOCK_MIN_YEAR: if (i < objc) { int year; if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) { return TCL_ERROR; } dataPtr->validMinYear = year; Tcl_SetObjResult(interp, objv[i]); continue; } if (i+1 >= objc) { Tcl_SetObjResult(interp, Tcl_NewWideIntObj(dataPtr->validMinYear)); } break; case CLOCK_MAX_YEAR: if (i < objc) { int year; if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) { return TCL_ERROR; } dataPtr->validMaxYear = year; Tcl_SetObjResult(interp, objv[i]); continue; } if (i+1 >= objc) { Tcl_SetObjResult(interp, Tcl_NewWideIntObj(dataPtr->validMaxYear)); } break; case CLOCK_MAX_JDN: if (i < objc) { double jd; if (Tcl_GetDoubleFromObj(interp, objv[i], &jd) != TCL_OK) { return TCL_ERROR; } dataPtr->maxJDN = jd; Tcl_SetObjResult(interp, objv[i]); continue; } if (i+1 >= objc) { Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dataPtr->maxJDN)); } break; case CLOCK_VALIDATE: if (i < objc) { int val; if (Tcl_GetBooleanFromObj(interp, objv[i], &val) != TCL_OK) { return TCL_ERROR; } if (val) { dataPtr->defFlags |= CLF_VALIDATE; } else { dataPtr->defFlags &= ~CLF_VALIDATE; } } if (i+1 >= objc) { Tcl_SetObjResult(interp, Tcl_NewWideIntObj(dataPtr->defFlags & CLF_VALIDATE ? 1 : 0)); } break; case CLOCK_CLEAR_CACHE: ClockConfigureClear(dataPtr); break; case CLOCK_INIT_COMPLETE: { /* * Init completed. * Compile clock ensemble (performance purposes). */ Tcl_Command token = Tcl_FindCommand(interp, "::clock", NULL, TCL_GLOBAL_ONLY); if (!token) { return TCL_ERROR; } int ensFlags = 0; if (Tcl_GetEnsembleFlags(interp, token, &ensFlags) != TCL_OK) { return TCL_ERROR; } ensFlags |= ENSEMBLE_COMPILE; if (Tcl_SetEnsembleFlags(interp, token, ensFlags) != TCL_OK) { return TCL_ERROR; } } 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( void *clientData, /* Opaque pointer to literal pool, etc. */ Tcl_Interp *interp, /* Tcl interpreter */ Tcl_Obj *timezoneObj) /* Name of the timezone */ { ClockClientData *dataPtr = (ClockClientData *)clientData; Tcl_Obj *ret, **out = NULL; /* if cached (if already setup this one) */ if ( timezoneObj == dataPtr->lastSetupTimeZone || timezoneObj == dataPtr->lastSetupTimeZoneUnnorm ) { if (dataPtr->lastSetupTZData != NULL) { return dataPtr->lastSetupTZData; } out = &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->literals[LIT_GMT] || timezoneObj == dataPtr->gmtSetupTimeZoneUnnorm ) { if (dataPtr->gmtSetupTZData != NULL) { return dataPtr->gmtSetupTZData; } out = &dataPtr->gmtSetupTZData; } else if ( timezoneObj == dataPtr->prevSetupTimeZone || timezoneObj == dataPtr->prevSetupTimeZoneUnnorm ) { if (dataPtr->prevSetupTZData != NULL) { return dataPtr->prevSetupTZData; } out = &dataPtr->prevSetupTZData; } ret = Tcl_ObjGetVar2(interp, dataPtr->literals[LIT_TZDATA], timezoneObj, TCL_LEAVE_ERR_MSG); /* cache using corresponding slot and as last used */ if (out != NULL) { TclSetObjRef(*out, ret); } else if (dataPtr->lastSetupTimeZone != timezoneObj) { SavePrevTimezoneObj(dataPtr); TclSetObjRef(dataPtr->lastSetupTimeZone, timezoneObj); TclUnsetObjRef(dataPtr->lastSetupTimeZoneUnnorm); TclSetObjRef(dataPtr->lastSetupTZData, ret); } 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( void *clientData, /* Opaque pointer to literal pool, etc. */ Tcl_Interp *interp) /* Tcl interpreter */ { ClockClientData *dataPtr = (ClockClientData *)clientData; /* if known (cached and same epoch) - return now */ if (dataPtr->systemTimeZone != NULL && dataPtr->lastTZEpoch == TzsetIfNecessary()) { return dataPtr->systemTimeZone; } TclUnsetObjRef(dataPtr->systemTimeZone); TclUnsetObjRef(dataPtr->systemSetupTZData); if (Tcl_EvalObjv(interp, 1, &dataPtr->literals[LIT_GETSYSTEMTIMEZONE], 0) != TCL_OK) { return NULL; } if (dataPtr->systemTimeZone == NULL) { TclSetObjRef(dataPtr->systemTimeZone, Tcl_GetObjResult(interp)); } Tcl_ResetResult(interp); return dataPtr->systemTimeZone; } /* *---------------------------------------------------------------------- * * ClockSetupTimeZone -- * * Sets up the timezone. Loads tzdata, etc. * * Results: * Returns normalized timezone object. * *---------------------------------------------------------------------- */ Tcl_Obj * ClockSetupTimeZone( void *clientData, /* Opaque pointer to literal pool, etc. */ Tcl_Interp *interp, /* Tcl interpreter */ Tcl_Obj *timezoneObj) { ClockClientData *dataPtr = (ClockClientData *)clientData; int loaded; Tcl_Obj *callargs[2]; /* if cached (if already setup this one) */ if ( timezoneObj == dataPtr->literals[LIT_GMT] && dataPtr->gmtSetupTZData != NULL ) { return timezoneObj; } if ( ( timezoneObj == dataPtr->lastSetupTimeZone || timezoneObj == dataPtr->lastSetupTimeZoneUnnorm ) && dataPtr->lastSetupTimeZone != NULL ) { return dataPtr->lastSetupTimeZone; } if ( ( timezoneObj == dataPtr->prevSetupTimeZone || timezoneObj == dataPtr->prevSetupTimeZoneUnnorm ) && dataPtr->prevSetupTimeZone != NULL ) { return dataPtr->prevSetupTimeZone; } /* differentiate normalized (last, GMT and system) zones, because used often and already set */ callargs[1] = NormTimezoneObj(dataPtr, timezoneObj, &loaded); /* if loaded (setup already called for this TZ) */ if (loaded) { return callargs[1]; } /* before setup just take a look in TZData variable */ if (Tcl_ObjGetVar2(interp, dataPtr->literals[LIT_TZDATA], timezoneObj, 0)) { /* put it to last slot and return normalized */ TimezoneLoaded(dataPtr, callargs[1], timezoneObj); return callargs[1]; } /* setup now */ callargs[0] = dataPtr->literals[LIT_SETUPTIMEZONE]; if (Tcl_EvalObjv(interp, 2, callargs, 0) == TCL_OK) { /* save unnormalized last used */ TclSetObjRef(dataPtr->lastSetupTimeZoneUnnorm, timezoneObj); return callargs[1]; } 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 buf[12+1], *p; if ( z < 0 ) { z = -z; *buf = '-'; } else { *buf = '+'; } TclItoAw(buf+1, z / 3600, '0', 2); z %= 3600; p = TclItoAw(buf+3, z / 60, '0', 2); z %= 60; if (z != 0) { p = TclItoAw(buf+5, z, '0', 2); } return Tcl_NewStringObj(buf, p - buf); } /* *---------------------------------------------------------------------- * * ClockConvertlocaltoutcObjCmd -- * * Tcl command that converts a UTC time to a local time by whatever means * is available. * * Usage: * ::tcl::clock::ConvertUTCToLocal dictionary timezone changeover * * Parameters: * dict - Dictionary containing a 'localSeconds' entry. * timezone - Time zone * changeover - Julian Day of the adoption of the Gregorian calendar. * * Results: * Returns a standard Tcl result. * * Side effects: * On success, sets the interpreter result to the given dictionary |
︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 | Tcl_Obj *secondsObj; Tcl_Obj *dict; int changeover; TclDateFields fields; int created = 0; int status; /* * Check params and convert time. */ if (objc != 4) { | > | | | 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 | Tcl_Obj *secondsObj; Tcl_Obj *dict; int changeover; TclDateFields fields; int created = 0; int status; fields.tzName = NULL; /* * Check params and convert time. */ if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "dict timezone changeover"); return TCL_ERROR; } dict = objv[1]; if (Tcl_DictObjGet(interp, dict, data->literals[LIT_LOCALSECONDS], &secondsObj)!= TCL_OK) { return TCL_ERROR; } if (secondsObj == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj("key \"localseconds\" not " "found in dictionary", -1)); return TCL_ERROR; } if ((TclGetWideIntFromObj(interp, secondsObj, &fields.localSeconds) != TCL_OK) || (TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK) || ConvertLocalToUTC(clientData, interp, &fields, objv[2], changeover)) { return TCL_ERROR; } /* * Copy-on-write; set the 'seconds' field in the dictionary and place the * modified dictionary in the interpreter result. */ |
︙ | ︙ | |||
372 373 374 375 376 377 378 | * * ClockGetdatefieldsObjCmd -- * * Tcl command that determines the values that [clock format] will use in * formatting a date, and populates a dictionary with them. * * Usage: | | | < | 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 | * * ClockGetdatefieldsObjCmd -- * * Tcl command that determines the values that [clock format] will use in * formatting a date, and populates a dictionary with them. * * Usage: * ::tcl::clock::GetDateFields seconds timezone changeover * * Parameters: * seconds - Time expressed in seconds from the Posix epoch. * timezone - Time zone in which time is to be expressed. * changeover - Julian Day Number at which the current locale adopted * the Gregorian calendar * * Results: * Returns a dictonary populated with the fields: * seconds - Seconds from the Posix epoch * localSeconds - Nominal seconds from the Posix epoch in the |
︙ | ︙ | |||
406 407 408 409 410 411 412 413 414 415 416 417 | { TclDateFields fields; Tcl_Obj *dict; ClockClientData *data = (ClockClientData *)clientData; Tcl_Obj *const *lit = data->literals; int changeover; /* * Check params. */ if (objc != 4) { | > > | | < < > | < < < < | < < < < < < < < < < < | 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 | { TclDateFields fields; Tcl_Obj *dict; ClockClientData *data = (ClockClientData *)clientData; Tcl_Obj *const *lit = data->literals; int changeover; fields.tzName = NULL; /* * Check params. */ if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "seconds timezone changeover"); return TCL_ERROR; } if (TclGetWideIntFromObj(interp, objv[1], &fields.seconds) != TCL_OK || TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK) { return TCL_ERROR; } /* * fields.seconds could be an unsigned number that overflowed. Make sure * that it isn't. */ if (TclHasInternalRep(objv[1], &tclBignumType)) { Tcl_SetObjResult(interp, lit[LIT_INTEGER_VALUE_TOO_LARGE]); return TCL_ERROR; } /* Extract fields */ if (ClockGetDateFields(clientData, interp, &fields, objv[2], changeover) != TCL_OK) { return TCL_ERROR; } /* Make dict of fields */ dict = Tcl_NewDictObj(); Tcl_DictObjPut(NULL, dict, lit[LIT_LOCALSECONDS], Tcl_NewWideIntObj(fields.localSeconds)); Tcl_DictObjPut(NULL, dict, lit[LIT_SECONDS], Tcl_NewWideIntObj(fields.seconds)); Tcl_DictObjPut(NULL, dict, lit[LIT_TZNAME], fields.tzName); |
︙ | ︙ | |||
485 486 487 488 489 490 491 492 493 494 495 496 497 498 | Tcl_NewWideIntObj(fields.iso8601Year)); Tcl_DictObjPut(NULL, dict, lit[LIT_ISO8601WEEK], Tcl_NewWideIntObj(fields.iso8601Week)); Tcl_DictObjPut(NULL, dict, lit[LIT_DAYOFWEEK], Tcl_NewWideIntObj(fields.dayOfWeek)); Tcl_SetObjResult(interp, dict); return TCL_OK; } /* *---------------------------------------------------------------------- * * ClockGetjuliandayfromerayearmonthdayObjCmd -- | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 | Tcl_NewWideIntObj(fields.iso8601Year)); Tcl_DictObjPut(NULL, dict, lit[LIT_ISO8601WEEK], Tcl_NewWideIntObj(fields.iso8601Week)); Tcl_DictObjPut(NULL, dict, lit[LIT_DAYOFWEEK], Tcl_NewWideIntObj(fields.dayOfWeek)); Tcl_SetObjResult(interp, dict); return TCL_OK; } /* *---------------------------------------------------------------------- * * 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( void *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 and seconds of the day. */ ClockExtractJDAndSODFromSeconds(fields->julianDay, fields->secondOfDay, fields->localSeconds); /* * Convert to Julian or Gregorian calendar. */ GetGregorianEraYearDay(fields, changeover); GetMonthDay(fields); GetYearWeekDay(fields, changeover); return TCL_OK; } /* *---------------------------------------------------------------------- * * ClockGetjuliandayfromerayearmonthdayObjCmd -- |
︙ | ︙ | |||
564 565 566 567 568 569 570 571 572 573 574 575 576 577 | Tcl_Obj *dict; ClockClientData *data = (ClockClientData *)clientData; Tcl_Obj *const *lit = data->literals; int changeover; int copied = 0; int status; int isBce = 0; /* * Check params. */ if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "dict changeover"); | > > | 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 | Tcl_Obj *dict; ClockClientData *data = (ClockClientData *)clientData; Tcl_Obj *const *lit = data->literals; int changeover; int copied = 0; int status; int isBce = 0; fields.tzName = NULL; /* * Check params. */ if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "dict changeover"); |
︙ | ︙ | |||
649 650 651 652 653 654 655 656 657 658 659 660 661 662 | ClockClientData *data = (ClockClientData *)clientData; Tcl_Obj *const *lit = data->literals; int changeover; int copied = 0; int status; int isBce = 0; /* * Check params. */ if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "dict changeover"); return TCL_ERROR; | > > | 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 | ClockClientData *data = (ClockClientData *)clientData; Tcl_Obj *const *lit = data->literals; int changeover; int copied = 0; int status; int isBce = 0; fields.tzName = NULL; /* * Check params. */ if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "dict changeover"); return TCL_ERROR; |
︙ | ︙ | |||
716 717 718 719 720 721 722 723 724 | * in the interpreter result on failure. * *---------------------------------------------------------------------- */ static int ConvertLocalToUTC( Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 | * in the interpreter result on failure. * *---------------------------------------------------------------------- */ static int ConvertLocalToUTC( void *clientData, /* Client data of the interpreter */ Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ Tcl_Obj *timezoneObj, /* Time zone */ int changeover) /* Julian Day of the Gregorian transition */ { ClockClientData *dataPtr = (ClockClientData *)clientData; Tcl_Obj *tzdata; /* Time zone data */ Tcl_Size rowc; /* Number of rows in tzdata */ Tcl_Obj **rowv; /* Pointers to the rows */ Tcl_WideInt seconds; ClockLastTZOffs * ltzoc = NULL; /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */ if (timezoneObj == dataPtr->literals[LIT_GMT]) { fields->seconds = fields->localSeconds; fields->tzOffset = 0; return TCL_OK; } /* * Check cacheable conversion could be used * (last-period UTC2Local cache within the same TZ and seconds) */ for (rowc = 0; rowc < 2; rowc++) { ltzoc = &dataPtr->lastTZOffsCache[rowc]; if (timezoneObj != ltzoc->timezoneObj || changeover != ltzoc->changeover) { ltzoc = NULL; continue; } seconds = fields->localSeconds - ltzoc->tzOffset; if ( seconds >= ltzoc->rangesVal[0] && seconds < ltzoc->rangesVal[1] ) { /* the same time zone and offset (UTC time inside the last minute) */ fields->tzOffset = ltzoc->tzOffset; fields->seconds = seconds; return TCL_OK; } /* in the DST-hole (because of the check above) - correct localSeconds */ if (fields->localSeconds == ltzoc->localSeconds) { /* the same time zone and offset (but we'll shift local-time) */ fields->tzOffset = ltzoc->tzOffset; fields->seconds = seconds; goto dstHole; } } /* * 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; } /* * Special case: If the time zone is :localtime, the tzdata will be empty. * Use 'mktime' to convert the time to local */ if (rowc == 0) { if (ConvertLocalToUTCUsingC(interp, fields, changeover) != TCL_OK) { return TCL_ERROR; }; /* we cannot cache (ranges unknown yet) - todo: check later the DST-hole here */ return TCL_OK; } else { Tcl_WideInt rangesVal[2]; if (ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv, rangesVal) != TCL_OK) { return TCL_ERROR; }; seconds = fields->seconds; /* Cache the last conversion */ if (ltzoc != NULL) { /* slot was found above */ /* timezoneObj and changeover are the same */ TclSetObjRef(ltzoc->tzName, fields->tzName); /* may be NULL */ } else { /* no TZ in cache - just move second slot down and use the first one */ ltzoc = &dataPtr->lastTZOffsCache[0]; TclUnsetObjRef(dataPtr->lastTZOffsCache[1].timezoneObj); TclUnsetObjRef(dataPtr->lastTZOffsCache[1].tzName); memcpy(&dataPtr->lastTZOffsCache[1], ltzoc, sizeof(*ltzoc)); TclInitObjRef(ltzoc->timezoneObj, timezoneObj); ltzoc->changeover = changeover; TclInitObjRef(ltzoc->tzName, fields->tzName); /* may be NULL */ } ltzoc->localSeconds = fields->localSeconds; ltzoc->rangesVal[0] = rangesVal[0]; ltzoc->rangesVal[1] = rangesVal[1]; ltzoc->tzOffset = fields->tzOffset; } /* check DST-hole: if retrieved seconds is out of range */ if ( ltzoc->rangesVal[0] > seconds || seconds >= ltzoc->rangesVal[1] ) { dstHole: #if 0 printf("given local-time is outside the time-zone (in DST-hole): " "%d - offs %d => %d <= %d < %d\n", (int)fields->localSeconds, fields->tzOffset, (int)ltzoc->rangesVal[0], (int)seconds, (int)ltzoc->rangesVal[1]); #endif /* because we don't know real TZ (we're outsize), just invalidate local * time (which could be verified in ClockValidDate later) */ fields->localSeconds = TCL_INV_SECONDS; /* not valid seconds */ } return TCL_OK; } /* *---------------------------------------------------------------------- * * ConvertLocalToUTCUsingTable -- * |
︙ | ︙ | |||
766 767 768 769 770 771 772 | *---------------------------------------------------------------------- */ static int ConvertLocalToUTCUsingTable( Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Time to convert, with 'seconds' filled in */ | | | > > > > | | < < | | > < | | | < < | | | > | < > > | > > | 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 | *---------------------------------------------------------------------- */ static int 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) /* Return bounds for time period */ { Tcl_Obj *row; Tcl_Size cellc; Tcl_Obj **cellv; struct { Tcl_Obj *tzName; int tzOffset; } have[8]; int nHave = 0; Tcl_Size i; /* * Perform an initial lookup assuming that local == UTC, and locate the * last time conversion prior to that time. Get the offset from that row, * and look up again. Continue until we find an offset that we found * before. This definition, rather than "the same offset" ensures that we * don't enter an endless loop, as would otherwise happen when trying to * convert a non-existent time such as 02:30 during the US Spring Daylight * Saving Time transition. */ fields->tzOffset = 0; fields->seconds = fields->localSeconds; while (1) { row = LookupLastTransition(interp, fields->seconds, rowc, rowv, rangesVal); if ((row == NULL) || TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK || TclGetIntFromObj(interp, cellv[1], &fields->tzOffset) != TCL_OK) { return TCL_ERROR; } for (i = 0; i < nHave; ++i) { if (have[i].tzOffset == fields->tzOffset) { goto found; } } if (nHave == 8) { Tcl_Panic("loop in ConvertLocalToUTCUsingTable"); } have[nHave].tzName = cellv[3]; have[nHave++].tzOffset = fields->tzOffset; fields->seconds = fields->localSeconds - fields->tzOffset; } found: fields->tzOffset = have[i].tzOffset; fields->seconds = fields->localSeconds - fields->tzOffset; TclSetObjRef(fields->tzName, have[i].tzName); return TCL_OK; } /* *---------------------------------------------------------------------- * * ConvertLocalToUTCUsingC -- |
︙ | ︙ | |||
846 847 848 849 850 851 852 | Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Time to convert, with 'seconds' filled in */ int changeover) /* Julian Day of the Gregorian transition */ { struct tm timeVal; int localErrno; int secondOfDay; | < < | < < < | | | 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 | Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Time to convert, with 'seconds' filled in */ int changeover) /* Julian Day of the Gregorian transition */ { struct tm timeVal; int localErrno; int secondOfDay; /* * Convert the given time to a date. */ ClockExtractJDAndSODFromSeconds(fields->julianDay, secondOfDay, fields->localSeconds); GetGregorianEraYearDay(fields, changeover); GetMonthDay(fields); /* * Convert the date/time to a 'struct tm'. */ |
︙ | ︙ | |||
917 918 919 920 921 922 923 | * * Side effects: * Populates the 'tzName' and 'tzOffset' fields. * *---------------------------------------------------------------------- */ | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > | 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 | * * Side effects: * Populates the 'tzName' and 'tzOffset' fields. * *---------------------------------------------------------------------- */ int ConvertUTCToLocal( void *clientData, /* Client data of the interpreter */ Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ Tcl_Obj *timezoneObj, /* Time zone */ int changeover) /* Julian Day of the Gregorian transition */ { ClockClientData *dataPtr = (ClockClientData *)clientData; Tcl_Obj *tzdata; /* Time zone data */ Tcl_Size rowc; /* Number of rows in tzdata */ Tcl_Obj **rowv; /* Pointers to the rows */ ClockLastTZOffs * ltzoc = NULL; /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */ if (timezoneObj == dataPtr->literals[LIT_GMT]) { fields->localSeconds = fields->seconds; fields->tzOffset = 0; if (dataPtr->gmtTZName == NULL) { Tcl_Obj *tzName; tzdata = ClockGetTZData(clientData, interp, timezoneObj); if ( TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK || Tcl_ListObjIndex(interp, rowv[0], 3, &tzName) != TCL_OK) { return TCL_ERROR; } TclSetObjRef(dataPtr->gmtTZName, tzName); } TclSetObjRef(fields->tzName, dataPtr->gmtTZName); return TCL_OK; } /* * Check cacheable conversion could be used * (last-period UTC2Local cache within the same TZ and seconds) */ for (rowc = 0; rowc < 2; rowc++) { ltzoc = &dataPtr->lastTZOffsCache[rowc]; if (timezoneObj != ltzoc->timezoneObj || changeover != ltzoc->changeover) { ltzoc = NULL; continue; } if ( fields->seconds >= ltzoc->rangesVal[0] && fields->seconds < ltzoc->rangesVal[1] ) { /* the same time zone and offset (UTC time inside the last minute) */ fields->tzOffset = ltzoc->tzOffset; fields->localSeconds = fields->seconds + fields->tzOffset; TclSetObjRef(fields->tzName, ltzoc->tzName); 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; } /* * Special case: If the time zone is :localtime, the tzdata will be empty. * Use 'localtime' to convert the time to local */ if (rowc == 0) { if (ConvertUTCToLocalUsingC(interp, fields, changeover) != TCL_OK) { return TCL_ERROR; } /* signal we need to revalidate TZ epoch next time fields gets used. */ fields->flags |= CLF_CTZ; /* we cannot cache (ranges unknown yet) */ } else { Tcl_WideInt rangesVal[2]; if (ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv, rangesVal) != TCL_OK) { return TCL_ERROR; } /* converted using table (TZ isn't :localtime) */ fields->flags &= ~CLF_CTZ; /* Cache the last conversion */ if (ltzoc != NULL) { /* slot was found above */ /* timezoneObj and changeover are the same */ TclSetObjRef(ltzoc->tzName, fields->tzName); } else { /* no TZ in cache - just move second slot down and use the first one */ ltzoc = &dataPtr->lastTZOffsCache[0]; TclUnsetObjRef(dataPtr->lastTZOffsCache[1].timezoneObj); TclUnsetObjRef(dataPtr->lastTZOffsCache[1].tzName); memcpy(&dataPtr->lastTZOffsCache[1], ltzoc, sizeof(*ltzoc)); TclInitObjRef(ltzoc->timezoneObj, timezoneObj); ltzoc->changeover = changeover; TclInitObjRef(ltzoc->tzName, fields->tzName); } ltzoc->localSeconds = fields->localSeconds; ltzoc->rangesVal[0] = rangesVal[0]; ltzoc->rangesVal[1] = rangesVal[1]; ltzoc->tzOffset = fields->tzOffset; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ConvertUTCToLocalUsingTable -- * |
︙ | ︙ | |||
971 972 973 974 975 976 977 | static int ConvertUTCToLocalUsingTable( Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the date */ Tcl_Size rowc, /* Number of rows in the conversion table * (>= 1) */ | | > | | < | 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 | static int ConvertUTCToLocalUsingTable( Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the date */ Tcl_Size rowc, /* Number of rows in the conversion table * (>= 1) */ Tcl_Obj *const rowv[], /* Rows of the conversion table */ Tcl_WideInt *rangesVal) /* Return bounds for time period */ { Tcl_Obj *row; /* Row containing the current information */ Tcl_Size cellc; /* Count of cells in the row (must be 4) */ Tcl_Obj **cellv; /* Pointers to the cells */ /* * Look up the nearest transition time. */ row = LookupLastTransition(interp, fields->seconds, rowc, rowv, rangesVal); if (row == NULL || TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK || TclGetIntFromObj(interp, cellv[1], &fields->tzOffset) != TCL_OK) { return TCL_ERROR; } /* * Convert the time. */ TclSetObjRef(fields->tzName, cellv[3]); fields->localSeconds = fields->seconds + fields->tzOffset; return TCL_OK; } /* *---------------------------------------------------------------------- * |
︙ | ︙ | |||
1026 1027 1028 1029 1030 1031 1032 | Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Time to convert, with 'seconds' filled in */ int changeover) /* Julian Day of the Gregorian transition */ { time_t tock; struct tm *timeVal; /* Time after conversion */ int diff; /* Time zone diff local-Greenwich */ | | | 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 | Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Time to convert, with 'seconds' filled in */ int changeover) /* Julian Day of the Gregorian transition */ { time_t tock; struct tm *timeVal; /* Time after conversion */ int diff; /* Time zone diff local-Greenwich */ char buffer[16], *p; /* Buffer for time zone name */ /* * Use 'localtime' to determine local year, month, day, time of day. */ tock = (time_t) fields->seconds; if ((Tcl_WideInt) tock != fields->seconds) { |
︙ | ︙ | |||
1063 1064 1065 1066 1067 1068 1069 | fields->dayOfMonth = timeVal->tm_mday; GetJulianDayFromEraYearMonthDay(fields, changeover); /* * Convert that value to seconds. */ | | < | < | | | | < | | > | | | > > > > > > > > > > > | 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 | fields->dayOfMonth = timeVal->tm_mday; GetJulianDayFromEraYearMonthDay(fields, changeover); /* * Convert that value to seconds. */ fields->localSeconds = (((fields->julianDay * 24LL + timeVal->tm_hour) * 60 + timeVal->tm_min) * 60 + timeVal->tm_sec) - JULIAN_SEC_POSIX_EPOCH; /* * Determine a time zone offset and name; just use +hhmm for the name. */ diff = (int) (fields->localSeconds - fields->seconds); fields->tzOffset = diff; if (diff < 0) { *buffer = '-'; diff = -diff; } else { *buffer = '+'; } TclItoAw(buffer+1, diff / 3600, '0', 2); diff %= 3600; p = TclItoAw(buffer+3, diff / 60, '0', 2); diff %= 60; if (diff != 0) { p = TclItoAw(buffer+5, diff, '0', 2); } TclSetObjRef(fields->tzName, Tcl_NewStringObj(buffer, p - buffer)); return TCL_OK; } /* *---------------------------------------------------------------------- * * LookupLastTransition -- * * Given a UTC time and a tzdata array, looks up the last transition on * or before the given time. * * Results: * Returns a pointer to the row, or NULL if an error occurs. * *---------------------------------------------------------------------- */ Tcl_Obj * LookupLastTransition( Tcl_Interp *interp, /* Interpreter for error messages */ Tcl_WideInt tick, /* Time from the epoch */ Tcl_Size rowc, /* Number of rows of tzdata */ Tcl_Obj *const *rowv, /* Rows in tzdata */ Tcl_WideInt *rangesVal) /* Return bounds for time period */ { Tcl_Size l, u; Tcl_Obj *compObj; Tcl_WideInt compVal, fromVal = LLONG_MIN, toVal = LLONG_MAX; /* * 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) { return NULL; } /* * Bizarre case - first row doesn't begin at MIN_WIDE_INT. Return it * anyway. */ if (tick < (fromVal = compVal)) { if (rangesVal) { rangesVal[0] = fromVal; rangesVal[1] = toVal; } return rowv[0]; } /* * Binary-search to find the transition. */ l = 0; u = rowc-1; while (l < u) { Tcl_Size m = (l + u + 1) / 2; if (Tcl_ListObjIndex(interp, rowv[m], 0, &compObj) != TCL_OK || TclGetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { return NULL; } if (tick >= compVal) { l = m; fromVal = compVal; } else { u = m-1; toVal = compVal; } } if (rangesVal) { rangesVal[0] = fromVal; rangesVal[1] = toVal; } return rowv[l]; } /* *---------------------------------------------------------------------- * * GetYearWeekDay -- |
︙ | ︙ | |||
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 | GetYearWeekDay( TclDateFields *fields, /* Date to convert, must have 'julianDay' */ int changeover) /* Julian Day Number of the Gregorian * transition */ { TclDateFields temp; int dayOfFiscalYear; /* * 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. */ temp.julianDay = fields->julianDay - 3; | > > | 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 | GetYearWeekDay( TclDateFields *fields, /* Date to convert, must have 'julianDay' */ int changeover) /* Julian Day Number of the Gregorian * transition */ { 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. */ temp.julianDay = fields->julianDay - 3; |
︙ | ︙ | |||
1405 1406 1407 1408 1409 1410 1411 | * * Side effects: * Stores 'julianDay' in the fields. * *---------------------------------------------------------------------- */ | | > > | 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 | * * Side effects: * Stores 'julianDay' in the fields. * *---------------------------------------------------------------------- */ void GetJulianDayFromEraYearWeekDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Julian Day Number of the Gregorian * transition */ { Tcl_WideInt firstMonday; /* Julian day number of week 1, day 1 in the * given year */ TclDateFields firstWeek; firstWeek.tzName = NULL; /* * Find January 4 in the ISO8601 year, which will always be in week 1. */ firstWeek.isBce = fields->isBce; firstWeek.year = fields->iso8601Year; |
︙ | ︙ | |||
1456 1457 1458 1459 1460 1461 1462 | * * Side effects: * Stores day number in 'julianDay' * *---------------------------------------------------------------------- */ | | | 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 | * * Side effects: * Stores day number in 'julianDay' * *---------------------------------------------------------------------- */ void GetJulianDayFromEraYearMonthDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Gregorian transition date as a Julian Day */ { Tcl_WideInt year, ym1, ym1o4, ym1o100, ym1o400; int month, mm1, q, r; |
︙ | ︙ | |||
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 | + ym1o4; } } /* *---------------------------------------------------------------------- * * IsGregorianLeapYear -- * * Tests whether a given year is a leap year, in either Julian or * Gregorian calendar. * * Results: * Returns 1 for a leap year, 0 otherwise. * *---------------------------------------------------------------------- */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 | + ym1o4; } } /* *---------------------------------------------------------------------- * * 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' * *---------------------------------------------------------------------- */ void GetJulianDayFromEraYearDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Gregorian transition date as a Julian Day */ { Tcl_WideInt year, ym1; /* Get absolute year number from the civil year */ if (fields->isBce) { 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 * Gregorian calendar. * * Results: * Returns 1 for a leap year, 0 otherwise. * *---------------------------------------------------------------------- */ int IsGregorianLeapYear( TclDateFields *fields) /* Date to test */ { Tcl_WideInt year = fields->year; if (fields->isBce) { year = 1 - year; |
︙ | ︙ | |||
1763 1764 1765 1766 1767 1768 1769 | case 2: if (Tcl_GetIndexFromObj(interp, objv[1], clicksSwitches, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } break; default: | | | | 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 | case 2: if (Tcl_GetIndexFromObj(interp, objv[1], clicksSwitches, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } break; default: Tcl_WrongNumArgs(interp, 0, objv, "clock clicks ?-switch?"); return TCL_ERROR; } switch (index) { case CLICKS_MILLIS: Tcl_GetTime(&now); clicks = now.sec * 1000LL + now.usec / 1000; break; case CLICKS_NATIVE: #ifdef TCL_WIDE_CLICKS clicks = TclpGetWideClicks(); #else clicks = (Tcl_WideInt)TclpGetClicks(); #endif |
︙ | ︙ | |||
1817 1818 1819 1820 1821 1822 1823 | int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { Tcl_Time now; Tcl_Obj *timeObj; if (objc != 1) { | | | 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 | int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { Tcl_Time now; Tcl_Obj *timeObj; if (objc != 1) { Tcl_WrongNumArgs(interp, 0, objv, "clock milliseconds"); return TCL_ERROR; } Tcl_GetTime(&now); TclNewUIntObj(timeObj, (Tcl_WideUInt) now.sec * 1000 + now.usec / 1000); Tcl_SetObjResult(interp, timeObj); return TCL_OK; |
︙ | ︙ | |||
1853 1854 1855 1856 1857 1858 1859 | ClockMicrosecondsObjCmd( TCL_UNUSED(void *), Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { if (objc != 1) { | | > > > > > > > > > > > | | > > > > | | < < < < | < < < < < > > | > > > > > > > > > > > | < < < < < | | | | | < | < < < | < < | | | > > > < < < > > > > > > > > | | | > > > > > > > > > | < > > | > > > | | | | | | > > > > > > > > > > | > > > > > > > > | < < < | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > | > > | > | > > > | > > | 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 | ClockMicrosecondsObjCmd( TCL_UNUSED(void *), Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { if (objc != 1) { Tcl_WrongNumArgs(interp, 0, objv, "clock microseconds"); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetMicroseconds())); return TCL_OK; } static inline void ClockInitFmtScnArgs( void *clientData, Tcl_Interp *interp, ClockFmtScnCmdArgs *opts) { memset(opts, 0, sizeof(*opts)); opts->clientData = clientData; opts->interp = interp; } /* *----------------------------------------------------------------------------- * * ClockParseFmtScnArgs -- * * Parses the arguments for sub-commands "scan", "format" and "add". * * Note: common options table used here, because for the options often used * the same literals (objects), so it avoids permanent "recompiling" of * option object representation to indexType with another table. * * Results: * Returns a standard Tcl result, and stores parsed options * (format, the locale, timezone and base) in structure "opts". * *----------------------------------------------------------------------------- */ #define CLC_FMT_ARGS (0) #define CLC_SCN_ARGS (1 << 0) #define CLC_ADD_ARGS (1 << 1) static int ClockParseFmtScnArgs( 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 */ const char *syntax /* Syntax of the current command */ ) { Tcl_Interp *interp = opts->interp; ClockClientData *dataPtr = (ClockClientData *)opts->clientData; int gmtFlag = 0; static const char *const options[] = { "-base", "-format", "-gmt", "-locale", "-timezone", "-validate", NULL }; enum optionInd { CLC_ARGS_BASE, CLC_ARGS_FORMAT, CLC_ARGS_GMT, CLC_ARGS_LOCALE, CLC_ARGS_TIMEZONE, CLC_ARGS_VALIDATE }; int optionIndex; /* Index of an option. */ int saw = 0; /* Flag == 1 if option was seen already. */ int i; Tcl_WideInt baseVal; /* Base time, expressed in seconds from the Epoch */ if ( flags & (CLC_SCN_ARGS) ) { /* default flags (from configure) */ opts->flags |= dataPtr->defFlags & (CLF_VALIDATE); } else { /* clock value (as current base) */ opts->baseObj = objv[1]; saw |= (1 << CLC_ARGS_BASE); } /* * Extract values for the keywords. */ 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 badOptionMsg; } /* if already specified */ if (saw & (1 << optionIndex)) { if ( !(flags & CLC_SCN_ARGS) && optionIndex == CLC_ARGS_BASE) { goto badOptionMsg; } Tcl_SetObjResult(interp, Tcl_ObjPrintf( "bad option \"%s\": doubly present", TclGetString(objv[i])) ); goto badOption; } switch (optionIndex) { case CLC_ARGS_FORMAT: if (flags & CLC_ADD_ARGS) { goto badOptionMsg; } opts->formatObj = objv[i+1]; break; case CLC_ARGS_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]; break; case CLC_ARGS_BASE: opts->baseObj = objv[i+1]; break; case CLC_ARGS_VALIDATE: if ( !(flags & CLC_SCN_ARGS) ) { goto badOptionMsg; } else { int val; if (Tcl_GetBooleanFromObj(interp, objv[i+1], &val) != TCL_OK) { return TCL_ERROR; } if (val) { opts->flags |= CLF_VALIDATE; } else { opts->flags &= ~CLF_VALIDATE; } } break; } 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", (char *)NULL); return TCL_ERROR; } if (gmtFlag) { opts->timezoneObj = dataPtr->literals[LIT_GMT]; } else /* 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) { 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", (char *)NULL); i = 1; goto badOption; } /* * Seconds could be an unsigned number that overflowed. Make sure * that it isn't. Additionally it may be too complex to calculate * julianday etc (forwards/backwards) by too large/small values, thus * just let accept a bit shorter values to avoid overflow. * Note the year is currently an integer, thus avoid to overflow it also. */ if ( baseObj->typePtr == &tclBignumType || baseVal < TCL_MIN_SECONDS || baseVal > TCL_MAX_SECONDS ) { 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 && (!(dataPtr->lastBase.date.flags & CLF_CTZ) || dataPtr->lastTZEpoch == TzsetIfNecessary()) ) { 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); TclSetObjRef(dataPtr->lastBase.timezoneObj, opts->timezoneObj); } return TCL_OK; badOptionMsg: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "bad option \"%s\": must be %s", TclGetString(objv[i]), syntax) ); badOption: Tcl_SetErrorCode(interp, "CLOCK", "badOption", (i < objc) ? Tcl_GetString(objv[i]) : (char *)NULL, (char *)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( void *clientData, /* Client data containing literal pool */ Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Parameter count */ Tcl_Obj *const objv[]) /* Parameter values */ { ClockClientData *dataPtr = (ClockClientData *)clientData; static const char *syntax = "clock format clockval|-now " "?-format string? " "?-gmt boolean? " "?-locale LOCALE? ?-timezone ZONE?"; 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, objv, syntax); Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", (char *)NULL); 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, "-format, -gmt, -locale, or -timezone"); 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: TclUnsetObjRef(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( void *clientData, /* Client data containing literal pool */ Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Parameter count */ Tcl_Obj *const objv[]) /* Parameter values */ { static const char *syntax = "clock scan string " "?-base seconds? " "?-format string? " "?-gmt boolean? " "?-locale LOCALE? ?-timezone ZONE? ?-validate boolean?"; 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, objv, syntax); Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", (char *)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, "-base, -format, -gmt, -locale, -timezone or -validate"); if (ret != TCL_OK) { goto done; } /* seconds are in localSeconds (relative base date), so reset time here */ yyHour = yyMinutes = yySeconds = yySecondOfDay = 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", (char *)NULL); ret = TCL_ERROR; goto done; } ret = ClockFreeScan(&yy, objv[1], &opts); } else { /* Use compiled version of Scan - */ ret = ClockScan(&yy, objv[1], &opts); } if (ret != TCL_OK) { goto done; } /* * If no GMT and not free-scan (where valid stage 1 is done in-between), * validate with stage 1 before local time conversion, otherwise it may * adjust date/time tokens to valid values */ if ( (opts.flags & CLF_VALIDATE_S1) && info->flags & (CLF_ASSEMBLE_SECONDS|CLF_LOCALSEC) ) { ret = ClockValidDate(&yy, &opts, CLF_VALIDATE_S1); if (ret != TCL_OK) { goto done; } } /* Convert date info structure into UTC seconds */ ret = ClockScanCommit(&yy, &opts); if (ret != TCL_OK) { goto done; } /* Apply remaining validation rules, if expected */ if ( (opts.flags & CLF_VALIDATE) ) { ret = ClockValidDate(&yy, &opts, opts.flags & CLF_VALIDATE); if (ret != TCL_OK) { goto done; } } done: TclUnsetObjRef(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( DateInfo *info, /* Clock scan info structure */ 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_ISO8601WEAK)) { GetJulianDayFromEraYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); } else if ( !(info->flags & CLF_DAYOFYEAR) /* no day of year */ || (info->flags & (CLF_DAYOFMONTH|CLF_MONTH)) /* yymmdd over yyddd */ == (CLF_DAYOFMONTH|CLF_MONTH) ) { GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); } else { GetJulianDayFromEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); } info->flags |= CLF_ASSEMBLE_SECONDS; info->flags &= ~CLF_ASSEMBLE_JULIANDAY; } /* some overflow checks */ if (info->flags & CLF_JULIANDAY) { ClockClientData *dataPtr = (ClockClientData *)opts->clientData; double curJDN = (double)yydate.julianDay + ((double)yySecondOfDay - SECONDS_PER_DAY/2) / SECONDS_PER_DAY; if (curJDN > dataPtr->maxJDN) { Tcl_SetObjResult(opts->interp, Tcl_NewStringObj( "requested date too large to represent", -1)); Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", (char *)NULL); return TCL_ERROR; } } /* Local seconds to UTC (stored in yydate.seconds) */ if (info->flags & (CLF_ASSEMBLE_SECONDS)) { yydate.localSeconds = -210866803200LL + ( SECONDS_PER_DAY * yydate.julianDay ) + ( yySecondOfDay % SECONDS_PER_DAY ); } if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_LOCALSEC)) { if (ConvertLocalToUTC(opts->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; } /*---------------------------------------------------------------------- * * ClockValidDate -- * * Validate date info structure for wrong data (e. g. out of ranges). * * Results: * Returns a standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ClockValidDate( DateInfo *info, /* Clock scan info structure */ ClockFmtScnCmdArgs *opts, /* Scan options */ int stage) /* Stage to validate (1, 2 or 3 for both) */ { const char *errMsg = "", *errCode = ""; TclDateFields temp; int tempCpyFlg = 0; ClockClientData *dataPtr = (ClockClientData *)opts->clientData; #if 0 printf("yyMonth %d, yyDay %d, yyDayOfYear %d, yyHour %d, yyMinutes %d, yySeconds %d, " "yySecondOfDay %d, sec %d, daySec %d, tzOffset %d\n", yyMonth, yyDay, yydate.dayOfYear, yyHour, yyMinutes, yySeconds, yySecondOfDay, (int)yydate.localSeconds, (int)(yydate.localSeconds % SECONDS_PER_DAY), yydate.tzOffset); #endif if (!(stage & CLF_VALIDATE_S1) || !(opts->flags & CLF_VALIDATE_S1)) { goto stage_2; } opts->flags &= ~CLF_VALIDATE_S1; /* stage 1 is done */ /* first year (used later in hath / daysInPriorMonths) */ if ((info->flags & (CLF_YEAR|CLF_ISO8601YEAR))) { if ((info->flags & CLF_ISO8601YEAR)) { if ( yydate.iso8601Year < dataPtr->validMinYear || yydate.iso8601Year > dataPtr->validMaxYear ) { errMsg = "invalid iso year"; errCode = "iso year"; goto error; } } if (info->flags & CLF_YEAR) { if ( yyYear < dataPtr->validMinYear || yyYear > dataPtr->validMaxYear ) { errMsg = "invalid year"; errCode = "year"; goto error; } } else if ((info->flags & CLF_ISO8601YEAR)) { yyYear = yydate.iso8601Year; /* used to recognize leap */ } if ((info->flags & (CLF_ISO8601YEAR|CLF_YEAR)) == (CLF_ISO8601YEAR|CLF_YEAR)) { if (yyYear != yydate.iso8601Year) { errMsg = "ambiguous year"; errCode = "year"; goto error; } } } /* and month (used later in hath) */ if (info->flags & CLF_MONTH) { if ( yyMonth < 1 || yyMonth > 12 ) { errMsg = "invalid month"; errCode = "month"; goto error; } } /* day of month */ if (info->flags & (CLF_DAYOFMONTH|CLF_DAYOFWEEK)) { if ( yyDay < 1 || yyDay > 31 ) { errMsg = "invalid day"; errCode = "day"; goto error; } else if ( (info->flags & CLF_MONTH) ) { const int *h = hath[IsGregorianLeapYear(&yydate)]; if ( yyDay > h[yyMonth-1] ) { errMsg = "invalid day"; goto error; } } } if (info->flags & CLF_DAYOFYEAR) { if ( yydate.dayOfYear < 1 || yydate.dayOfYear > daysInPriorMonths[IsGregorianLeapYear(&yydate)][12] ) { errMsg = "invalid day of year"; errCode = "day of year"; goto error; } } /* mmdd !~ ddd */ if ((info->flags & (CLF_DAYOFYEAR|CLF_DAYOFMONTH|CLF_MONTH)) == (CLF_DAYOFYEAR|CLF_DAYOFMONTH|CLF_MONTH)) { if (!tempCpyFlg) { memcpy(&temp, &yydate, sizeof(temp)); tempCpyFlg = 1; } GetJulianDayFromEraYearDay(&temp, GREGORIAN_CHANGE_DATE); if (temp.julianDay != yydate.julianDay) { errMsg = "ambiguous day"; errCode = "day"; goto error; } } if (info->flags & CLF_TIME) { /* hour */ if ( yyHour < 0 || yyHour > ((yyMeridian == MER24) ? 23 : 12) ) { errMsg = "invalid time (hour)"; errCode = "hour"; goto error; } /* minutes */ if ( yyMinutes < 0 || yyMinutes > 59 ) { errMsg = "invalid time (minutes)"; errCode = "minutes"; goto error; } /* oldscan could return secondOfDay (parsedTime) -1 by invalid time (ex.: 25:00:00) */ if ( yySeconds < 0 || yySeconds > 59 || yySecondOfDay <= -1 ) { errMsg = "invalid time"; errCode = "seconds"; goto error; } } if (!(stage & CLF_VALIDATE_S2) || !(opts->flags & CLF_VALIDATE_S2)) { return TCL_OK; } opts->flags &= ~CLF_VALIDATE_S2; /* stage 2 is done */ /* * Further tests expected ready calculated julianDay (inclusive relative), * and time-zone conversion (local to UTC time). */ stage_2: /* time, regarding the modifications by the time-zone (looks for given time * in between DST-time hole, so does not exist in this time-zone) */ if (info->flags & CLF_TIME) { /* * we don't need to do the backwards time-conversion (UTC to local) and * compare results, because the after conversion (local to UTC) we * should have valid localSeconds (was not invalidated to TCL_INV_SECONDS), * so if it was invalidated - invalid time, outside the time-zone (in DST-hole) */ if ( yydate.localSeconds == TCL_INV_SECONDS ) { errMsg = "invalid time (does not exist in this time-zone)"; errCode = "out-of-time"; goto error; } } /* day of week */ if (info->flags & CLF_DAYOFWEEK) { if (!tempCpyFlg) { memcpy(&temp, &yydate, sizeof(temp)); tempCpyFlg = 1; } GetYearWeekDay(&temp, GREGORIAN_CHANGE_DATE); if (temp.dayOfWeek != yyDayOfWeek) { errMsg = "invalid day of week"; errCode = "day of week"; goto error; } } return TCL_OK; error: Tcl_SetObjResult(opts->interp, Tcl_ObjPrintf("unable to convert input string: %s", errMsg)); Tcl_SetErrorCode(opts->interp, "CLOCK", "invInpStr", errCode, (char *)NULL); return TCL_ERROR; } /*---------------------------------------------------------------------- * * ClockFreeScan -- * * Used by ClockScanObjCmd for free scanning without format. * * Results: * Returns a standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ int ClockFreeScan( 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 = (ClockClientData *)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. yySecondOfDay -> info->date.secondOfDay or * yyMonth -> info->date.month (same as yydate.month) */ yyInput = Tcl_GetString(strObj); if (TclClockFreeScan(interp, info) != TCL_OK) { Tcl_Obj *msg; TclNewObj(msg); 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 (info->flags & CLF_YEAR) { if (yyYear < 100) { if (yyYear >= dataPtr->yearOfCenturySwitch) { yyYear -= 100; } yyYear += dataPtr->currentYearCentury; } yydate.isBce = 0; 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 (info->flags & CLF_ZONE) { if (yyTimezone || !yyDSTmode) { /* Real time zone from numeric zone */ 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); } else { /* simplest case - GMT / UTC */ opts->timezoneObj = ClockSetupTimeZone(dataPtr, interp, dataPtr->literals[LIT_GMT]); } if (opts->timezoneObj == NULL) { goto done; } // TclSetObjRef(yydate.tzName, opts->timezoneObj); info->flags |= CLF_ASSEMBLE_SECONDS; } /* * For freescan apply validation rules (stage 1) before mixed with * relative time (otherwise always valid recalculated date & time). */ if ( (opts->flags & CLF_VALIDATE) ) { if (ClockValidDate(info, opts, CLF_VALIDATE_S1) != TCL_OK) { goto done; } } /* * Assemble date, time, zone into seconds-from-epoch */ if ((info->flags & (CLF_TIME|CLF_HAVEDATE)) == CLF_HAVEDATE) { yySecondOfDay = 0; info->flags |= CLF_ASSEMBLE_SECONDS; } else if (info->flags & CLF_TIME) { yySecondOfDay = ToSeconds(yyHour, yyMinutes, yySeconds, yyMeridian); info->flags |= CLF_ASSEMBLE_SECONDS; } else if ( (info->flags & (CLF_DAYOFWEEK|CLF_HAVEDATE)) == CLF_DAYOFWEEK || (info->flags & CLF_ORDINALMONTH) || ( (info->flags & CLF_RELCONV) && ( yyRelMonth != 0 || yyRelDay != 0 ) ) ) { yySecondOfDay = 0; info->flags |= CLF_ASSEMBLE_SECONDS; } else { yySecondOfDay = yydate.localSeconds % SECONDS_PER_DAY; } /* * Do relative times */ ret = ClockCalcRelTime(info); /* 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( DateInfo *info) /* Date fields used for converting */ { int prevDayOfWeek = yyDayOfWeek; /* preserve unchanged day of week */ /* * Because some calculations require in-between conversion of the * julian day, we can repeat this processing multiple times */ repeat_rel: if (info->flags & CLF_RELCONV) { /* * 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; /* compiler fix for negative offs - wrap y, m = (0, -1) -> (-1, 11) */ if (m < 0) { yyYear--; m = 12 + m; } 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) { Tcl_WideInt newSecs = yySecondOfDay + yyRelSeconds; /* if seconds increment outside of current date, increment day */ if (newSecs / SECONDS_PER_DAY != yySecondOfDay / SECONDS_PER_DAY) { yyRelDay += newSecs / SECONDS_PER_DAY; yySecondOfDay = 0; yyRelSeconds = newSecs % SECONDS_PER_DAY; goto repeat_rel; } } info->flags &= ~CLF_RELCONV; } /* * Do relative (ordinal) month */ if (info->flags & CLF_ORDINALMONTH) { 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 */ yyYear += yyMonthOrdinalIncr; yyRelMonth += monthDiff; info->flags &= ~CLF_ORDINALMONTH; info->flags |= CLF_RELCONV|CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; goto repeat_rel; } /* * Do relative weekday */ if ((info->flags & (CLF_DAYOFWEEK|CLF_HAVEDATE)) == CLF_DAYOFWEEK) { /* restore scanned day of week */ yyDayOfWeek = prevDayOfWeek; /* if needed assemble julianDay now */ if (info->flags & CLF_ASSEMBLE_JULIANDAY) { GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); info->flags &= ~CLF_ASSEMBLE_JULIANDAY; } yydate.isBce = 0; yydate.julianDay = WeekdayOnOrBefore(yyDayOfWeek, yydate.julianDay + 6) + 7 * yyDayOrdinal; if (yyDayOrdinal > 0) { yydate.julianDay -= 7; } info->flags |= CLF_ASSEMBLE_DATE|CLF_ASSEMBLE_SECONDS; } 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( int dayOfWeek, int offs) { 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 */ { 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( void *clientData, /* Client data containing literal pool */ Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Parameter count */ Tcl_Obj *const objv[]) /* Parameter values */ { static const char *syntax = "clock add clockval|-now ?number units?..." "?-gmt boolean? " "?-locale LOCALE? ?-timezone ZONE?"; ClockClientData *dataPtr = (ClockClientData *)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, objv, syntax); Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", (char *)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, "-gmt, -locale, or -timezone"); if (ret != TCL_OK) { goto done; } /* time together as seconds of the day */ yySecondOfDay = 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 in ClockParseFmtScnArgs) */ if (TclGetWideIntFromObj(NULL, objv[i], &offs) != TCL_OK) { continue; } /* get unit */ if (Tcl_GetIndexFromObj(interp, objv[i+1], units, "unit", 0, &unitIndex) != TCL_OK) { goto done; } if (objv[i]->typePtr == &tclBignumType || offs > (unitIndex < CLC_ADD_HOURS ? 0x7fffffff : TCL_MAX_SECONDS) || offs < (unitIndex < CLC_ADD_HOURS ? -0x7fffffff : TCL_MIN_SECONDS) ) { Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]); 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 ( (info->flags & CLF_RELCONV) && ( 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) != TCL_OK) { goto done; } } /* process increment by offset + unit */ info->flags |= CLF_RELCONV; 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 (info->flags & CLF_RELCONV) { if (ClockCalcRelTime(info) != TCL_OK) { goto done; } } /* Convert date info structure into UTC seconds */ ret = ClockScanCommit(&yy, &opts); done: TclUnsetObjRef(yy.date.tzName); if (ret != TCL_OK) { return ret; } Tcl_SetObjResult(interp, Tcl_NewWideIntObj(yy.date.seconds)); return TCL_OK; } /*---------------------------------------------------------------------- * * ClockSecondsObjCmd - * * Returns a count of microseconds since the epoch. |
︙ | ︙ | |||
2007 2008 2009 2010 2011 2012 2013 | int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { Tcl_Time now; Tcl_Obj *timeObj; if (objc != 1) { | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 | int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { Tcl_Time now; Tcl_Obj *timeObj; if (objc != 1) { Tcl_WrongNumArgs(interp, 0, objv, "clock seconds"); return TCL_ERROR; } Tcl_GetTime(&now); TclNewUIntObj(timeObj, (Tcl_WideUInt)now.sec); Tcl_SetObjResult(interp, timeObj); return TCL_OK; } /* *---------------------------------------------------------------------- * * ClockSafeCatchCmd -- * * Same as "::catch" command but avoids overwriting of interp state. * * See [554117edde] for more info (and proper solution). * *---------------------------------------------------------------------- */ int ClockSafeCatchCmd( TCL_UNUSED(void *), Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { typedef struct { int status; /* return code status */ int flags; /* Each remaining field saves the */ int returnLevel; /* corresponding field of the Interp */ int returnCode; /* struct. These fields taken together are */ Tcl_Obj *errorInfo; /* the "state" of the interp. */ Tcl_Obj *errorCode; Tcl_Obj *returnOpts; Tcl_Obj *objResult; Tcl_Obj *errorStack; int resetErrorStack; } InterpState; Interp *iPtr = (Interp *)interp; int ret, flags = 0; InterpState *statePtr; if (objc == 1) { /* wrong # args : */ return Tcl_CatchObjCmd(NULL, interp, objc, objv); } statePtr = (InterpState *)Tcl_SaveInterpState(interp, 0); if (!statePtr->errorInfo) { /* todo: avoid traced get of errorInfo here */ TclInitObjRef(statePtr->errorInfo, Tcl_ObjGetVar2(interp, iPtr->eiVar, NULL, 0)); flags |= ERR_LEGACY_COPY; } if (!statePtr->errorCode) { /* todo: avoid traced get of errorCode here */ TclInitObjRef(statePtr->errorCode, Tcl_ObjGetVar2(interp, iPtr->ecVar, NULL, 0)); flags |= ERR_LEGACY_COPY; } /* original catch */ ret = Tcl_CatchObjCmd(NULL, interp, objc, objv); if (ret == TCL_ERROR) { Tcl_DiscardInterpState((Tcl_InterpState)statePtr); return TCL_ERROR; } /* overwrite result in state with catch result */ TclSetObjRef(statePtr->objResult, Tcl_GetObjResult(interp)); /* set result (together with restore state) to interpreter */ (void) Tcl_RestoreInterpState(interp, (Tcl_InterpState)statePtr); /* todo: unless ERR_LEGACY_COPY not set in restore (branch [bug-554117edde] not merged yet) */ iPtr->flags |= (flags & ERR_LEGACY_COPY); return ret; } /* *---------------------------------------------------------------------- * * TzsetIfNecessary -- * * Calls the tzset() library function if the contents of the TZ |
︙ | ︙ | |||
2043 2044 2045 2046 2047 2048 2049 | #else #define WCHAR char #define wcslen strlen #define wcscmp strcmp #define wcscpy strcpy #endif | | > | > > > | > > | > > > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 | #else #define WCHAR char #define wcslen strlen #define wcscmp strcmp #define wcscpy strcpy #endif static size_t TzsetIfNecessary(void) { static WCHAR* tzWas = (WCHAR *)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 WCHAR *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 changed 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"); } if (tzIsNow != NULL && (tzWas == NULL || tzWas == (WCHAR *)INT2PTR(-1) || wcscmp(tzIsNow, tzWas) != 0)) { tzset(); if (tzWas != NULL && tzWas != (WCHAR *)INT2PTR(-1)) { ckfree(tzWas); } tzWas = (WCHAR *)ckalloc(sizeof(WCHAR) * (wcslen(tzIsNow) + 1)); wcscpy(tzWas, tzIsNow); tzWasEpoch++; } else if (tzIsNow == NULL && tzWas != NULL) { tzset(); if (tzWas != (WCHAR *)INT2PTR(-1)) { ckfree(tzWas); } tzWas = NULL; tzWasEpoch++; } Tcl_MutexUnlock(&clockMutex); return tzWasEpoch; } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ |
Added generic/tclClockFmt.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 | /* * tclClockFmt.c -- * * Contains the date format (and scan) routines. This code is back-ported * from the time and date facilities of tclSE engine, by Serg G. Brester. * * 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" /* * Miscellaneous forward declarations and functions used within this file */ static void ClockFmtObj_DupInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); static void ClockFmtObj_FreeInternalRep(Tcl_Obj *objPtr); static int ClockFmtObj_SetFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static void ClockFmtObj_UpdateString(Tcl_Obj *objPtr); TCL_DECLARE_MUTEX(ClockFmtMutex); /* Serializes access to common format list. */ static void ClockFmtScnStorageDelete(ClockFmtScnStorage *fss); static void ClockFrmScnFinalize(void *clientData); /* * Clock scan and format facilities. */ /* *---------------------------------------------------------------------- * * _str2int -- , _str2wideInt -- * * Fast inline-convertion of string to signed int or wide int by given * start/end. * * The given string should contain numbers chars only (because already * pre-validated within parsing routines) * * Results: * Returns a standard Tcl result. * TCL_OK - by successful conversion, TCL_ERROR by (wide) int overflow * *---------------------------------------------------------------------- */ static inline void _str2int_no( int *out, const char *p, const char *e, int sign) { /* assert(e <= p+10); */ int val = 0; /* overflow impossible for 10 digits ("9..9"), so no needs to check at all */ while (p < e) { /* never overflows */ val = val * 10 + (*p++ - '0'); } if (sign < 0) { val = -val; } *out = val; } static inline void _str2wideInt_no( Tcl_WideInt *out, const char *p, const char *e, int sign) { /* assert(e <= p+18); */ Tcl_WideInt val = 0; /* overflow impossible for 18 digits ("9..9"), so no needs to check at all */ while (p < e) { /* never overflows */ val = val * 10 + (*p++ - '0'); } if (sign < 0) { val = -val; } *out = val; } /* int & Tcl_WideInt overflows may happens here (expected case) */ #if defined(__GNUC__) || defined(__GNUG__) # pragma GCC optimize("no-trapv") #endif static inline int _str2int( int *out, const char *p, const char *e, int sign) { int val = 0; /* overflow impossible for 10 digits ("9..9"), so no needs to check before */ const char *eNO = p+10; if (eNO > e) { eNO = e; } while (p < eNO) { /* never overflows */ val = val * 10 + (*p++ - '0'); } if (sign >= 0) { while (p < e) { /* check for overflow */ int prev = val; val = val * 10 + (*p++ - '0'); if (val / 10 < prev) { return TCL_ERROR; } } } else { val = -val; while (p < e) { /* check for overflow */ int prev = val; val = val * 10 - (*p++ - '0'); if (val / 10 > prev) { return TCL_ERROR; } } } *out = val; return TCL_OK; } static inline int _str2wideInt( Tcl_WideInt *out, const char *p, const char *e, int sign) { Tcl_WideInt val = 0; /* overflow impossible for 18 digits ("9..9"), so no needs to check before */ const char *eNO = p+18; if (eNO > e) { eNO = e; } while (p < eNO) { /* never overflows */ val = val * 10 + (*p++ - '0'); } if (sign >= 0) { while (p < e) { /* check for overflow */ Tcl_WideInt prev = val; val = val * 10 + (*p++ - '0'); if (val / 10 < prev) { return TCL_ERROR; } } } else { val = -val; while (p < e) { /* check for overflow */ Tcl_WideInt prev = val; val = val * 10 - (*p++ - '0'); if (val / 10 > prev) { return TCL_ERROR; } } } *out = val; return TCL_OK; } int TclAtoWIe( Tcl_WideInt *out, const char *p, const char *e, int sign) { return _str2wideInt(out, p, e, sign); } #if defined(__GNUC__) || defined(__GNUG__) # pragma GCC reset_options #endif /* *---------------------------------------------------------------------- * * _itoaw -- , _witoaw -- * * Fast inline-convertion of signed int or wide int to string, using * given padding with specified padchar and width (or without padding). * * This is a very fast replacement for sprintf("%02d"). * * Results: * Returns position in buffer after end of conversion result. * *---------------------------------------------------------------------- */ static inline char * _itoaw( char *buf, int val, char padchar, unsigned short int width) { char *p; static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; /* positive integer */ if (val >= 0) { /* check resp. recalculate width */ while (width <= 9 && val >= wrange[width]) { width++; } /* number to string backwards */ p = buf + width; *p-- = '\0'; do { char c = (val % 10); val /= 10; *p-- = '0' + c; } while (val > 0); /* fulling with pad-char */ while (p >= buf) { *p-- = padchar; } return buf + width; } /* negative integer */ if (!width) width++; /* check resp. recalculate width (regarding sign) */ width--; while (width <= 9 && val <= -wrange[width]) { width++; } width++; /* number to string backwards */ p = buf + width; *p-- = '\0'; /* differentiate platforms with -1 % 10 == 1 and -1 % 10 == -1 */ if (-1 % 10 == -1) { do { char c = (val % 10); val /= 10; *p-- = '0' - c; } while (val < 0); } else { do { char c = (val % 10); val /= 10; *p-- = '0' + c; } while (val < 0); } /* sign by 0 padding */ if (padchar != '0') { *p-- = '-'; } /* fulling with pad-char */ while (p >= buf + 1) { *p-- = padchar; } /* sign by non 0 padding */ if (padchar == '0') { *p = '-'; } return buf + width; } char * TclItoAw( char *buf, int val, char padchar, unsigned short int width) { return _itoaw(buf, val, padchar, width); } static inline char * _witoaw( char *buf, Tcl_WideInt val, char padchar, unsigned short int width) { char *p; static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; /* positive integer */ if (val >= 0) { /* check resp. recalculate width */ if (val >= 10000000000LL) { Tcl_WideInt val2; val2 = val / 10000000000LL; while (width <= 9 && val2 >= wrange[width]) { width++; } width += 10; } else { while (width <= 9 && val >= wrange[width]) { width++; } } /* number to string backwards */ p = buf + width; *p-- = '\0'; do { char c = (val % 10); val /= 10; *p-- = '0' + c; } while (val > 0); /* fulling with pad-char */ while (p >= buf) { *p-- = padchar; } return buf + width; } /* negative integer */ if (!width) width++; /* check resp. recalculate width (regarding sign) */ width--; if (val <= -10000000000LL) { Tcl_WideInt val2; val2 = val / 10000000000LL; while (width <= 9 && val2 <= -wrange[width]) { width++; } width += 10; } else { while (width <= 9 && val <= -wrange[width]) { width++; } } width++; /* number to string backwards */ p = buf + width; *p-- = '\0'; /* differentiate platforms with -1 % 10 == 1 and -1 % 10 == -1 */ if (-1 % 10 == -1) { do { char c = (val % 10); val /= 10; *p-- = '0' - c; } while (val < 0); } else { do { char c = (val % 10); val /= 10; *p-- = '0' + c; } while (val < 0); } /* sign by 0 padding */ if (padchar != '0') { *p-- = '-'; } /* fulling with pad-char */ while (p >= buf + 1) { *p-- = padchar; } /* sign by non 0 padding */ if (padchar == '0') { *p = '-'; } return buf + width; } /* * Global GC as LIFO for released scan/format object storages. * * Used to holds last released CLOCK_FMT_SCN_STORAGE_GC_SIZE formats * (after last reference from Tcl-object will be removed). This is helpful * to avoid continuous (re)creation and compiling by some dynamically resp. * variable format objects, that could be often reused. * * As long as format storage is used resp. belongs to GC, it takes place in * FmtScnHashTable also. */ #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 static struct { ClockFmtScnStorage *stackPtr; ClockFmtScnStorage *stackBound; unsigned int count; } ClockFmtScnStorage_GC = {NULL, NULL, 0}; /* *---------------------------------------------------------------------- * * ClockFmtScnStorageGC_In -- * * Adds an format storage object to GC. * * If current GC is full (size larger as CLOCK_FMT_SCN_STORAGE_GC_SIZE) * this removes last unused storage at begin of GC stack (LIFO). * * Assumes caller holds the ClockFmtMutex. * * Results: * None. * *---------------------------------------------------------------------- */ static inline void ClockFmtScnStorageGC_In(ClockFmtScnStorage *entry) { /* add new entry */ TclSpliceIn(entry, ClockFmtScnStorage_GC.stackPtr); if (ClockFmtScnStorage_GC.stackBound == NULL) { ClockFmtScnStorage_GC.stackBound = entry; } ClockFmtScnStorage_GC.count++; /* if GC ist full */ if (ClockFmtScnStorage_GC.count > CLOCK_FMT_SCN_STORAGE_GC_SIZE) { /* GC stack is LIFO: delete first inserted entry */ ClockFmtScnStorage *delEnt = ClockFmtScnStorage_GC.stackBound; ClockFmtScnStorage_GC.stackBound = delEnt->prevPtr; TclSpliceOut(delEnt, ClockFmtScnStorage_GC.stackPtr); ClockFmtScnStorage_GC.count--; delEnt->prevPtr = delEnt->nextPtr = NULL; /* remove it now */ ClockFmtScnStorageDelete(delEnt); } } /* *---------------------------------------------------------------------- * * ClockFmtScnStorage_GC_Out -- * * Restores (for reusing) given format storage object from GC. * * Assumes caller holds the ClockFmtMutex. * * Results: * None. * *---------------------------------------------------------------------- */ static inline void ClockFmtScnStorage_GC_Out(ClockFmtScnStorage *entry) { TclSpliceOut(entry, ClockFmtScnStorage_GC.stackPtr); ClockFmtScnStorage_GC.count--; if (ClockFmtScnStorage_GC.stackBound == entry) { ClockFmtScnStorage_GC.stackBound = entry->prevPtr; } entry->prevPtr = entry->nextPtr = NULL; } #endif /* * Global format storage hash table of type ClockFmtScnStorageHashKeyType * (contains list of scan/format object storages, shared across all threads). * * Used for fast searching by format string. */ static Tcl_HashTable FmtScnHashTable; static int initialized = 0; /* * Wrappers between pointers to hash entry and format storage object */ static inline Tcl_HashEntry * HashEntry4FmtScn(ClockFmtScnStorage *fss) { return (Tcl_HashEntry*)(fss + 1); }; static inline ClockFmtScnStorage * FmtScn4HashEntry(Tcl_HashEntry *hKeyPtr) { return (ClockFmtScnStorage*)(((char*)hKeyPtr) - sizeof(ClockFmtScnStorage)); }; /* *---------------------------------------------------------------------- * * ClockFmtScnStorageAllocProc -- * * Allocate space for a hash entry containing format storage together * with the string key. * * Results: * The return value is a pointer to the created entry. * *---------------------------------------------------------------------- */ static Tcl_HashEntry * ClockFmtScnStorageAllocProc( TCL_UNUSED(Tcl_HashTable *), /* Hash table. */ void *keyPtr) /* Key to store in the hash table entry. */ { ClockFmtScnStorage *fss; const char *string = (const char *) keyPtr; Tcl_HashEntry *hPtr; unsigned int size, allocsize = sizeof(ClockFmtScnStorage) + sizeof(Tcl_HashEntry); allocsize += (size = strlen(string) + 1); if (size > sizeof(hPtr->key)) { allocsize -= sizeof(hPtr->key); } fss = (ClockFmtScnStorage *)ckalloc(allocsize); /* initialize */ memset(fss, 0, sizeof(*fss)); hPtr = HashEntry4FmtScn(fss); memcpy(&hPtr->key.string, string, size); hPtr->clientData = 0; /* currently unused */ return hPtr; } /* *---------------------------------------------------------------------- * * ClockFmtScnStorageFreeProc -- * * Free format storage object and space of given hash entry. * * Results: * None. * *---------------------------------------------------------------------- */ static void ClockFmtScnStorageFreeProc( Tcl_HashEntry *hPtr) { ClockFmtScnStorage *fss = FmtScn4HashEntry(hPtr); if (fss->scnTok != NULL) { ckfree(fss->scnTok); fss->scnTok = NULL; fss->scnTokC = 0; } if (fss->fmtTok != NULL) { ckfree(fss->fmtTok); fss->fmtTok = NULL; fss->fmtTokC = 0; } ckfree(fss); } /* *---------------------------------------------------------------------- * * ClockFmtScnStorageDelete -- * * Delete format storage object. * * Results: * None. * *---------------------------------------------------------------------- */ static void ClockFmtScnStorageDelete(ClockFmtScnStorage *fss) { Tcl_HashEntry *hPtr = HashEntry4FmtScn(fss); /* * This will delete a hash entry and call "Tcl_Free" for storage self, if * some additionally handling required, freeEntryProc can be used instead */ Tcl_DeleteHashEntry(hPtr); } /* * Derivation of tclStringHashKeyType with another allocEntryProc */ static Tcl_HashKeyType ClockFmtScnStorageHashKeyType; /* * Type definition of clock-format tcl object type. */ static const Tcl_ObjType ClockFmtObjType = { "clock-format", /* name */ ClockFmtObj_FreeInternalRep, /* freeIntRepProc */ ClockFmtObj_DupInternalRep, /* dupIntRepProc */ ClockFmtObj_UpdateString, /* updateStringProc */ ClockFmtObj_SetFromAny, /* setFromAnyProc */ TCL_OBJTYPE_V0 }; #define ObjClockFmtScn(objPtr) \ (*((ClockFmtScnStorage **)&(objPtr)->internalRep.twoPtrValue.ptr1)) #define ObjLocFmtKey(objPtr) \ (*((Tcl_Obj **)&(objPtr)->internalRep.twoPtrValue.ptr2)) static void ClockFmtObj_DupInternalRep( Tcl_Obj *srcPtr, Tcl_Obj *copyPtr) { ClockFmtScnStorage *fss = ObjClockFmtScn(srcPtr); if (fss != NULL) { Tcl_MutexLock(&ClockFmtMutex); fss->objRefCount++; Tcl_MutexUnlock(&ClockFmtMutex); } ObjClockFmtScn(copyPtr) = fss; /* regards special case - format not localizable */ if (ObjLocFmtKey(srcPtr) != srcPtr) { TclInitObjRef(ObjLocFmtKey(copyPtr), ObjLocFmtKey(srcPtr)); } else { ObjLocFmtKey(copyPtr) = copyPtr; } copyPtr->typePtr = &ClockFmtObjType; /* if no format representation, dup string representation */ if (fss == NULL) { copyPtr->bytes = (char *)ckalloc(srcPtr->length + 1); memcpy(copyPtr->bytes, srcPtr->bytes, srcPtr->length + 1); copyPtr->length = srcPtr->length; } } static void ClockFmtObj_FreeInternalRep( Tcl_Obj *objPtr) { ClockFmtScnStorage *fss = ObjClockFmtScn(objPtr); if (fss != NULL) { Tcl_MutexLock(&ClockFmtMutex); /* decrement object reference count of format/scan storage */ if (--fss->objRefCount <= 0) { #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 /* don't remove it right now (may be reusable), just add to GC */ ClockFmtScnStorageGC_In(fss); #else /* remove storage (format representation) */ ClockFmtScnStorageDelete(fss); #endif } Tcl_MutexUnlock(&ClockFmtMutex); } ObjClockFmtScn(objPtr) = NULL; if (ObjLocFmtKey(objPtr) != objPtr) { TclUnsetObjRef(ObjLocFmtKey(objPtr)); } else { ObjLocFmtKey(objPtr) = NULL; } objPtr->typePtr = NULL; }; static int ClockFmtObj_SetFromAny( TCL_UNUSED(Tcl_Interp *), Tcl_Obj *objPtr) { /* validate string representation before free old internal representation */ (void)TclGetString(objPtr); /* free old internal representation */ if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc) objPtr->typePtr->freeIntRepProc(objPtr); /* initial state of format object */ ObjClockFmtScn(objPtr) = NULL; ObjLocFmtKey(objPtr) = NULL; objPtr->typePtr = &ClockFmtObjType; return TCL_OK; }; static void ClockFmtObj_UpdateString( Tcl_Obj *objPtr) { const char *name = "UNKNOWN"; size_t len; ClockFmtScnStorage *fss = ObjClockFmtScn(objPtr); if (fss != NULL) { Tcl_HashEntry *hPtr = HashEntry4FmtScn(fss); name = hPtr->key.string; } len = strlen(name); objPtr->length = len++, objPtr->bytes = (char *)ckalloc(len); if (objPtr->bytes) { memcpy(objPtr->bytes, name, len); } } /* *---------------------------------------------------------------------- * * ClockFrmObjGetLocFmtKey -- * * Retrieves format key object used to search localized format. * * This is normally stored in second pointer of internal representation. * If format object is not localizable, it is equal the given format * pointer (special case to fast fallback by not-localizable formats). * * Results: * Returns tcl object with key or format object if not localizable. * * Side effects: * Converts given format object to ClockFmtObjType on demand for caching * the key inside its internal representation. * *---------------------------------------------------------------------- */ Tcl_Obj* ClockFrmObjGetLocFmtKey( Tcl_Interp *interp, Tcl_Obj *objPtr) { Tcl_Obj *keyObj; if (objPtr->typePtr != &ClockFmtObjType) { if (ClockFmtObj_SetFromAny(interp, objPtr) != TCL_OK) { return NULL; } } keyObj = ObjLocFmtKey(objPtr); if (keyObj) { return keyObj; } keyObj = Tcl_ObjPrintf("FMT_%s", TclGetString(objPtr)); TclInitObjRef(ObjLocFmtKey(objPtr), keyObj); return keyObj; } /* *---------------------------------------------------------------------- * * FindOrCreateFmtScnStorage -- * * Retrieves format storage for given string format. * * This will find the given format in the global storage hash table * or create a format storage object on demaind and save the * reference in the first pointer of internal representation of given * object. * * Results: * Returns scan/format storage pointer to ClockFmtScnStorage. * * Side effects: * Converts given format object to ClockFmtObjType on demand for caching * the format storage reference inside its internal representation. * Increments objRefCount of the ClockFmtScnStorage reference. * *---------------------------------------------------------------------- */ static ClockFmtScnStorage * FindOrCreateFmtScnStorage( Tcl_Interp *interp, Tcl_Obj *objPtr) { const char *strFmt = TclGetString(objPtr); ClockFmtScnStorage *fss = NULL; int isNew; Tcl_HashEntry *hPtr; Tcl_MutexLock(&ClockFmtMutex); /* if not yet initialized */ if (!initialized) { /* initialize type */ memcpy(&ClockFmtScnStorageHashKeyType, &tclStringHashKeyType, sizeof(tclStringHashKeyType)); ClockFmtScnStorageHashKeyType.allocEntryProc = ClockFmtScnStorageAllocProc; ClockFmtScnStorageHashKeyType.freeEntryProc = ClockFmtScnStorageFreeProc; /* initialize hash table */ Tcl_InitCustomHashTable(&FmtScnHashTable, TCL_CUSTOM_TYPE_KEYS, &ClockFmtScnStorageHashKeyType); initialized = 1; Tcl_CreateExitHandler(ClockFrmScnFinalize, NULL); } /* get or create entry (and alocate storage) */ hPtr = Tcl_CreateHashEntry(&FmtScnHashTable, strFmt, &isNew); if (hPtr != NULL) { fss = FmtScn4HashEntry(hPtr); #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 /* unlink if it is currently in GC */ if (isNew == 0 && fss->objRefCount == 0) { ClockFmtScnStorage_GC_Out(fss); } #endif /* new reference, so increment in lock right now */ fss->objRefCount++; ObjClockFmtScn(objPtr) = fss; } Tcl_MutexUnlock(&ClockFmtMutex); if (fss == NULL && interp != NULL) { Tcl_AppendResult(interp, "retrieve clock format failed \"", strFmt ? strFmt : "", "\"", NULL); Tcl_SetErrorCode(interp, "TCL", "EINVAL", (char *)NULL); } return fss; } /* *---------------------------------------------------------------------- * * Tcl_GetClockFrmScnFromObj -- * * Returns a clock format/scan representation of (*objPtr), if possible. * If something goes wrong, NULL is returned, and if interp is non-NULL, * an error message is written there. * * Results: * Valid representation of type ClockFmtScnStorage. * * Side effects: * Caches the ClockFmtScnStorage reference as the internal rep of (*objPtr) * and in global hash table, shared across all threads. * *---------------------------------------------------------------------- */ ClockFmtScnStorage * Tcl_GetClockFrmScnFromObj( Tcl_Interp *interp, Tcl_Obj *objPtr) { ClockFmtScnStorage *fss; if (objPtr->typePtr != &ClockFmtObjType) { if (ClockFmtObj_SetFromAny(interp, objPtr) != TCL_OK) { return NULL; } } fss = ObjClockFmtScn(objPtr); if (fss == NULL) { fss = FindOrCreateFmtScnStorage(interp, objPtr); } return fss; } /* *---------------------------------------------------------------------- * * ClockLocalizeFormat -- * * Wrap the format object in options to the localized format, * corresponding given locale. * * This searches localized format in locale catalog, and if not yet * exists, it executes ::tcl::clock::LocalizeFormat in given interpreter * and caches its result in the locale catalog. * * Results: * Localized format object. * * Side effects: * Caches the localized format inside locale catalog. * *---------------------------------------------------------------------- */ Tcl_Obj * ClockLocalizeFormat( ClockFmtScnCmdArgs *opts) { ClockClientData *dataPtr = (ClockClientData *)opts->clientData; Tcl_Obj *valObj = NULL, *keyObj; keyObj = ClockFrmObjGetLocFmtKey(opts->interp, opts->formatObj); /* special case - format object is not localizable */ if (keyObj == opts->formatObj) { return opts->formatObj; } /* prevents loss of key object if the format object (where key stored) * becomes changed (loses its internal representation during evals) */ Tcl_IncrRefCount(keyObj); if (opts->mcDictObj == NULL) { ClockMCDict(opts); if (opts->mcDictObj == NULL) goto done; } /* try to find in cache within locale mc-catalog */ if (Tcl_DictObjGet(NULL, opts->mcDictObj, keyObj, &valObj) != TCL_OK) { goto done; } /* call LocalizeFormat locale format fmtkey */ if (valObj == NULL) { Tcl_Obj *callargs[4]; callargs[0] = dataPtr->literals[LIT_LOCALIZE_FORMAT]; callargs[1] = opts->localeObj; callargs[2] = opts->formatObj; callargs[3] = opts->mcDictObj; if (Tcl_EvalObjv(opts->interp, 4, callargs, 0) == TCL_OK ) { valObj = Tcl_GetObjResult(opts->interp); } /* ensure mcDictObj remains unshared */ if (opts->mcDictObj->refCount > 1) { /* smart reference (shared dict as object with no ref-counter) */ opts->mcDictObj = TclDictObjSmartRef(opts->interp, opts->mcDictObj); } if (!valObj) { goto done; } /* cache it inside mc-dictionary (this incr. ref count of keyObj/valObj) */ if (Tcl_DictObjPut(opts->interp, opts->mcDictObj, keyObj, valObj) != TCL_OK ) { valObj = NULL; goto done; } Tcl_ResetResult(opts->interp); /* check special case - format object is not localizable */ if (valObj == opts->formatObj) { /* mark it as unlocalizable, by setting self as key (without refcount incr) */ if (valObj->typePtr == &ClockFmtObjType) { TclUnsetObjRef(ObjLocFmtKey(valObj)); ObjLocFmtKey(valObj) = valObj; } } } done: TclUnsetObjRef(keyObj); return (opts->formatObj = valObj); } /* *---------------------------------------------------------------------- * * FindTokenBegin -- * * Find begin of given scan token in string, corresponding token type. * * Results: * Position of token inside string if found. Otherwise - end of string. * * Side effects: * None. * *---------------------------------------------------------------------- */ static const char * FindTokenBegin( const char *p, const char *end, ClockScanToken *tok) { char c; if (p < end) { /* next token a known token type */ switch (tok->map->type) { case CTOKT_INT: case CTOKT_WIDE: /* should match at least one digit */ while (!isdigit(UCHAR(*p)) && (p = Tcl_UtfNext(p)) < end) {}; return p; break; case CTOKT_WORD: c = *(tok->tokWord.start); /* should match at least to the first char of this word */ while (*p != c && (p = Tcl_UtfNext(p)) < end) {}; return p; break; case CTOKT_SPACE: while (!isspace(UCHAR(*p)) && (p = Tcl_UtfNext(p)) < end) {}; return p; break; case CTOKT_CHAR: c = *((char *)tok->map->data); while (*p != c && (p = Tcl_UtfNext(p)) < end) {}; return p; break; } } return p; } /* *---------------------------------------------------------------------- * * DetermineGreedySearchLen -- * * Determine min/max lengths as exact as possible (speed, greedy match). * * Results: * None. Lengths are stored in *minLenPtr, *maxLenPtr. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void DetermineGreedySearchLen( DateInfo *info, ClockScanToken *tok, int *minLenPtr, int *maxLenPtr) { int minLen = tok->map->minSize; int maxLen; const char *p = yyInput + minLen, *end = info->dateEnd; /* if still tokens available, try to correct minimum length */ if ((tok+1)->map) { end -= tok->endDistance + yySpaceCount; /* find position of next known token */ p = FindTokenBegin(p, end, tok+1); if (p < end) { minLen = p - yyInput; } } /* max length to the end regarding distance to end (min-width of following tokens) */ maxLen = end - yyInput; /* several amendments */ if (maxLen > tok->map->maxSize) { maxLen = tok->map->maxSize; }; if (minLen < tok->map->minSize) { minLen = tok->map->minSize; } if (minLen > maxLen) { maxLen = minLen; } if (maxLen > info->dateEnd - yyInput) { maxLen = info->dateEnd - yyInput; } /* check digits rigth now */ if (tok->map->type == CTOKT_INT || tok->map->type == CTOKT_WIDE) { p = yyInput; end = p + maxLen; if (end > info->dateEnd) { end = info->dateEnd; }; while (isdigit(UCHAR(*p)) && p < end) { p++; }; maxLen = p - yyInput; } /* try to get max length more precise for greedy match, * check the next ahead token available there */ if (minLen < maxLen && tok->lookAhTok) { ClockScanToken *laTok = tok + tok->lookAhTok + 1; p = yyInput + maxLen; /* regards all possible spaces here (because they are optional) */ end = p + tok->lookAhMax + yySpaceCount + 1; if (end > info->dateEnd) { end = info->dateEnd; } p += tok->lookAhMin; if (laTok->map && p < end) { const char *f; /* try to find laTok between [lookAhMin, lookAhMax] */ while (minLen < maxLen) { f = FindTokenBegin(p, end, laTok); /* if found (not below lookAhMax) */ if (f < end) { break; } /* try again with fewer length */ maxLen--; p--; end--; } } else if (p > end) { maxLen -= (p - end); if (maxLen < minLen) { maxLen = minLen; } } } *minLenPtr = minLen; *maxLenPtr = maxLen; } /* *---------------------------------------------------------------------- * * ObjListSearch -- * * Find largest part of the input string from start regarding min and * max lengths in the given list (utf-8, case sensitive). * * Results: * TCL_OK - match found, TCL_RETURN - not matched, TCL_ERROR in error case. * * Side effects: * Input points to end of the found token in string. * *---------------------------------------------------------------------- */ static inline int ObjListSearch( DateInfo *info, int *val, Tcl_Obj **lstv, Tcl_Size lstc, int minLen, int maxLen) { Tcl_Size i, l, lf = -1; const char *s, *f, *sf; /* search in list */ for (i = 0; i < lstc; i++) { s = TclGetStringFromObj(lstv[i], &l); if ( l >= minLen && (f = TclUtfFindEqualNC(yyInput, yyInput + maxLen, s, s + l, &sf)) > yyInput ) { l = f - yyInput; if (l < minLen) { continue; } /* found, try to find longest value (greedy search) */ if (l < maxLen && minLen != maxLen) { lf = i; minLen = l + 1; continue; } /* max possible - end of search */ *val = i; yyInput += l; break; } } /* if found */ if (i < lstc) { return TCL_OK; } if (lf >= 0) { *val = lf; yyInput += minLen - 1; return TCL_OK; } return TCL_RETURN; } #if 0 /* currently unused */ static int LocaleListSearch(ClockFmtScnCmdArgs *opts, DateInfo *info, int mcKey, int *val, int minLen, int maxLen) { Tcl_Obj **lstv; Tcl_Size lstc; Tcl_Obj *valObj; /* get msgcat value */ valObj = ClockMCGet(opts, mcKey); if (valObj == NULL) { return TCL_ERROR; } /* is a list */ if (TclListObjGetElements(opts->interp, valObj, &lstc, &lstv) != TCL_OK) { return TCL_ERROR; } /* search in list */ return ObjListSearch(info, val, lstv, lstc, minLen, maxLen); } #endif /* *---------------------------------------------------------------------- * * ClockMCGetListIdxTree -- * * Retrieves localized string indexed tree in the locale catalog for * given literal index mcKey (and builds it on demand). * * Searches localized index in locale catalog, and if not yet exists, * creates string indexed tree and stores it in the locale catalog. * * Results: * Localized string index tree. * * Side effects: * Caches the localized string index tree inside locale catalog. * *---------------------------------------------------------------------- */ static TclStrIdxTree * ClockMCGetListIdxTree( ClockFmtScnCmdArgs *opts, int mcKey) { TclStrIdxTree * idxTree; Tcl_Obj *objPtr = ClockMCGetIdx(opts, mcKey); if ( objPtr != NULL && (idxTree = TclStrIdxTreeGetFromObj(objPtr)) != NULL ) { return idxTree; } else { /* build new index */ Tcl_Obj **lstv; Tcl_Size lstc; Tcl_Obj *valObj; objPtr = TclStrIdxTreeNewObj(); if ((idxTree = TclStrIdxTreeGetFromObj(objPtr)) == NULL) { goto done; /* unexpected, but ...*/ } valObj = ClockMCGet(opts, mcKey); if (valObj == NULL) { goto done; } if (TclListObjGetElements(opts->interp, valObj, &lstc, &lstv) != TCL_OK) { goto done; }; if (TclStrIdxTreeBuildFromList(idxTree, lstc, lstv, NULL) != TCL_OK) { goto done; } ClockMCSetIdx(opts, mcKey, objPtr); objPtr = NULL; }; done: if (objPtr) { Tcl_DecrRefCount(objPtr); idxTree = NULL; } return idxTree; } /* *---------------------------------------------------------------------- * * ClockMCGetMultiListIdxTree -- * * Retrieves localized string indexed tree in the locale catalog for * multiple lists by literal indices mcKeys (and builds it on demand). * * Searches localized index in locale catalog for mcKey, and if not * yet exists, creates string indexed tree and stores it in the * locale catalog. * * Results: * Localized string index tree. * * Side effects: * Caches the localized string index tree inside locale catalog. * *---------------------------------------------------------------------- */ static TclStrIdxTree * ClockMCGetMultiListIdxTree( ClockFmtScnCmdArgs *opts, int mcKey, int *mcKeys) { TclStrIdxTree * idxTree; Tcl_Obj *objPtr = ClockMCGetIdx(opts, mcKey); if ( objPtr != NULL && (idxTree = TclStrIdxTreeGetFromObj(objPtr)) != NULL ) { return idxTree; } else { /* build new index */ Tcl_Obj **lstv; Tcl_Size lstc; Tcl_Obj *valObj; objPtr = TclStrIdxTreeNewObj(); if ((idxTree = TclStrIdxTreeGetFromObj(objPtr)) == NULL) { goto done; /* unexpected, but ...*/ } while (*mcKeys) { valObj = ClockMCGet(opts, *mcKeys); if (valObj == NULL) { goto done; } if (TclListObjGetElements(opts->interp, valObj, &lstc, &lstv) != TCL_OK) { goto done; }; if (TclStrIdxTreeBuildFromList(idxTree, lstc, lstv, NULL) != TCL_OK) { goto done; } mcKeys++; } ClockMCSetIdx(opts, mcKey, objPtr); objPtr = NULL; }; done: if (objPtr) { Tcl_DecrRefCount(objPtr); idxTree = NULL; } return idxTree; } /* *---------------------------------------------------------------------- * * ClockStrIdxTreeSearch -- * * Find largest part of the input string from start regarding lengths * in the given localized string indexed tree (utf-8, case sensitive). * * Results: * TCL_OK - match found and the index stored in *val, * TCL_RETURN - not matched or ambigous, * TCL_ERROR - in error case. * * Side effects: * Input points to end of the found token in string. * *---------------------------------------------------------------------- */ static inline int ClockStrIdxTreeSearch( DateInfo *info, TclStrIdxTree *idxTree, int *val, int minLen, int maxLen) { const char *f; TclStrIdx *foundItem; f = TclStrIdxTreeSearch(NULL, &foundItem, idxTree, yyInput, yyInput + maxLen); if (f <= yyInput || (f - yyInput) < minLen) { /* not found */ return TCL_RETURN; } if (!foundItem->value) { /* ambigous */ return TCL_RETURN; } *val = PTR2INT(foundItem->value); /* shift input pointer */ yyInput = f; return TCL_OK; } #if 0 /* currently unused */ static int StaticListSearch(ClockFmtScnCmdArgs *opts, DateInfo *info, const char **lst, int *val) { size_t len; const char **s = lst; while (*s != NULL) { len = strlen(*s); if ( len <= info->dateEnd - yyInput && strncasecmp(yyInput, *s, len) == 0 ) { *val = (s - lst); yyInput += len; break; } s++; } if (*s != NULL) { return TCL_OK; } return TCL_RETURN; } #endif static inline const char * FindWordEnd( ClockScanToken *tok, const char * p, const char * end) { const char *x = tok->tokWord.start; const char *pfnd = p; if (x == tok->tokWord.end - 1) { /* fast phase-out for single char word */ if (*p == *x) { return ++p; } } /* multi-char word */ x = TclUtfFindEqualNC(x, tok->tokWord.end, p, end, &pfnd); if (x < tok->tokWord.end) { /* no match -> error */ return NULL; } return pfnd; } static int ClockScnToken_Month_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { #if 0 /* currently unused, test purposes only */ static const char * months[] = { /* full */ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", /* abbr */ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; int val; if (StaticListSearch(opts, info, months, &val) != TCL_OK) { return TCL_RETURN; } yyMonth = (val % 12) + 1; return TCL_OK; #endif static int monthsKeys[] = {MCLIT_MONTHS_FULL, MCLIT_MONTHS_ABBREV, 0}; int ret, val; int minLen, maxLen; TclStrIdxTree *idxTree; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); /* get or create tree in msgcat dict */ idxTree = ClockMCGetMultiListIdxTree(opts, MCLIT_MONTHS_COMB, monthsKeys); if (idxTree == NULL) { return TCL_ERROR; } ret = ClockStrIdxTreeSearch(info, idxTree, &val, minLen, maxLen); if (ret != TCL_OK) { return ret; } yyMonth = val; return TCL_OK; } static int ClockScnToken_DayOfWeek_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { static int dowKeys[] = {MCLIT_DAYS_OF_WEEK_ABBREV, MCLIT_DAYS_OF_WEEK_FULL, 0}; int ret, val; int minLen, maxLen; char curTok = *tok->tokWord.start; TclStrIdxTree *idxTree; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); /* %u %w %Ou %Ow */ if ( curTok != 'a' && curTok != 'A' && ((minLen <= 1 && maxLen >= 1) || PTR2INT(tok->map->data)) ) { val = -1; if (PTR2INT(tok->map->data) == 0) { if (*yyInput >= '0' && *yyInput <= '9') { val = *yyInput - '0'; } } else { idxTree = ClockMCGetListIdxTree(opts, PTR2INT(tok->map->data) /* mcKey */); if (idxTree == NULL) { return TCL_ERROR; } ret = ClockStrIdxTreeSearch(info, idxTree, &val, minLen, maxLen); if (ret != TCL_OK) { return ret; } --val; } if (val != -1) { if (val == 0) { val = 7; } if (val > 7) { Tcl_SetObjResult(opts->interp, Tcl_NewStringObj("day of week is greater than 7", -1)); Tcl_SetErrorCode(opts->interp, "CLOCK", "badDayOfWeek", (char *)NULL); return TCL_ERROR; } info->date.dayOfWeek = val; yyInput++; return TCL_OK; } return TCL_RETURN; } /* %a %A */ idxTree = ClockMCGetMultiListIdxTree(opts, MCLIT_DAYS_OF_WEEK_COMB, dowKeys); if (idxTree == NULL) { return TCL_ERROR; } ret = ClockStrIdxTreeSearch(info, idxTree, &val, minLen, maxLen); if (ret != TCL_OK) { return ret; } --val; if (val == 0) { val = 7; } info->date.dayOfWeek = val; return TCL_OK; } static int ClockScnToken_amPmInd_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { int ret, val; int minLen, maxLen; Tcl_Obj *amPmObj[2]; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); amPmObj[0] = ClockMCGet(opts, MCLIT_AM); amPmObj[1] = ClockMCGet(opts, MCLIT_PM); if (amPmObj[0] == NULL || amPmObj[1] == NULL) { return TCL_ERROR; } ret = ObjListSearch(info, &val, amPmObj, 2, minLen, maxLen); if (ret != TCL_OK) { return ret; } if (val == 0) { yyMeridian = MERam; } else { yyMeridian = MERpm; } return TCL_OK; } static int ClockScnToken_LocaleERA_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { ClockClientData *dataPtr = (ClockClientData *)opts->clientData; int ret, val; int minLen, maxLen; Tcl_Obj *eraObj[6]; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); eraObj[0] = ClockMCGet(opts, MCLIT_BCE); eraObj[1] = ClockMCGet(opts, MCLIT_CE); eraObj[2] = dataPtr->mcLiterals[MCLIT_BCE2]; eraObj[3] = dataPtr->mcLiterals[MCLIT_CE2]; eraObj[4] = dataPtr->mcLiterals[MCLIT_BCE3]; eraObj[5] = dataPtr->mcLiterals[MCLIT_CE3]; if (eraObj[0] == NULL || eraObj[1] == NULL) { return TCL_ERROR; } ret = ObjListSearch(info, &val, eraObj, 6, minLen, maxLen); if (ret != TCL_OK) { return ret; } if (val & 1) { yydate.isBce = 0; } else { yydate.isBce = 1; } return TCL_OK; } static int ClockScnToken_LocaleListMatcher_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { int ret, val; int minLen, maxLen; TclStrIdxTree *idxTree; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); /* get or create tree in msgcat dict */ idxTree = ClockMCGetListIdxTree(opts, PTR2INT(tok->map->data) /* mcKey */); if (idxTree == NULL) { return TCL_ERROR; } ret = ClockStrIdxTreeSearch(info, idxTree, &val, minLen, maxLen); if (ret != TCL_OK) { return ret; } if (tok->map->offs > 0) { *(int *)(((char *)info) + tok->map->offs) = --val; } return TCL_OK; } static int ClockScnToken_JDN_Proc(TCL_UNUSED(ClockFmtScnCmdArgs *), DateInfo *info, ClockScanToken *tok) { int minLen, maxLen; const char *p = yyInput, *end; const char *s; Tcl_WideInt intJD; int fractJD = 0, fractJDDiv = 1; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); end = yyInput + maxLen; /* currently positive astronomic dates only */ if (*p == '+' || *p == '-') { p++; }; s = p; while (p < end && isdigit(UCHAR(*p))) { p++; } if ( _str2wideInt(&intJD, s, p, (*yyInput != '-' ? 1 : -1)) != TCL_OK) { return TCL_RETURN; }; yyInput = p; if (p >= end || *p++ != '.') { /* allow pure integer JDN */ /* by astronomical JD the seconds of day offs is 12 hours */ if (tok->map->offs) { goto done; } /* calendar JD */ yydate.julianDay = intJD; return TCL_OK; } s = p; while (p < end && isdigit(UCHAR(*p))) { fractJDDiv *= 10; p++; } if ( _str2int(&fractJD, s, p, 1) != TCL_OK) { return TCL_RETURN; }; yyInput = p; done: /* * Build a date from julian day (integer and fraction). * Note, astronomical JDN starts at noon in opposite to calendar julianday. */ fractJD = (int)tok->map->offs /* 0 for calendar or 43200 for astro JD */ + (int)((Tcl_WideInt)SECONDS_PER_DAY * fractJD / fractJDDiv); if (fractJD > SECONDS_PER_DAY) { fractJD %= SECONDS_PER_DAY; intJD += 1; } yydate.secondOfDay = fractJD; yydate.julianDay = intJD; yydate.seconds = -210866803200LL + ( SECONDS_PER_DAY * intJD ) + ( fractJD ); info->flags |= CLF_POSIXSEC; return TCL_OK; } static int ClockScnToken_TimeZone_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { int minLen, maxLen; int len = 0; const char *p = yyInput; Tcl_Obj *tzObjStor = NULL; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); /* numeric timezone */ if (*p == '+' || *p == '-') { /* max chars in numeric zone = "+00:00:00" */ #define MAX_ZONE_LEN 9 char buf[MAX_ZONE_LEN + 1]; char *bp = buf; *bp++ = *p++; len++; if (maxLen > MAX_ZONE_LEN) maxLen = MAX_ZONE_LEN; /* cumulate zone into buf without ':' */ while (len + 1 < maxLen) { if (!isdigit(UCHAR(*p))) break; *bp++ = *p++; len++; if (!isdigit(UCHAR(*p))) break; *bp++ = *p++; len++; if (len + 2 < maxLen) { if (*p == ':') { p++; len++; } } } *bp = '\0'; if (len < minLen) { return TCL_RETURN; } #undef MAX_ZONE_LEN /* timezone */ tzObjStor = Tcl_NewStringObj(buf, bp-buf); } else { /* legacy (alnum) timezone like CEST, etc. */ if (maxLen > 4) maxLen = 4; while (len < maxLen) { if ( (*p & 0x80) || (!isalpha(UCHAR(*p)) && !isdigit(UCHAR(*p))) ) { /* INTL: ISO only. */ break; } p++; len++; } if (len < minLen) { return TCL_RETURN; } /* timezone */ tzObjStor = Tcl_NewStringObj(yyInput, p-yyInput); /* convert using dict */ } /* try to apply new time zone */ Tcl_IncrRefCount(tzObjStor); opts->timezoneObj = ClockSetupTimeZone(opts->clientData, opts->interp, tzObjStor); Tcl_DecrRefCount(tzObjStor); if (opts->timezoneObj == NULL) { return TCL_ERROR; } yyInput += len; return TCL_OK; } static int ClockScnToken_StarDate_Proc(TCL_UNUSED(ClockFmtScnCmdArgs *), DateInfo *info, ClockScanToken *tok) { int minLen, maxLen; const char *p = yyInput, *end; const char *s; int year, fractYear, fractDayDiv, fractDay; static const char *stardatePref = "stardate "; DetermineGreedySearchLen(info, tok, &minLen, &maxLen); end = yyInput + maxLen; /* stardate string */ p = TclUtfFindEqualNCInLwr(p, end, stardatePref, stardatePref + 9, &s); if (p >= end || p - yyInput < 9) { return TCL_RETURN; } /* bypass spaces */ while (p < end && isspace(UCHAR(*p))) { p++; } if (p >= end) { return TCL_RETURN; } /* currently positive stardate only */ if (*p == '+') { p++; }; s = p; while (p < end && isdigit(UCHAR(*p))) { p++; } if (p >= end || p - s < 4) { return TCL_RETURN; } if ( _str2int(&year, s, p-3, 1) != TCL_OK || _str2int(&fractYear, p-3, p, 1) != TCL_OK) { return TCL_RETURN; }; if (*p++ != '.') { return TCL_RETURN; } s = p; fractDayDiv = 1; while (p < end && isdigit(UCHAR(*p))) { fractDayDiv *= 10; p++; } if ( _str2int(&fractDay, s, p, 1) != TCL_OK) { return TCL_RETURN; }; yyInput = p; /* Build a date from year and fraction. */ yydate.year = year + RODDENBERRY; yydate.isBce = 0; yydate.gregorian = 1; if (IsGregorianLeapYear(&yydate)) { fractYear *= 366; } else { fractYear *= 365; } yydate.dayOfYear = fractYear / 1000 + 1; if (fractYear % 1000 >= 500) { yydate.dayOfYear++; } GetJulianDayFromEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); yydate.localSeconds = -210866803200LL + ( SECONDS_PER_DAY * yydate.julianDay ) + ( SECONDS_PER_DAY * fractDay / fractDayDiv ); return TCL_OK; } static const char *ScnSTokenMapIndex = "dmbyYHMSpJjCgGVazUsntQ"; static ClockScanTokenMap ScnSTokenMap[] = { /* %d %e */ {CTOKT_INT, CLF_DAYOFMONTH, 0, 1, 2, offsetof(DateInfo, date.dayOfMonth), NULL, NULL}, /* %m %N */ {CTOKT_INT, CLF_MONTH, 0, 1, 2, offsetof(DateInfo, date.month), NULL, NULL}, /* %b %B %h */ {CTOKT_PARSER, CLF_MONTH, 0, 0, 0xffff, 0, ClockScnToken_Month_Proc, NULL}, /* %y */ {CTOKT_INT, CLF_YEAR, 0, 1, 2, offsetof(DateInfo, date.year), NULL, NULL}, /* %Y */ {CTOKT_INT, CLF_YEAR | CLF_CENTURY, 0, 4, 4, offsetof(DateInfo, date.year), NULL, NULL}, /* %H %k %I %l */ {CTOKT_INT, CLF_TIME, 0, 1, 2, offsetof(DateInfo, date.hour), NULL, NULL}, /* %M */ {CTOKT_INT, CLF_TIME, 0, 1, 2, offsetof(DateInfo, date.minutes), NULL, NULL}, /* %S */ {CTOKT_INT, CLF_TIME, 0, 1, 2, offsetof(DateInfo, date.secondOfMin), NULL, NULL}, /* %p %P */ {CTOKT_PARSER, 0, 0, 0, 0xffff, 0, ClockScnToken_amPmInd_Proc, NULL}, /* %J */ {CTOKT_WIDE, CLF_JULIANDAY | CLF_SIGNED, 0, 1, 0xffff, offsetof(DateInfo, date.julianDay), NULL, NULL}, /* %j */ {CTOKT_INT, CLF_DAYOFYEAR, 0, 1, 3, offsetof(DateInfo, date.dayOfYear), NULL, NULL}, /* %C */ {CTOKT_INT, CLF_CENTURY|CLF_ISO8601CENTURY, 0, 1, 2, offsetof(DateInfo, dateCentury), NULL, NULL}, /* %g */ {CTOKT_INT, CLF_ISO8601YEAR, 0, 2, 2, offsetof(DateInfo, date.iso8601Year), NULL, NULL}, /* %G */ {CTOKT_INT, CLF_ISO8601YEAR | CLF_ISO8601CENTURY, 0, 4, 4, offsetof(DateInfo, date.iso8601Year), NULL, NULL}, /* %V */ {CTOKT_INT, CLF_ISO8601WEAK, 0, 1, 2, offsetof(DateInfo, date.iso8601Week), NULL, NULL}, /* %a %A %u %w */ {CTOKT_PARSER, CLF_DAYOFWEEK, 0, 0, 0xffff, 0, ClockScnToken_DayOfWeek_Proc, NULL}, /* %z %Z */ {CTOKT_PARSER, CLF_OPTIONAL, 0, 0, 0xffff, 0, ClockScnToken_TimeZone_Proc, NULL}, /* %U %W */ {CTOKT_INT, CLF_OPTIONAL, 0, 1, 2, 0, /* currently no capture, parse only token */ NULL, NULL}, /* %s */ {CTOKT_WIDE, CLF_POSIXSEC | CLF_SIGNED, 0, 1, 0xffff, offsetof(DateInfo, date.seconds), NULL, NULL}, /* %n */ {CTOKT_CHAR, 0, 0, 1, 1, 0, NULL, "\n"}, /* %t */ {CTOKT_CHAR, 0, 0, 1, 1, 0, NULL, "\t"}, /* %Q */ {CTOKT_PARSER, CLF_LOCALSEC, 0, 16, 30, 0, ClockScnToken_StarDate_Proc, NULL}, }; static const char *ScnSTokenMapAliasIndex[2] = { "eNBhkIlPAuwZW", "dmbbHHHpaaazU" }; static const char *ScnETokenMapIndex = "EJjys"; static ClockScanTokenMap ScnETokenMap[] = { /* %EE */ {CTOKT_PARSER, 0, 0, 0, 0xffff, offsetof(DateInfo, date.year), ClockScnToken_LocaleERA_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %EJ */ {CTOKT_PARSER, CLF_JULIANDAY | CLF_SIGNED, 0, 1, 0xffff, 0, /* calendar JDN starts at midnight */ ClockScnToken_JDN_Proc, NULL}, /* %Ej */ {CTOKT_PARSER, CLF_JULIANDAY | CLF_SIGNED, 0, 1, 0xffff, (SECONDS_PER_DAY/2), /* astro JDN starts at noon */ ClockScnToken_JDN_Proc, NULL}, /* %Ey */ {CTOKT_PARSER, 0, 0, 0, 0xffff, 0, /* currently no capture, parse only token */ ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %Es */ {CTOKT_WIDE, CLF_LOCALSEC | CLF_SIGNED, 0, 1, 0xffff, offsetof(DateInfo, date.localSeconds), NULL, NULL}, }; static const char *ScnETokenMapAliasIndex[2] = { "", "" }; static const char *ScnOTokenMapIndex = "dmyHMSu"; static ClockScanTokenMap ScnOTokenMap[] = { /* %Od %Oe */ {CTOKT_PARSER, CLF_DAYOFMONTH, 0, 0, 0xffff, offsetof(DateInfo, date.dayOfMonth), ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %Om */ {CTOKT_PARSER, CLF_MONTH, 0, 0, 0xffff, offsetof(DateInfo, date.month), ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %Oy */ {CTOKT_PARSER, CLF_YEAR, 0, 0, 0xffff, offsetof(DateInfo, date.year), ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %OH %Ok %OI %Ol */ {CTOKT_PARSER, CLF_TIME, 0, 0, 0xffff, offsetof(DateInfo, date.hour), ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %OM */ {CTOKT_PARSER, CLF_TIME, 0, 0, 0xffff, offsetof(DateInfo, date.minutes), ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %OS */ {CTOKT_PARSER, CLF_TIME, 0, 0, 0xffff, offsetof(DateInfo, date.secondOfMin), ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %Ou Ow */ {CTOKT_PARSER, CLF_DAYOFWEEK, 0, 0, 0xffff, 0, ClockScnToken_DayOfWeek_Proc, (void *)MCLIT_LOCALE_NUMERALS}, }; static const char *ScnOTokenMapAliasIndex[2] = { "ekIlw", "dHHHu" }; /* Token map reserved for CTOKT_SPACE */ static ClockScanTokenMap ScnSpaceTokenMap = { CTOKT_SPACE, 0, 0, 1, 1, 0, NULL, NULL }; static ClockScanTokenMap ScnWordTokenMap = { CTOKT_WORD, 0, 0, 1, 1, 0, NULL, NULL }; static inline unsigned int EstimateTokenCount( const char *fmt, const char *end) { const char *p = fmt; unsigned int tokcnt; /* estimate token count by % char and format length */ tokcnt = 0; while (p <= end) { if (*p++ == '%') { tokcnt++; p++; } } p = fmt + tokcnt * 2; if (p < end) { if ((unsigned int)(end - p) < tokcnt) { tokcnt += (end - p); } else { tokcnt += tokcnt; } } return ++tokcnt; } #define AllocTokenInChain(tok, chain, tokCnt, type) \ if (++(tok) >= (chain) + (tokCnt)) { \ chain = (type)ckrealloc((char *)(chain), \ (tokCnt + CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE) * sizeof(*(tok))); \ if ((chain) == NULL) { goto done; }; \ (tok) = (chain) + (tokCnt); \ (tokCnt) += CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE; \ } \ memset(tok, 0, sizeof(*(tok))); /* *---------------------------------------------------------------------- */ ClockFmtScnStorage * ClockGetOrParseScanFormat( Tcl_Interp *interp, /* Tcl interpreter */ Tcl_Obj *formatObj) /* Format container */ { ClockFmtScnStorage *fss; fss = Tcl_GetClockFrmScnFromObj(interp, formatObj); if (fss == NULL) { return NULL; } /* if format (scnTok) already tokenized */ if (fss->scnTok != NULL) { return fss; } Tcl_MutexLock(&ClockFmtMutex); /* first time scanning - tokenize format */ if (fss->scnTok == NULL) { ClockScanToken *tok, *scnTok; unsigned int tokCnt; const char *p, *e, *cp; e = p = HashEntry4FmtScn(fss)->key.string; e += strlen(p); /* estimate token count by % char and format length */ fss->scnTokC = EstimateTokenCount(p, e); fss->scnSpaceCount = 0; scnTok = tok = (ClockScanToken *)ckalloc(sizeof(*tok) * fss->scnTokC); memset(tok, 0, sizeof(*(tok))); tokCnt = 1; while (p < e) { switch (*p) { case '%': if (1) { ClockScanTokenMap * scnMap = ScnSTokenMap; const char *mapIndex = ScnSTokenMapIndex, **aliasIndex = ScnSTokenMapAliasIndex; if (p+1 >= e) { goto word_tok; } p++; /* try to find modifier: */ switch (*p) { case '%': /* begin new word token - don't join with previous word token, * because current mapping should be "...%%..." -> "...%..." */ tok->map = &ScnWordTokenMap; tok->tokWord.start = p; tok->tokWord.end = p+1; AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *); tokCnt++; p++; continue; break; case 'E': scnMap = ScnETokenMap, mapIndex = ScnETokenMapIndex, aliasIndex = ScnETokenMapAliasIndex; p++; break; case 'O': scnMap = ScnOTokenMap, mapIndex = ScnOTokenMapIndex, aliasIndex = ScnOTokenMapAliasIndex; p++; break; } /* search direct index */ cp = strchr(mapIndex, *p); if (!cp || *cp == '\0') { /* search wrapper index (multiple chars for same token) */ cp = strchr(aliasIndex[0], *p); if (!cp || *cp == '\0') { p--; if (scnMap != ScnSTokenMap) p--; goto word_tok; } cp = strchr(mapIndex, aliasIndex[1][cp - aliasIndex[0]]); if (!cp || *cp == '\0') { /* unexpected, but ... */ #ifdef DEBUG Tcl_Panic("token \"%c\" has no map in wrapper resolver", *p); #endif p--; if (scnMap != ScnSTokenMap) p--; goto word_tok; } } tok->map = &scnMap[cp - mapIndex]; tok->tokWord.start = p; /* calculate look ahead value by standing together tokens */ if (tok > scnTok) { ClockScanToken *prevTok = tok - 1; while (prevTok >= scnTok) { if (prevTok->map->type != tok->map->type) { break; } prevTok->lookAhMin += tok->map->minSize; prevTok->lookAhMax += tok->map->maxSize; prevTok->lookAhTok++; prevTok--; } } /* increase space count used in format */ if ( tok->map->type == CTOKT_CHAR && isspace(UCHAR(*((char *)tok->map->data))) ) { fss->scnSpaceCount++; } /* next token */ AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *); tokCnt++; p++; continue; } break; default: if ( *p == ' ' || isspace(UCHAR(*p)) ) { tok->map = &ScnSpaceTokenMap; tok->tokWord.start = p++; while (p < e && isspace(UCHAR(*p))) { p++; } tok->tokWord.end = p; /* increase space count used in format */ fss->scnSpaceCount++; /* next token */ AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *); tokCnt++; continue; } word_tok: if (1) { ClockScanToken *wordTok = tok; if (tok > scnTok && (tok-1)->map == &ScnWordTokenMap) { wordTok = tok-1; } /* new word token */ if (wordTok == tok) { wordTok->tokWord.start = p; wordTok->map = &ScnWordTokenMap; AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *); tokCnt++; } if (isspace(UCHAR(*p))) { fss->scnSpaceCount++; } p = Tcl_UtfNext(p); wordTok->tokWord.end = p; } break; } } /* calculate end distance value for each tokens */ if (tok > scnTok) { unsigned int endDist = 0; ClockScanToken *prevTok = tok-1; while (prevTok >= scnTok) { prevTok->endDistance = endDist; if (prevTok->map->type != CTOKT_WORD) { endDist += prevTok->map->minSize; } else { endDist += prevTok->tokWord.end - prevTok->tokWord.start; } prevTok--; } } /* correct count of real used tokens and free mem if desired * (1 is acceptable delta to prevent memory fragmentation) */ if (fss->scnTokC > tokCnt + (CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE / 2)) { if ( (tok = (ClockScanToken *)ckrealloc(scnTok, tokCnt * sizeof(*tok))) != NULL ) { scnTok = tok; } } /* now we're ready - assign now to storage (note the threaded race condition) */ fss->scnTok = scnTok; fss->scnTokC = tokCnt; } done: Tcl_MutexUnlock(&ClockFmtMutex); return fss; } /* *---------------------------------------------------------------------- */ int ClockScan( DateInfo *info, /* Date fields used for parsing & converting */ Tcl_Obj *strObj, /* String containing the time to scan */ ClockFmtScnCmdArgs *opts) /* Command options */ { ClockClientData *dataPtr = (ClockClientData *)opts->clientData; ClockFmtScnStorage *fss; ClockScanToken *tok; ClockScanTokenMap *map; const char *p, *x, *end; unsigned short int flags = 0; int ret = TCL_ERROR; /* get localized format */ if (ClockLocalizeFormat(opts) == NULL) { return TCL_ERROR; } if ( !(fss = ClockGetOrParseScanFormat(opts->interp, opts->formatObj)) || !(tok = fss->scnTok) ) { return TCL_ERROR; } /* prepare parsing */ yyMeridian = MER24; p = TclGetString(strObj); end = p + strObj->length; /* in strict mode - bypass spaces at begin / end only (not between tokens) */ if (opts->flags & CLF_STRICT) { while (p < end && isspace(UCHAR(*p))) { p++; } } yyInput = p; /* look ahead to count spaces (bypass it by count length and distances) */ x = end; while (p < end) { if (isspace(UCHAR(*p))) { x = ++p; /* after first space in space block */ yySpaceCount++; while (p < end && isspace(UCHAR(*p))) { p++; yySpaceCount++; } continue; } x = end; p++; } /* ignore more as 1 space at end */ yySpaceCount -= (end - x); end = x; /* ignore mandatory spaces used in format */ yySpaceCount -= fss->scnSpaceCount; if (yySpaceCount < 0) { yySpaceCount = 0; } info->dateStart = p = yyInput; info->dateEnd = end; /* parse string */ for (; tok->map != NULL; tok++) { map = tok->map; /* bypass spaces at begin of input before parsing each token */ if ( !(opts->flags & CLF_STRICT) && ( map->type != CTOKT_SPACE && map->type != CTOKT_WORD && map->type != CTOKT_CHAR ) ) { while (p < end && isspace(UCHAR(*p))) { yySpaceCount--; p++; } } yyInput = p; /* end of input string */ if (p >= end) { break; } switch (map->type) { case CTOKT_INT: case CTOKT_WIDE: if (1) { int minLen, size; int sign = 1; if (map->flags & CLF_SIGNED) { if (*p == '+') { yyInput = ++p; } else if (*p == '-') { yyInput = ++p; sign = -1; }; } DetermineGreedySearchLen(info, tok, &minLen, &size); if (size < map->minSize) { /* missing input -> error */ if ((map->flags & CLF_OPTIONAL)) { continue; } goto not_match; } /* string 2 number, put number into info structure by offset */ if (map->offs) { p = yyInput; x = p + size; if (map->type == CTOKT_INT) { if (size <= 10) { _str2int_no((int *)(((char *)info) + map->offs), p, x, sign); } else { if (_str2int((int *)(((char *)info) + map->offs), p, x, sign) != TCL_OK) { goto overflow; } } p = x; } else { if (size <= 18) { _str2wideInt_no((Tcl_WideInt *)(((char *)info) + map->offs), p, x, sign); } else { if (_str2wideInt((Tcl_WideInt *)(((char *)info) + map->offs), p, x, sign) != TCL_OK) { goto overflow; } } p = x; } flags = (flags & ~map->clearFlags) | map->flags; } } break; case CTOKT_PARSER: switch (map->parser(opts, info, tok)) { case TCL_OK: break; case TCL_RETURN: if ((map->flags & CLF_OPTIONAL)) { yyInput = p; continue; } goto not_match; break; default: goto done; break; }; /* decrement count for possible spaces in match */ while (p < yyInput) { if (isspace(UCHAR(*p))) { yySpaceCount--; } p++; } p = yyInput; flags = (flags & ~map->clearFlags) | map->flags; break; case CTOKT_SPACE: /* at least one space */ if (!isspace(UCHAR(*p))) { /* unmatched -> error */ goto not_match; } /* don't decrement yySpaceCount by regular (first expected space), * already considered above with fss->scnSpaceCount */; p++; while (p < end && isspace(UCHAR(*p))) { yySpaceCount--; p++; } break; case CTOKT_WORD: x = FindWordEnd(tok, p, end); if (!x) { /* no match -> error */ goto not_match; } p = x; break; case CTOKT_CHAR: x = (char *)map->data; if (*x != *p) { /* no match -> error */ goto not_match; } if (isspace(UCHAR(*x))) { yySpaceCount--; } p++; break; } } /* check end was reached */ if (p < end) { /* in non-strict mode bypass spaces at end of input */ if ( !(opts->flags & CLF_STRICT) && isspace(UCHAR(*p)) ) { p++; while (p < end && isspace(UCHAR(*p))) { p++; } } /* something after last token - wrong format */ if (p < end) { goto not_match; } } /* end of string, check only optional tokens at end, otherwise - not match */ while (tok->map != NULL) { if (!(opts->flags & CLF_STRICT) && (tok->map->type == CTOKT_SPACE)) { tok++; if (tok->map == NULL) { /* no tokens anymore - trailing spaces are mandatory */ goto not_match; } } if (!(tok->map->flags & CLF_OPTIONAL)) { goto not_match; } tok++; } /* * Invalidate result */ flags |= info->flags; /* seconds token (%s) take precedence over all other tokens */ if ((opts->flags & CLF_EXTENDED) || !(flags & CLF_POSIXSEC)) { if (flags & CLF_DATE) { if (!(flags & CLF_JULIANDAY)) { info->flags |= CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY; /* dd precedence below ddd */ switch (flags & (CLF_MONTH|CLF_DAYOFYEAR|CLF_DAYOFMONTH)) { case (CLF_DAYOFYEAR|CLF_DAYOFMONTH): /* miss month: ddd over dd (without month) */ flags &= ~CLF_DAYOFMONTH; /* fallthrough */ case (CLF_DAYOFYEAR): /* ddd over naked weekday */ if (!(flags & CLF_ISO8601YEAR)) { flags &= ~CLF_ISO8601WEAK; } break; case (CLF_MONTH|CLF_DAYOFYEAR|CLF_DAYOFMONTH): /* both available: mmdd over ddd */ case (CLF_MONTH|CLF_DAYOFMONTH): case (CLF_DAYOFMONTH): /* mmdd / dd over naked weekday */ if (!(flags & CLF_ISO8601YEAR)) { flags &= ~CLF_ISO8601WEAK; } break; /* neither mmdd nor ddd available */ case 0: /* but we have day of the week, which can be used */ if (flags & CLF_DAYOFWEEK) { /* prefer week based calculation of julianday */ flags |= CLF_ISO8601WEAK; } } /* YearWeekDay below YearMonthDay */ if ( (flags & CLF_ISO8601WEAK) && ( (flags & (CLF_YEAR|CLF_DAYOFYEAR)) == (CLF_YEAR|CLF_DAYOFYEAR) || (flags & (CLF_YEAR|CLF_DAYOFMONTH|CLF_MONTH)) == (CLF_YEAR|CLF_DAYOFMONTH|CLF_MONTH) ) ) { /* yy precedence below yyyy */ if (!(flags & CLF_ISO8601CENTURY) && (flags & CLF_CENTURY)) { /* normally precedence of ISO is higher, but no century - so put it down */ flags &= ~CLF_ISO8601WEAK; } else /* yymmdd or yyddd over naked weekday */ if (!(flags & CLF_ISO8601YEAR)) { flags &= ~CLF_ISO8601WEAK; } } if ( (flags & CLF_YEAR) ) { if (yyYear < 100) { if (!(flags & CLF_CENTURY)) { if (yyYear >= dataPtr->yearOfCenturySwitch) { yyYear -= 100; } yyYear += dataPtr->currentYearCentury; } else { yyYear += info->dateCentury * 100; } } } if ( (flags & (CLF_ISO8601WEAK|CLF_ISO8601YEAR)) ) { if ((flags & (CLF_ISO8601YEAR|CLF_YEAR)) == CLF_YEAR) { /* for calculations expected iso year */ info->date.iso8601Year = yyYear; } else if (info->date.iso8601Year < 100) { if (!(flags & CLF_ISO8601CENTURY)) { if (info->date.iso8601Year >= dataPtr->yearOfCenturySwitch) { info->date.iso8601Year -= 100; } info->date.iso8601Year += dataPtr->currentYearCentury; } else { info->date.iso8601Year += info->dateCentury * 100; } } if ((flags & (CLF_ISO8601YEAR|CLF_YEAR)) == CLF_ISO8601YEAR) { /* for calculations expected year (e. g. CLF_ISO8601WEAK not set) */ yyYear = info->date.iso8601Year; } } } } /* if no time - reset time */ if (!(flags & (CLF_TIME|CLF_LOCALSEC|CLF_POSIXSEC))) { info->flags |= CLF_ASSEMBLE_SECONDS; yydate.localSeconds = 0; } if (flags & CLF_TIME) { info->flags |= CLF_ASSEMBLE_SECONDS; yySecondOfDay = ToSeconds(yyHour, yyMinutes, yySeconds, yyMeridian); } else if (!(flags & (CLF_LOCALSEC|CLF_POSIXSEC))) { info->flags |= CLF_ASSEMBLE_SECONDS; yySecondOfDay = yydate.localSeconds % SECONDS_PER_DAY; } } /* tell caller which flags were set */ info->flags |= flags; ret = TCL_OK; goto done; overflow: Tcl_SetObjResult(opts->interp, Tcl_NewStringObj("integer value too large to represent", -1)); Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", (char *)NULL); goto done; not_match: #if 1 Tcl_SetObjResult(opts->interp, Tcl_NewStringObj("input string does not match supplied format", -1)); #else /* to debug where exactly scan breaks */ Tcl_SetObjResult(opts->interp, Tcl_ObjPrintf( "input string \"%s\" does not match supplied format \"%s\"," " locale \"%s\" - token \"%s\"", info->dateStart, HashEntry4FmtScn(fss)->key.string, Tcl_GetString(opts->localeObj), tok && tok->tokWord.start ? tok->tokWord.start : "NULL")); #endif Tcl_SetErrorCode(opts->interp, "CLOCK", "badInputString", (char *)NULL); done: return ret; } #define FrmResultIsAllocated(dateFmt) \ (dateFmt->resEnd - dateFmt->resMem > MIN_FMT_RESULT_BLOCK_ALLOC) static inline int FrmResultAllocate( DateFormat *dateFmt, int len) { int needed = dateFmt->output + len - dateFmt->resEnd; if (needed >= 0) { /* >= 0 - regards NTS zero */ int newsize = dateFmt->resEnd - dateFmt->resMem + needed + MIN_FMT_RESULT_BLOCK_ALLOC*2; char *newRes; /* differentiate between stack and memory */ if (!FrmResultIsAllocated(dateFmt)) { newRes = (char *)ckalloc(newsize); if (newRes == NULL) { return TCL_ERROR; } memcpy(newRes, dateFmt->resMem, dateFmt->output - dateFmt->resMem); } else { newRes = (char *)ckrealloc(dateFmt->resMem, newsize); if (newRes == NULL) { return TCL_ERROR; } } dateFmt->output = newRes + (dateFmt->output - dateFmt->resMem); dateFmt->resMem = newRes; dateFmt->resEnd = newRes + newsize; } return TCL_OK; } static int ClockFmtToken_HourAMPM_Proc( TCL_UNUSED(ClockFmtScnCmdArgs *), TCL_UNUSED(DateFormat *), TCL_UNUSED(ClockFormatToken *), int *val) { *val = ( ( *val + SECONDS_PER_DAY - 3600 ) / 3600 ) % 12 + 1; return TCL_OK; } static int ClockFmtToken_AMPM_Proc( ClockFmtScnCmdArgs *opts, DateFormat *dateFmt, ClockFormatToken *tok, int *val) { Tcl_Obj *mcObj; const char *s; Tcl_Size len; if (*val < (SECONDS_PER_DAY / 2)) { mcObj = ClockMCGet(opts, MCLIT_AM); } else { mcObj = ClockMCGet(opts, MCLIT_PM); } if (mcObj == NULL) { return TCL_ERROR; } s = TclGetStringFromObj(mcObj, &len); if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; memcpy(dateFmt->output, s, len + 1); if (*tok->tokWord.start == 'p') { len = Tcl_UtfToUpper(dateFmt->output); } dateFmt->output += len; return TCL_OK; } static int ClockFmtToken_StarDate_Proc( TCL_UNUSED(ClockFmtScnCmdArgs *), DateFormat *dateFmt, TCL_UNUSED(ClockFormatToken *), TCL_UNUSED(int *)) { int fractYear; /* Get day of year, zero based */ int v = dateFmt->date.dayOfYear - 1; /* Convert day of year to a fractional year */ if (IsGregorianLeapYear(&dateFmt->date)) { fractYear = 1000 * v / 366; } else { fractYear = 1000 * v / 365; } /* Put together the StarDate as "Stardate %02d%03d.%1d" */ if (FrmResultAllocate(dateFmt, 30) != TCL_OK) { return TCL_ERROR; }; memcpy(dateFmt->output, "Stardate ", 9); dateFmt->output += 9; dateFmt->output = _itoaw(dateFmt->output, dateFmt->date.year - RODDENBERRY, '0', 2); dateFmt->output = _itoaw(dateFmt->output, fractYear, '0', 3); *dateFmt->output++ = '.'; /* be sure positive after decimal point (note: clock-value can be negative) */ v = dateFmt->date.secondOfDay / ( SECONDS_PER_DAY / 10 ); if (v < 0) v = 10 + v; dateFmt->output = _itoaw(dateFmt->output, v, '0', 1); return TCL_OK; } static int ClockFmtToken_WeekOfYear_Proc( TCL_UNUSED(ClockFmtScnCmdArgs *), DateFormat *dateFmt, ClockFormatToken *tok, int *val) { int dow = dateFmt->date.dayOfWeek; if (*tok->tokWord.start == 'U') { if (dow == 7) { dow = 0; } dow++; } *val = ( dateFmt->date.dayOfYear - dow + 7 ) / 7; return TCL_OK; } static int ClockFmtToken_JDN_Proc( TCL_UNUSED(ClockFmtScnCmdArgs *), DateFormat *dateFmt, ClockFormatToken *tok, TCL_UNUSED(int *)) { Tcl_WideInt intJD = dateFmt->date.julianDay; int fractJD; /* Convert to JDN parts (regarding start offset) and time fraction */ fractJD = dateFmt->date.secondOfDay - (int)tok->map->offs; /* 0 for calendar or 43200 for astro JD */ if (fractJD < 0) { intJD--; fractJD += SECONDS_PER_DAY; } if (fractJD && intJD < 0) { /* avoid jump over 0, by negative JD's */ intJD++; if (intJD == 0) { /* -0.0 / -0.9 has zero integer part, so append "-" extra */ if (FrmResultAllocate(dateFmt, 1) != TCL_OK) { return TCL_ERROR; }; *dateFmt->output++ = '-'; } /* and inverse seconds of day, -0(75) -> -0.25 as float */ fractJD = SECONDS_PER_DAY - fractJD; } /* 21 is max width of (negative) wide-int (rather smaller, but anyway a time fraction below) */ if (FrmResultAllocate(dateFmt, 21) != TCL_OK) { return TCL_ERROR; }; dateFmt->output = _witoaw(dateFmt->output, intJD, '0', 1); /* simplest cases .0 and .5 */ if (!fractJD || fractJD == (SECONDS_PER_DAY / 2)) { /* point + 0 or 5 */ if (FrmResultAllocate(dateFmt, 1+1) != TCL_OK) { return TCL_ERROR; }; *dateFmt->output++ = '.'; *dateFmt->output++ = !fractJD ? '0' : '5'; *dateFmt->output = '\0'; return TCL_OK; } else { /* wrap the time fraction */ #define JDN_MAX_PRECISION 8 #define JDN_MAX_PRECBOUND 100000000 /* 10**JDN_MAX_PRECISION */ char *p; /* to float (part after floating point, + 0.5 to round it up) */ fractJD = (int)( (double)fractJD * JDN_MAX_PRECBOUND / SECONDS_PER_DAY + 0.5 ); /* point + integer (as time fraction after floating point) */ if (FrmResultAllocate(dateFmt, 1+JDN_MAX_PRECISION) != TCL_OK) { return TCL_ERROR; }; *dateFmt->output++ = '.'; p = _itoaw(dateFmt->output, fractJD, '0', JDN_MAX_PRECISION); /* remove trailing zero's */ dateFmt->output++; while (p > dateFmt->output && *(p-1) == '0') {p--;} *p = '\0'; dateFmt->output = p; } return TCL_OK; } static int ClockFmtToken_TimeZone_Proc( ClockFmtScnCmdArgs *opts, DateFormat *dateFmt, ClockFormatToken *tok, TCL_UNUSED(int *)) { if (*tok->tokWord.start == 'z') { int z = dateFmt->date.tzOffset; char sign = '+'; if ( z < 0 ) { z = -z; sign = '-'; } if (FrmResultAllocate(dateFmt, 7) != TCL_OK) { return TCL_ERROR; }; *dateFmt->output++ = sign; dateFmt->output = _itoaw(dateFmt->output, z / 3600, '0', 2); z %= 3600; dateFmt->output = _itoaw(dateFmt->output, z / 60, '0', 2); z %= 60; if (z != 0) { dateFmt->output = _itoaw(dateFmt->output, z, '0', 2); } } else { Tcl_Obj * objPtr; const char *s; Tcl_Size len; /* convert seconds to local seconds to obtain tzName object */ if (ConvertUTCToLocal(opts->clientData, opts->interp, &dateFmt->date, opts->timezoneObj, GREGORIAN_CHANGE_DATE) != TCL_OK) { return TCL_ERROR; }; objPtr = dateFmt->date.tzName; s = TclGetStringFromObj(objPtr, &len); if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; memcpy(dateFmt->output, s, len + 1); dateFmt->output += len; } return TCL_OK; } static int ClockFmtToken_LocaleERA_Proc( ClockFmtScnCmdArgs *opts, DateFormat *dateFmt, TCL_UNUSED(ClockFormatToken *), TCL_UNUSED(int *)) { Tcl_Obj *mcObj; const char *s; Tcl_Size len; if (dateFmt->date.isBce) { mcObj = ClockMCGet(opts, MCLIT_BCE); } else { mcObj = ClockMCGet(opts, MCLIT_CE); } if (mcObj == NULL) { return TCL_ERROR; } s = TclGetStringFromObj(mcObj, &len); if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; memcpy(dateFmt->output, s, len + 1); dateFmt->output += len; return TCL_OK; } static int ClockFmtToken_LocaleERAYear_Proc( ClockFmtScnCmdArgs *opts, DateFormat *dateFmt, ClockFormatToken *tok, int *val) { Tcl_Size rowc; Tcl_Obj **rowv; if (dateFmt->localeEra == NULL) { Tcl_Obj *mcObj = ClockMCGet(opts, MCLIT_LOCALE_ERAS); if (mcObj == NULL) { return TCL_ERROR; } if (TclListObjGetElements(opts->interp, mcObj, &rowc, &rowv) != TCL_OK) { return TCL_ERROR; } if (rowc != 0) { dateFmt->localeEra = LookupLastTransition(opts->interp, dateFmt->date.localSeconds, rowc, rowv, NULL); } if (dateFmt->localeEra == NULL) { dateFmt->localeEra = (Tcl_Obj*)1; } } /* if no LOCALE_ERAS in catalog or era not found */ if (dateFmt->localeEra == (Tcl_Obj*)1) { if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { return TCL_ERROR; }; if (*tok->tokWord.start == 'C') { /* %EC */ *val = dateFmt->date.year / 100; dateFmt->output = _itoaw(dateFmt->output, *val, '0', 2); } else { /* %Ey */ *val = dateFmt->date.year % 100; dateFmt->output = _itoaw(dateFmt->output, *val, '0', 2); } } else { Tcl_Obj *objPtr; const char *s; Tcl_Size len; if (*tok->tokWord.start == 'C') { /* %EC */ if (Tcl_ListObjIndex(opts->interp, dateFmt->localeEra, 1, &objPtr) != TCL_OK ) { return TCL_ERROR; } } else { /* %Ey */ if (Tcl_ListObjIndex(opts->interp, dateFmt->localeEra, 2, &objPtr) != TCL_OK ) { return TCL_ERROR; } if (Tcl_GetIntFromObj(opts->interp, objPtr, val) != TCL_OK) { return TCL_ERROR; } *val = dateFmt->date.year - *val; /* if year in locale numerals */ if (*val >= 0 && *val < 100) { /* year as integer */ Tcl_Obj * mcObj = ClockMCGet(opts, MCLIT_LOCALE_NUMERALS); if (mcObj == NULL) { return TCL_ERROR; } if (Tcl_ListObjIndex(opts->interp, mcObj, *val, &objPtr) != TCL_OK) { return TCL_ERROR; } } else { /* year as integer */ if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { return TCL_ERROR; }; dateFmt->output = _itoaw(dateFmt->output, *val, '0', 2); return TCL_OK; } } s = TclGetStringFromObj(objPtr, &len); if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; memcpy(dateFmt->output, s, len + 1); dateFmt->output += len; } return TCL_OK; } static const char *FmtSTokenMapIndex = "demNbByYCHMSIklpaAuwUVzgGjJsntQ"; static ClockFormatTokenMap FmtSTokenMap[] = { /* %d */ {CTOKT_INT, "0", 2, 0, 0, 0, offsetof(DateFormat, date.dayOfMonth), NULL, NULL}, /* %e */ {CTOKT_INT, " ", 2, 0, 0, 0, offsetof(DateFormat, date.dayOfMonth), NULL, NULL}, /* %m */ {CTOKT_INT, "0", 2, 0, 0, 0, offsetof(DateFormat, date.month), NULL, NULL}, /* %N */ {CTOKT_INT, " ", 2, 0, 0, 0, offsetof(DateFormat, date.month), NULL, NULL}, /* %b %h */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX | CLFMT_DECR, 0, 12, offsetof(DateFormat, date.month), NULL, (void *)MCLIT_MONTHS_ABBREV}, /* %B */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX | CLFMT_DECR, 0, 12, offsetof(DateFormat, date.month), NULL, (void *)MCLIT_MONTHS_FULL}, /* %y */ {CTOKT_INT, "0", 2, 0, 0, 100, offsetof(DateFormat, date.year), NULL, NULL}, /* %Y */ {CTOKT_INT, "0", 4, 0, 0, 0, offsetof(DateFormat, date.year), NULL, NULL}, /* %C */ {CTOKT_INT, "0", 2, 0, 100, 0, offsetof(DateFormat, date.year), NULL, NULL}, /* %H */ {CTOKT_INT, "0", 2, 0, 3600, 24, offsetof(DateFormat, date.secondOfDay), NULL, NULL}, /* %M */ {CTOKT_INT, "0", 2, 0, 60, 60, offsetof(DateFormat, date.secondOfDay), NULL, NULL}, /* %S */ {CTOKT_INT, "0", 2, 0, 0, 60, offsetof(DateFormat, date.secondOfDay), NULL, NULL}, /* %I */ {CTOKT_INT, "0", 2, CLFMT_CALC, 0, 0, offsetof(DateFormat, date.secondOfDay), ClockFmtToken_HourAMPM_Proc, NULL}, /* %k */ {CTOKT_INT, " ", 2, 0, 3600, 24, offsetof(DateFormat, date.secondOfDay), NULL, NULL}, /* %l */ {CTOKT_INT, " ", 2, CLFMT_CALC, 0, 0, offsetof(DateFormat, date.secondOfDay), ClockFmtToken_HourAMPM_Proc, NULL}, /* %p %P */ {CTOKT_INT, NULL, 0, 0, 0, 0, offsetof(DateFormat, date.secondOfDay), ClockFmtToken_AMPM_Proc, NULL}, /* %a */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, offsetof(DateFormat, date.dayOfWeek), NULL, (void *)MCLIT_DAYS_OF_WEEK_ABBREV}, /* %A */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, offsetof(DateFormat, date.dayOfWeek), NULL, (void *)MCLIT_DAYS_OF_WEEK_FULL}, /* %u */ {CTOKT_INT, " ", 1, 0, 0, 0, offsetof(DateFormat, date.dayOfWeek), NULL, NULL}, /* %w */ {CTOKT_INT, " ", 1, 0, 0, 7, offsetof(DateFormat, date.dayOfWeek), NULL, NULL}, /* %U %W */ {CTOKT_INT, "0", 2, CLFMT_CALC, 0, 0, offsetof(DateFormat, date.dayOfYear), ClockFmtToken_WeekOfYear_Proc, NULL}, /* %V */ {CTOKT_INT, "0", 2, 0, 0, 0, offsetof(DateFormat, date.iso8601Week), NULL, NULL}, /* %z %Z */ {CFMTT_PROC, NULL, 0, 0, 0, 0, 0, ClockFmtToken_TimeZone_Proc, NULL}, /* %g */ {CTOKT_INT, "0", 2, 0, 0, 100, offsetof(DateFormat, date.iso8601Year), NULL, NULL}, /* %G */ {CTOKT_INT, "0", 4, 0, 0, 0, offsetof(DateFormat, date.iso8601Year), NULL, NULL}, /* %j */ {CTOKT_INT, "0", 3, 0, 0, 0, offsetof(DateFormat, date.dayOfYear), NULL, NULL}, /* %J */ {CTOKT_WIDE, "0", 7, 0, 0, 0, offsetof(DateFormat, date.julianDay), NULL, NULL}, /* %s */ {CTOKT_WIDE, "0", 1, 0, 0, 0, offsetof(DateFormat, date.seconds), NULL, NULL}, /* %n */ {CTOKT_CHAR, "\n", 0, 0, 0, 0, 0, NULL, NULL}, /* %t */ {CTOKT_CHAR, "\t", 0, 0, 0, 0, 0, NULL, NULL}, /* %Q */ {CFMTT_PROC, NULL, 0, 0, 0, 0, 0, ClockFmtToken_StarDate_Proc, NULL}, }; static const char *FmtSTokenMapAliasIndex[2] = { "hPWZ", "bpUz" }; static const char *FmtETokenMapIndex = "EJjys"; static ClockFormatTokenMap FmtETokenMap[] = { /* %EE */ {CFMTT_PROC, NULL, 0, 0, 0, 0, 0, ClockFmtToken_LocaleERA_Proc, NULL}, /* %EJ */ {CFMTT_PROC, NULL, 0, 0, 0, 0, 0, /* calendar JDN starts at midnight */ ClockFmtToken_JDN_Proc, NULL}, /* %Ej */ {CFMTT_PROC, NULL, 0, 0, 0, 0, (SECONDS_PER_DAY/2), /* astro JDN starts at noon */ ClockFmtToken_JDN_Proc, NULL}, /* %Ey %EC */ {CTOKT_INT, NULL, 0, 0, 0, 0, offsetof(DateFormat, date.year), ClockFmtToken_LocaleERAYear_Proc, NULL}, /* %Es */ {CTOKT_WIDE, "0", 1, 0, 0, 0, offsetof(DateFormat, date.localSeconds), NULL, NULL}, }; static const char *FmtETokenMapAliasIndex[2] = { "C", "y" }; static const char *FmtOTokenMapIndex = "dmyHIMSuw"; static ClockFormatTokenMap FmtOTokenMap[] = { /* %Od %Oe */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, offsetof(DateFormat, date.dayOfMonth), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %Om */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, offsetof(DateFormat, date.month), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %Oy */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, offsetof(DateFormat, date.year), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %OH %Ok */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 3600, 24, offsetof(DateFormat, date.secondOfDay), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %OI %Ol */ {CTOKT_INT, NULL, 0, CLFMT_CALC | CLFMT_LOCALE_INDX, 0, 0, offsetof(DateFormat, date.secondOfDay), ClockFmtToken_HourAMPM_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %OM */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 60, 60, offsetof(DateFormat, date.secondOfDay), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %OS */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 60, offsetof(DateFormat, date.secondOfDay), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %Ou */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, offsetof(DateFormat, date.dayOfWeek), NULL, (void *)MCLIT_LOCALE_NUMERALS}, /* %Ow */ {CTOKT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, offsetof(DateFormat, date.dayOfWeek), NULL, (void *)MCLIT_LOCALE_NUMERALS}, }; static const char *FmtOTokenMapAliasIndex[2] = { "ekl", "dHI" }; static ClockFormatTokenMap FmtWordTokenMap = { CTOKT_WORD, NULL, 0, 0, 0, 0, 0, NULL, NULL }; /* *---------------------------------------------------------------------- */ ClockFmtScnStorage * ClockGetOrParseFmtFormat( Tcl_Interp *interp, /* Tcl interpreter */ Tcl_Obj *formatObj) /* Format container */ { ClockFmtScnStorage *fss; fss = Tcl_GetClockFrmScnFromObj(interp, formatObj); if (fss == NULL) { return NULL; } /* if format (fmtTok) already tokenized */ if (fss->fmtTok != NULL) { return fss; } Tcl_MutexLock(&ClockFmtMutex); /* first time formatting - tokenize format */ if (fss->fmtTok == NULL) { ClockFormatToken *tok, *fmtTok; unsigned int tokCnt; const char *p, *e, *cp; e = p = HashEntry4FmtScn(fss)->key.string; e += strlen(p); /* estimate token count by % char and format length */ fss->fmtTokC = EstimateTokenCount(p, e); fmtTok = tok = (ClockFormatToken *)ckalloc(sizeof(*tok) * fss->fmtTokC); memset(tok, 0, sizeof(*(tok))); tokCnt = 1; while (p < e) { switch (*p) { case '%': if (1) { ClockFormatTokenMap * fmtMap = FmtSTokenMap; const char *mapIndex = FmtSTokenMapIndex, **aliasIndex = FmtSTokenMapAliasIndex; if (p+1 >= e) { goto word_tok; } p++; /* try to find modifier: */ switch (*p) { case '%': /* begin new word token - don't join with previous word token, * because current mapping should be "...%%..." -> "...%..." */ tok->map = &FmtWordTokenMap; tok->tokWord.start = p; tok->tokWord.end = p+1; AllocTokenInChain(tok, fmtTok, fss->fmtTokC, ClockFormatToken *); tokCnt++; p++; continue; break; case 'E': fmtMap = FmtETokenMap, mapIndex = FmtETokenMapIndex, aliasIndex = FmtETokenMapAliasIndex; p++; break; case 'O': fmtMap = FmtOTokenMap, mapIndex = FmtOTokenMapIndex, aliasIndex = FmtOTokenMapAliasIndex; p++; break; } /* search direct index */ cp = strchr(mapIndex, *p); if (!cp || *cp == '\0') { /* search wrapper index (multiple chars for same token) */ cp = strchr(aliasIndex[0], *p); if (!cp || *cp == '\0') { p--; if (fmtMap != FmtSTokenMap) p--; goto word_tok; } cp = strchr(mapIndex, aliasIndex[1][cp - aliasIndex[0]]); if (!cp || *cp == '\0') { /* unexpected, but ... */ #ifdef DEBUG Tcl_Panic("token \"%c\" has no map in wrapper resolver", *p); #endif p--; if (fmtMap != FmtSTokenMap) p--; goto word_tok; } } tok->map = &fmtMap[cp - mapIndex]; tok->tokWord.start = p; /* next token */ AllocTokenInChain(tok, fmtTok, fss->fmtTokC, ClockFormatToken *); tokCnt++; p++; continue; } break; default: word_tok: if (1) { ClockFormatToken *wordTok = tok; if (tok > fmtTok && (tok-1)->map == &FmtWordTokenMap) { wordTok = tok-1; } if (wordTok == tok) { wordTok->tokWord.start = p; wordTok->map = &FmtWordTokenMap; AllocTokenInChain(tok, fmtTok, fss->fmtTokC, ClockFormatToken *); tokCnt++; } p = Tcl_UtfNext(p); wordTok->tokWord.end = p; } break; } } /* correct count of real used tokens and free mem if desired * (1 is acceptable delta to prevent memory fragmentation) */ if (fss->fmtTokC > tokCnt + (CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE / 2)) { if ( (tok = (ClockFormatToken *)ckrealloc(fmtTok, tokCnt * sizeof(*tok))) != NULL ) { fmtTok = tok; } } /* now we're ready - assign now to storage (note the threaded race condition) */ fss->fmtTok = fmtTok; fss->fmtTokC = tokCnt; } done: Tcl_MutexUnlock(&ClockFmtMutex); return fss; } /* *---------------------------------------------------------------------- */ int ClockFormat( DateFormat *dateFmt, /* Date fields used for parsing & converting */ ClockFmtScnCmdArgs *opts) /* Command options */ { ClockFmtScnStorage *fss; ClockFormatToken *tok; ClockFormatTokenMap *map; char resMem[MIN_FMT_RESULT_BLOCK_ALLOC]; /* get localized format */ if (ClockLocalizeFormat(opts) == NULL) { return TCL_ERROR; } if ( !(fss = ClockGetOrParseFmtFormat(opts->interp, opts->formatObj)) || !(tok = fss->fmtTok) ) { return TCL_ERROR; } /* result container object */ dateFmt->resMem = resMem; dateFmt->resEnd = dateFmt->resMem + sizeof(resMem); if (fss->fmtMinAlloc > sizeof(resMem)) { dateFmt->resMem = (char *)ckalloc(fss->fmtMinAlloc); dateFmt->resEnd = dateFmt->resMem + fss->fmtMinAlloc; if (dateFmt->resMem == NULL) { return TCL_ERROR; } } dateFmt->output = dateFmt->resMem; *dateFmt->output = '\0'; /* do format each token */ for (; tok->map != NULL; tok++) { map = tok->map; switch (map->type) { case CTOKT_INT: if (1) { int val = (int)*(int *)(((char *)dateFmt) + map->offs); if (map->fmtproc == NULL) { if (map->flags & CLFMT_DECR) { val--; } if (map->flags & CLFMT_INCR) { val++; } if (map->divider) { val /= map->divider; } if (map->divmod) { val %= map->divmod; } } else { if (map->fmtproc(opts, dateFmt, tok, &val) != TCL_OK) { goto done; } /* if not calculate only (output inside fmtproc) */ if (!(map->flags & CLFMT_CALC)) { continue; } } if (!(map->flags & CLFMT_LOCALE_INDX)) { if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { goto error; }; if (map->width) { dateFmt->output = _itoaw(dateFmt->output, val, *map->tostr, map->width); } else { dateFmt->output += sprintf(dateFmt->output, map->tostr, val); } } else { const char *s; Tcl_Obj * mcObj = ClockMCGet(opts, PTR2INT(map->data) /* mcKey */); if (mcObj == NULL) { goto error; } if ( Tcl_ListObjIndex(opts->interp, mcObj, val, &mcObj) != TCL_OK || mcObj == NULL ) { goto error; } s = TclGetString(mcObj); if (FrmResultAllocate(dateFmt, mcObj->length) != TCL_OK) { goto error; }; memcpy(dateFmt->output, s, mcObj->length + 1); dateFmt->output += mcObj->length; } } break; case CTOKT_WIDE: if (1) { Tcl_WideInt val = *(Tcl_WideInt *)(((char *)dateFmt) + map->offs); if (FrmResultAllocate(dateFmt, 21) != TCL_OK) { goto error; }; if (map->width) { dateFmt->output = _witoaw(dateFmt->output, val, *map->tostr, map->width); } else { dateFmt->output += sprintf(dateFmt->output, map->tostr, val); } } break; case CTOKT_CHAR: if (FrmResultAllocate(dateFmt, 1) != TCL_OK) { goto error; }; *dateFmt->output++ = *map->tostr; break; case CFMTT_PROC: if (map->fmtproc(opts, dateFmt, tok, NULL) != TCL_OK) { goto error; }; break; case CTOKT_WORD: if (1) { Tcl_Size len = tok->tokWord.end - tok->tokWord.start; if (FrmResultAllocate(dateFmt, len) != TCL_OK) { goto error; }; if (len == 1) { *dateFmt->output++ = *tok->tokWord.start; } else { memcpy(dateFmt->output, tok->tokWord.start, len); dateFmt->output += len; } } break; } } goto done; error: if (dateFmt->resMem != resMem) { ckfree(dateFmt->resMem); } dateFmt->resMem = NULL; done: if (dateFmt->resMem) { size_t size; Tcl_Obj *result; TclNewObj(result); result->length = dateFmt->output - dateFmt->resMem; size = result->length+1; if (dateFmt->resMem == resMem) { result->bytes = (char *)ckalloc(size); if (result->bytes == NULL) { return TCL_ERROR; } memcpy(result->bytes, dateFmt->resMem, size); } else if ((dateFmt->resEnd - dateFmt->resMem) / size > MAX_FMT_RESULT_THRESHOLD) { result->bytes = (char *)ckrealloc(dateFmt->resMem, size); if (result->bytes == NULL) { result->bytes = dateFmt->resMem; } } else { result->bytes = dateFmt->resMem; } /* save last used buffer length */ if ( dateFmt->resMem != resMem && fss->fmtMinAlloc < size + MIN_FMT_RESULT_BLOCK_DELTA ) { fss->fmtMinAlloc = size + MIN_FMT_RESULT_BLOCK_DELTA; } result->bytes[result->length] = '\0'; Tcl_SetObjResult(opts->interp, result); return TCL_OK; } return TCL_ERROR; } void ClockFrmScnClearCaches(void) { Tcl_MutexLock(&ClockFmtMutex); /* clear caches ... */ Tcl_MutexUnlock(&ClockFmtMutex); } static void ClockFrmScnFinalize( TCL_UNUSED(void *)) { Tcl_MutexLock(&ClockFmtMutex); #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 /* clear GC */ ClockFmtScnStorage_GC.stackPtr = NULL; ClockFmtScnStorage_GC.stackBound = NULL; ClockFmtScnStorage_GC.count = 0; #endif if (initialized) { Tcl_DeleteHashTable(&FmtScnHashTable); initialized = 0; } Tcl_MutexUnlock(&ClockFmtMutex); Tcl_MutexFinalize(&ClockFmtMutex); } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ |
Changes to generic/tclDate.c.
︙ | ︙ | |||
74 75 76 77 78 79 80 | /* * tclDate.c -- * * This file is generated from a yacc grammar defined in the file * tclGetDate.y. It should not be edited directly. * | | | > < < < | | < < > < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < > > > > > > > | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | /* * tclDate.c -- * * This file is generated from a yacc grammar defined in the file * tclGetDate.y. It should not be edited directly. * * Copyright © 1992-1995 Karl Lehenbauer & Mark Diekhans. * Copyright © 1995-1997 Sun Microsystems, Inc. * Copyright © 2015 Sergey 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. * */ #include "tclInt.h" /* * Bison generates several labels that happen to be unused. MS Visual C++ * doesn't like that, and complains. Tell it to shut up. */ #ifdef _MSC_VER #pragma warning( disable : 4102 ) #endif /* _MSC_VER */ #if 0 #define YYDEBUG 1 #endif /* * yyparse will accept a 'struct DateInfo' as its parameter; that's where the * parsed fields will be returned. */ #include "tclDate.h" #define YYMALLOC ckalloc #define YYFREE(x) (ckfree((void*) (x))) #define EPOCH 1970 #define START_OF_TIME 1902 #define END_OF_TIME 2037 /* * The offset of tm_year of struct tm returned by localtime, gmtime, etc. * Posix requires 1900. */ #define TM_YEAR_BASE 1900 #define HOUR(x) ((60 * (int)(x))) #define IsLeapYear(x) (((x) % 4 == 0) && ((x) % 100 != 0 || (x) % 400 == 0)) #define yyIncrFlags(f) \ do { \ info->errFlags |= (info->flags & (f)); \ if (info->errFlags) { YYABORT; } \ info->flags |= (f); \ } while (0); /* * An entry in the lexical lookup table. */ typedef struct { const char *name; int type; int value; } TABLE; /* * Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { |
︙ | ︙ | |||
255 256 257 258 259 260 261 | tDAYZONE = 260, /* tDAYZONE */ tID = 261, /* tID */ tMERIDIAN = 262, /* tMERIDIAN */ tMONTH = 263, /* tMONTH */ tMONTH_UNIT = 264, /* tMONTH_UNIT */ tSTARDATE = 265, /* tSTARDATE */ tSEC_UNIT = 266, /* tSEC_UNIT */ | | | | > | | | > > | | > | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | tDAYZONE = 260, /* tDAYZONE */ tID = 261, /* tID */ tMERIDIAN = 262, /* tMERIDIAN */ tMONTH = 263, /* tMONTH */ tMONTH_UNIT = 264, /* tMONTH_UNIT */ tSTARDATE = 265, /* tSTARDATE */ tSEC_UNIT = 266, /* tSEC_UNIT */ tUNUMBER = 267, /* tUNUMBER */ tZONE = 268, /* tZONE */ tZONEwO4 = 269, /* tZONEwO4 */ tZONEwO2 = 270, /* tZONEwO2 */ tEPOCH = 271, /* tEPOCH */ tDST = 272, /* tDST */ tISOBAS8 = 273, /* tISOBAS8 */ tISOBAS6 = 274, /* tISOBAS6 */ tISOBASL = 275, /* tISOBASL */ tDAY_UNIT = 276, /* tDAY_UNIT */ tNEXT = 277, /* tNEXT */ SP = 278 /* SP */ }; typedef enum yytokentype yytoken_kind_t; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { Tcl_WideInt Number; enum _MERIDIAN Meridian; }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 |
︙ | ︙ | |||
319 320 321 322 323 324 325 | YYSYMBOL_tDAYZONE = 5, /* tDAYZONE */ YYSYMBOL_tID = 6, /* tID */ YYSYMBOL_tMERIDIAN = 7, /* tMERIDIAN */ YYSYMBOL_tMONTH = 8, /* tMONTH */ YYSYMBOL_tMONTH_UNIT = 9, /* tMONTH_UNIT */ YYSYMBOL_tSTARDATE = 10, /* tSTARDATE */ YYSYMBOL_tSEC_UNIT = 11, /* tSEC_UNIT */ | | | | > | | | > > | | > | | | | > | | | | | > | | > | > | | > > > | | | | | | | > | | | < < | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | YYSYMBOL_tDAYZONE = 5, /* tDAYZONE */ YYSYMBOL_tID = 6, /* tID */ YYSYMBOL_tMERIDIAN = 7, /* tMERIDIAN */ YYSYMBOL_tMONTH = 8, /* tMONTH */ YYSYMBOL_tMONTH_UNIT = 9, /* tMONTH_UNIT */ YYSYMBOL_tSTARDATE = 10, /* tSTARDATE */ YYSYMBOL_tSEC_UNIT = 11, /* tSEC_UNIT */ YYSYMBOL_tUNUMBER = 12, /* tUNUMBER */ YYSYMBOL_tZONE = 13, /* tZONE */ YYSYMBOL_tZONEwO4 = 14, /* tZONEwO4 */ YYSYMBOL_tZONEwO2 = 15, /* tZONEwO2 */ YYSYMBOL_tEPOCH = 16, /* tEPOCH */ YYSYMBOL_tDST = 17, /* tDST */ YYSYMBOL_tISOBAS8 = 18, /* tISOBAS8 */ YYSYMBOL_tISOBAS6 = 19, /* tISOBAS6 */ YYSYMBOL_tISOBASL = 20, /* tISOBASL */ YYSYMBOL_tDAY_UNIT = 21, /* tDAY_UNIT */ YYSYMBOL_tNEXT = 22, /* tNEXT */ YYSYMBOL_SP = 23, /* SP */ YYSYMBOL_24_ = 24, /* ':' */ YYSYMBOL_25_ = 25, /* ',' */ YYSYMBOL_26_ = 26, /* '-' */ YYSYMBOL_27_ = 27, /* '/' */ YYSYMBOL_28_T_ = 28, /* 'T' */ YYSYMBOL_29_ = 29, /* '.' */ YYSYMBOL_30_ = 30, /* '+' */ YYSYMBOL_YYACCEPT = 31, /* $accept */ YYSYMBOL_spec = 32, /* spec */ YYSYMBOL_item = 33, /* item */ YYSYMBOL_iextime = 34, /* iextime */ YYSYMBOL_time = 35, /* time */ YYSYMBOL_zone = 36, /* zone */ YYSYMBOL_comma = 37, /* comma */ YYSYMBOL_day = 38, /* day */ YYSYMBOL_iexdate = 39, /* iexdate */ YYSYMBOL_date = 40, /* date */ YYSYMBOL_ordMonth = 41, /* ordMonth */ YYSYMBOL_isosep = 42, /* isosep */ YYSYMBOL_isodate = 43, /* isodate */ YYSYMBOL_isotime = 44, /* isotime */ YYSYMBOL_iso = 45, /* iso */ YYSYMBOL_trek = 46, /* trek */ YYSYMBOL_relspec = 47, /* relspec */ YYSYMBOL_relunits = 48, /* relunits */ YYSYMBOL_sign = 49, /* sign */ YYSYMBOL_unit = 50, /* unit */ YYSYMBOL_INTNUM = 51, /* INTNUM */ YYSYMBOL_numitem = 52, /* numitem */ YYSYMBOL_o_merid = 53 /* o_merid */ }; typedef enum yysymbol_kind_t yysymbol_kind_t; /* Second part of user prologue. */ /* * Prototypes of internal functions. */ static int LookupWord(YYSTYPE* yylvalPtr, char *buff); static void TclDateerror(YYLTYPE* location, DateInfo* info, const char *s); static int TclDatelex(YYSTYPE* yylvalPtr, YYLTYPE* location, DateInfo* info); MODULE_SCOPE int yyparse(DateInfo*); #ifdef short # undef short |
︙ | ︙ | |||
696 697 698 699 700 701 702 | # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ | | | | | | | | | | | > | > | | | | | | | | > | | | | | | | | | | | | > > | | | > | | | | | | | > | > | | > | > | | | | | < > | | | > | | > | | | < | | | | | | > | | | | | > | < < | | | | > > > > | > | | | | > | 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 98 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 31 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 23 /* YYNRULES -- Number of rules. */ #define YYNRULES 72 /* YYNSTATES -- Number of states. */ #define YYNSTATES 103 /* YYMAXUTOK -- Last valid token kind. */ #define YYMAXUTOK 278 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ (0 <= (YYX) && (YYX) <= YYMAXUTOK \ ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ : YYSYMBOL_YYUNDEF) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex. */ static const yytype_int8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 30, 25, 26, 29, 27, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 24, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { 0, 171, 171, 172, 176, 179, 182, 185, 188, 191, 194, 197, 201, 204, 209, 215, 221, 226, 230, 234, 238, 242, 246, 252, 253, 256, 260, 264, 268, 272, 276, 282, 288, 292, 297, 298, 303, 307, 312, 316, 321, 328, 332, 338, 338, 340, 345, 350, 352, 357, 359, 360, 368, 379, 393, 398, 401, 404, 407, 410, 413, 416, 421, 424, 429, 433, 437, 443, 446, 449, 454, 472, 475 }; #endif /** Accessing symbol of state STATE. */ #define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) #if YYDEBUG || 0 /* The user-facing name of the symbol whose (internal) number is YYSYMBOL. No bounds checking. */ static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "\"end of file\"", "error", "\"invalid token\"", "tAGO", "tDAY", "tDAYZONE", "tID", "tMERIDIAN", "tMONTH", "tMONTH_UNIT", "tSTARDATE", "tSEC_UNIT", "tUNUMBER", "tZONE", "tZONEwO4", "tZONEwO2", "tEPOCH", "tDST", "tISOBAS8", "tISOBAS6", "tISOBASL", "tDAY_UNIT", "tNEXT", "SP", "':'", "','", "'-'", "'/'", "'T'", "'.'", "'+'", "$accept", "spec", "item", "iextime", "time", "zone", "comma", "day", "iexdate", "date", "ordMonth", "isosep", "isodate", "isotime", "iso", "trek", "relspec", "relunits", "sign", "unit", "INTNUM", "numitem", "o_merid", YY_NULLPTR }; static const char * yysymbol_name (yysymbol_kind_t yysymbol) { return yytname[yysymbol]; } #endif #define YYPACT_NINF (-21) #define yypact_value_is_default(Yyn) \ ((Yyn) == YYPACT_NINF) #define YYTABLE_NINF (-68) #define yytable_value_is_error(Yyn) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { -21, 11, -21, -20, -21, 5, -21, -9, -21, 46, 17, 9, 9, -21, -21, -21, 24, -21, 57, -21, -21, -21, 33, -21, -21, -21, -21, -21, -21, -15, -21, -21, -21, 45, 26, -21, -7, -21, 51, -21, -20, -21, -21, -21, 48, -21, -21, 67, 68, 52, 69, -21, -9, -9, -21, -21, -21, -21, 74, -21, -7, -21, -21, -21, -21, 44, -21, 79, 40, -7, -21, -21, 72, 73, -21, 62, 61, 63, 64, -21, -21, -21, -21, 66, -21, -21, -21, -21, 84, -7, -21, -21, -21, 80, 81, 82, 83, -21, -21, -21, -21, -21, -21 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_int8 yydefact[] = { 2, 0, 1, 25, 19, 0, 66, 0, 64, 70, 18, 0, 0, 39, 45, 46, 0, 65, 0, 62, 63, 3, 71, 4, 5, 8, 47, 6, 7, 34, 10, 11, 9, 55, 0, 61, 0, 12, 23, 26, 36, 67, 69, 68, 0, 27, 15, 38, 0, 0, 0, 17, 0, 0, 52, 51, 30, 41, 67, 59, 0, 72, 16, 44, 43, 0, 54, 67, 0, 22, 58, 24, 0, 0, 40, 14, 0, 0, 32, 20, 21, 42, 60, 0, 48, 49, 50, 29, 67, 0, 57, 37, 53, 0, 0, 0, 0, 28, 56, 13, 35, 31, 33 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -21, -21, -21, 31, -21, -21, 58, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -5, -18, -6, -21, -21 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { 0, 1, 21, 22, 23, 24, 39, 25, 26, 27, 28, 65, 29, 86, 30, 31, 32, 33, 34, 35, 36, 37, 62 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { 59, 44, 6, 41, 8, 38, 52, 53, 63, 42, 43, 2, 60, 64, 17, 3, 4, 40, 70, 5, 6, 7, 8, 9, 10, 11, 12, 13, 69, 14, 15, 16, 17, 18, 51, 19, 54, 19, 67, 20, 61, 20, 82, 55, 42, 43, 79, 80, 66, 68, 45, 90, 88, 46, 47, -67, 83, -67, 42, 43, 76, 56, 89, 84, 77, 57, 6, -67, 8, 58, 48, 98, 49, 50, 71, 42, 43, 73, 17, 74, 75, 78, 81, 87, 91, 92, 93, 94, 97, 95, 48, 96, 99, 100, 101, 102, 85, 0, 72 }; static const yytype_int8 yycheck[] = { 18, 7, 9, 12, 11, 25, 11, 12, 23, 18, 19, 0, 18, 28, 21, 4, 5, 12, 36, 8, 9, 10, 11, 12, 13, 14, 15, 16, 34, 18, 19, 20, 21, 22, 17, 26, 12, 26, 12, 30, 7, 30, 60, 19, 18, 19, 52, 53, 3, 23, 4, 69, 12, 7, 8, 9, 12, 11, 18, 19, 8, 4, 68, 19, 12, 8, 9, 21, 11, 12, 24, 89, 26, 27, 23, 18, 19, 29, 21, 12, 12, 12, 8, 4, 12, 12, 24, 26, 4, 26, 24, 27, 12, 12, 12, 12, 65, -1, 40 }; /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of state STATE-NUM. */ static const yytype_int8 yystos[] = { 0, 32, 0, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 26, 30, 33, 34, 35, 36, 38, 39, 40, 41, 43, 45, 46, 47, 48, 49, 50, 51, 52, 25, 37, 12, 12, 18, 19, 51, 4, 7, 8, 24, 26, 27, 17, 49, 49, 12, 19, 4, 8, 12, 50, 51, 7, 53, 23, 28, 42, 3, 12, 23, 51, 50, 23, 37, 29, 12, 12, 8, 12, 12, 51, 51, 8, 50, 12, 19, 34, 44, 4, 12, 51, 50, 12, 12, 24, 26, 26, 27, 4, 50, 12, 12, 12, 12 }; /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const yytype_int8 yyr1[] = { 0, 31, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 42, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 48, 48, 48, 48, 48, 48, 49, 49, 50, 50, 50, 51, 51, 51, 52, 53, 53 }; /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 3, 2, 2, 2, 1, 1, 3, 3, 2, 1, 2, 1, 2, 2, 4, 3, 2, 5, 3, 5, 1, 5, 2, 4, 2, 1, 3, 2, 3, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 4, 2, 1, 4, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 }; enum { YYENOMEM = -2 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) |
︙ | ︙ | |||
1496 1497 1498 1499 1500 1501 1502 | YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); yyerror_range[1] = yyloc; YY_REDUCE_PRINT (yyn); switch (yyn) { case 4: /* item: time */ { | | | | | | | | < > > > > | > > | > > > > | > > > > > | < < < < < < < < | < | < < < | < | < | > > > > > > | > > > > > > | > > | | | | | | | > > > > > > > | | | > > > > > > > | > | | < < < < < < < < | < < < < < < < < | | | | | | | | | | | | | | < | | | > > | > > | > | | | < < < < | < < < < < < < < < < | < < > > > > > > > > > > > > | | | | > > > > > > | | | | | | | | | | | | | | > > > | > > > > > > > > > > > > > > > | | | | 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 | YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); yyerror_range[1] = yyloc; YY_REDUCE_PRINT (yyn); switch (yyn) { case 4: /* item: time */ { yyIncrFlags(CLF_TIME); } break; case 5: /* item: zone */ { yyIncrFlags(CLF_ZONE); } break; case 6: /* item: date */ { yyIncrFlags(CLF_HAVEDATE); } break; case 7: /* item: ordMonth */ { yyIncrFlags(CLF_ORDINALMONTH); } break; case 8: /* item: day */ { yyIncrFlags(CLF_DAYOFWEEK); } break; case 9: /* item: relspec */ { info->flags |= CLF_RELCONV; } break; case 10: /* item: iso */ { yyIncrFlags(CLF_TIME|CLF_HAVEDATE); } break; case 11: /* item: trek */ { yyIncrFlags(CLF_TIME|CLF_HAVEDATE); info->flags |= CLF_RELCONV; } break; case 13: /* iextime: tUNUMBER ':' tUNUMBER ':' tUNUMBER */ { yyHour = (yyvsp[-4].Number); yyMinutes = (yyvsp[-2].Number); yySeconds = (yyvsp[0].Number); } break; case 14: /* iextime: tUNUMBER ':' tUNUMBER */ { yyHour = (yyvsp[-2].Number); yyMinutes = (yyvsp[0].Number); yySeconds = 0; } break; case 15: /* time: tUNUMBER tMERIDIAN */ { yyHour = (yyvsp[-1].Number); yyMinutes = 0; yySeconds = 0; yyMeridian = (yyvsp[0].Meridian); } break; case 16: /* time: iextime o_merid */ { yyMeridian = (yyvsp[0].Meridian); } break; case 17: /* zone: tZONE tDST */ { yyTimezone = (yyvsp[-1].Number); yyDSTmode = DSTon; } break; case 18: /* zone: tZONE */ { yyTimezone = (yyvsp[0].Number); yyDSTmode = DSToff; } break; case 19: /* zone: tDAYZONE */ { yyTimezone = (yyvsp[0].Number); yyDSTmode = DSTon; } break; case 20: /* zone: tZONEwO4 sign INTNUM */ { /* GMT+0100, GMT-1000, etc. */ yyTimezone = (yyvsp[-2].Number) - (yyvsp[-1].Number)*((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60); yyDSTmode = DSToff; } break; case 21: /* zone: tZONEwO2 sign INTNUM */ { /* GMT+1, GMT-10, etc. */ yyTimezone = (yyvsp[-2].Number) - (yyvsp[-1].Number)*((yyvsp[0].Number) * 60); yyDSTmode = DSToff; } break; case 22: /* zone: sign INTNUM */ { /* +0100, -0100 */ yyTimezone = -(yyvsp[-1].Number)*((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60); yyDSTmode = DSToff; } break; case 25: /* day: tDAY */ { yyDayOrdinal = 1; yyDayOfWeek = (yyvsp[0].Number); } break; case 26: /* day: tDAY comma */ { yyDayOrdinal = 1; yyDayOfWeek = (yyvsp[-1].Number); } break; case 27: /* day: tUNUMBER tDAY */ { yyDayOrdinal = (yyvsp[-1].Number); yyDayOfWeek = (yyvsp[0].Number); } break; case 28: /* day: sign SP tUNUMBER tDAY */ { yyDayOrdinal = (yyvsp[-3].Number) * (yyvsp[-1].Number); yyDayOfWeek = (yyvsp[0].Number); } break; case 29: /* day: sign tUNUMBER tDAY */ { yyDayOrdinal = (yyvsp[-2].Number) * (yyvsp[-1].Number); yyDayOfWeek = (yyvsp[0].Number); } break; case 30: /* day: tNEXT tDAY */ { yyDayOrdinal = 2; yyDayOfWeek = (yyvsp[0].Number); } break; case 31: /* iexdate: tUNUMBER '-' tUNUMBER '-' tUNUMBER */ { yyMonth = (yyvsp[-2].Number); yyDay = (yyvsp[0].Number); yyYear = (yyvsp[-4].Number); } break; case 32: /* date: tUNUMBER '/' tUNUMBER */ { yyMonth = (yyvsp[-2].Number); yyDay = (yyvsp[0].Number); } break; case 33: /* date: tUNUMBER '/' tUNUMBER '/' tUNUMBER */ { yyMonth = (yyvsp[-4].Number); yyDay = (yyvsp[-2].Number); yyYear = (yyvsp[0].Number); } break; case 35: /* date: tUNUMBER '-' tMONTH '-' tUNUMBER */ { yyDay = (yyvsp[-4].Number); yyMonth = (yyvsp[-2].Number); yyYear = (yyvsp[0].Number); } break; case 36: /* date: tMONTH tUNUMBER */ { yyMonth = (yyvsp[-1].Number); yyDay = (yyvsp[0].Number); } break; case 37: /* date: tMONTH tUNUMBER comma tUNUMBER */ { yyMonth = (yyvsp[-3].Number); yyDay = (yyvsp[-2].Number); yyYear = (yyvsp[0].Number); } break; case 38: /* date: tUNUMBER tMONTH */ { yyMonth = (yyvsp[0].Number); yyDay = (yyvsp[-1].Number); } break; case 39: /* date: tEPOCH */ { yyMonth = 1; yyDay = 1; yyYear = EPOCH; } break; case 40: /* date: tUNUMBER tMONTH tUNUMBER */ { yyMonth = (yyvsp[-1].Number); yyDay = (yyvsp[-2].Number); yyYear = (yyvsp[0].Number); } break; case 41: /* ordMonth: tNEXT tMONTH */ { yyMonthOrdinalIncr = 1; yyMonthOrdinal = (yyvsp[0].Number); } break; case 42: /* ordMonth: tNEXT tUNUMBER tMONTH */ { yyMonthOrdinalIncr = (yyvsp[-1].Number); yyMonthOrdinal = (yyvsp[0].Number); } break; case 45: /* isodate: tISOBAS8 */ { /* YYYYMMDD */ yyYear = (yyvsp[0].Number) / 10000; yyMonth = ((yyvsp[0].Number) % 10000)/100; yyDay = (yyvsp[0].Number) % 100; } break; case 46: /* isodate: tISOBAS6 */ { /* YYMMDD */ yyYear = (yyvsp[0].Number) / 10000; yyMonth = ((yyvsp[0].Number) % 10000)/100; yyDay = (yyvsp[0].Number) % 100; } break; case 48: /* isotime: tISOBAS6 */ { yyHour = (yyvsp[0].Number) / 10000; yyMinutes = ((yyvsp[0].Number) % 10000)/100; yySeconds = (yyvsp[0].Number) % 100; } break; case 51: /* iso: tISOBASL tISOBAS6 */ { /* YYYYMMDDhhmmss */ yyYear = (yyvsp[-1].Number) / 10000; yyMonth = ((yyvsp[-1].Number) % 10000)/100; yyDay = (yyvsp[-1].Number) % 100; yyHour = (yyvsp[0].Number) / 10000; yyMinutes = ((yyvsp[0].Number) % 10000)/100; yySeconds = (yyvsp[0].Number) % 100; } break; case 52: /* iso: tISOBASL tUNUMBER */ { /* YYYYMMDDhhmm */ if (yyDigitCount != 4) YYABORT; /* normally unreached */ yyYear = (yyvsp[-1].Number) / 10000; yyMonth = ((yyvsp[-1].Number) % 10000)/100; yyDay = (yyvsp[-1].Number) % 100; yyHour = (yyvsp[0].Number) / 100; yyMinutes = ((yyvsp[0].Number) % 100); yySeconds = 0; } break; case 53: /* trek: tSTARDATE INTNUM '.' tUNUMBER */ { /* * Offset computed year by -377 so that the returned years will be * in a range accessible with a 32 bit clock seconds value. */ yyYear = (yyvsp[-2].Number)/1000 + 2323 - 377; yyDay = 1; yyMonth = 1; yyRelDay += (((yyvsp[-2].Number)%1000)*(365 + IsLeapYear(yyYear)))/1000; yyRelSeconds += (yyvsp[0].Number) * (144LL * 60LL); } break; case 54: /* relspec: relunits tAGO */ { yyRelSeconds *= -1; yyRelMonth *= -1; yyRelDay *= -1; } break; case 56: /* relunits: sign SP INTNUM unit */ { *yyRelPointer += (yyvsp[-3].Number) * (yyvsp[-1].Number) * (yyvsp[0].Number); } break; case 57: /* relunits: sign INTNUM unit */ { *yyRelPointer += (yyvsp[-2].Number) * (yyvsp[-1].Number) * (yyvsp[0].Number); } break; case 58: /* relunits: INTNUM unit */ { *yyRelPointer += (yyvsp[-1].Number) * (yyvsp[0].Number); } break; case 59: /* relunits: tNEXT unit */ { *yyRelPointer += (yyvsp[0].Number); } break; case 60: /* relunits: tNEXT INTNUM unit */ { *yyRelPointer += (yyvsp[-1].Number) * (yyvsp[0].Number); } break; case 61: /* relunits: unit */ { *yyRelPointer += (yyvsp[0].Number); } break; case 62: /* sign: '-' */ { (yyval.Number) = -1; } break; case 63: /* sign: '+' */ { (yyval.Number) = 1; } break; case 64: /* unit: tSEC_UNIT */ { (yyval.Number) = (yyvsp[0].Number); yyRelPointer = &yyRelSeconds; } break; case 65: /* unit: tDAY_UNIT */ { (yyval.Number) = (yyvsp[0].Number); yyRelPointer = &yyRelDay; } break; case 66: /* unit: tMONTH_UNIT */ { (yyval.Number) = (yyvsp[0].Number); yyRelPointer = &yyRelMonth; } break; case 67: /* INTNUM: tUNUMBER */ { (yyval.Number) = (yyvsp[0].Number); } break; case 68: /* INTNUM: tISOBAS6 */ { (yyval.Number) = (yyvsp[0].Number); } break; case 69: /* INTNUM: tISOBAS8 */ { (yyval.Number) = (yyvsp[0].Number); } break; case 70: /* numitem: tUNUMBER */ { if ((info->flags & (CLF_TIME|CLF_HAVEDATE|CLF_RELCONV)) == (CLF_TIME|CLF_HAVEDATE)) { yyYear = (yyvsp[0].Number); } else { yyIncrFlags(CLF_TIME); if (yyDigitCount <= 2) { yyHour = (yyvsp[0].Number); yyMinutes = 0; } else { yyHour = (yyvsp[0].Number) / 100; yyMinutes = (yyvsp[0].Number) % 100; } yySeconds = 0; yyMeridian = MER24; } } break; case 71: /* o_merid: %empty */ { (yyval.Meridian) = MER24; } break; case 72: /* o_merid: tMERIDIAN */ { (yyval.Meridian) = (yyvsp[0].Meridian); } break; |
︙ | ︙ | |||
2151 2152 2153 2154 2155 2156 2157 | { "tomorrow", tDAY_UNIT, 1 }, { "yesterday", tDAY_UNIT, -1 }, { "today", tDAY_UNIT, 0 }, { "now", tSEC_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tSEC_UNIT, 0 }, { "next", tNEXT, 1 }, | < < < < < < < < < < < < < < | 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 | { "tomorrow", tDAY_UNIT, 1 }, { "yesterday", tDAY_UNIT, -1 }, { "today", tDAY_UNIT, 0 }, { "now", tSEC_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tSEC_UNIT, 0 }, { "next", tNEXT, 1 }, { "ago", tAGO, 1 }, { "epoch", tEPOCH, 0 }, { "stardate", tSTARDATE, 0 }, { NULL, 0, 0 } }; /* |
︙ | ︙ | |||
2264 2265 2266 2267 2268 2269 2270 | }; /* * Military timezone table. */ static const TABLE MilitaryTable[] = { | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > | | | | | | | | | | 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 | }; /* * Military timezone table. */ static const TABLE MilitaryTable[] = { { "a", tZONE, -HOUR( 1) }, { "b", tZONE, -HOUR( 2) }, { "c", tZONE, -HOUR( 3) }, { "d", tZONE, -HOUR( 4) }, { "e", tZONE, -HOUR( 5) }, { "f", tZONE, -HOUR( 6) }, { "g", tZONE, -HOUR( 7) }, { "h", tZONE, -HOUR( 8) }, { "i", tZONE, -HOUR( 9) }, { "k", tZONE, -HOUR(10) }, { "l", tZONE, -HOUR(11) }, { "m", tZONE, -HOUR(12) }, { "n", tZONE, HOUR( 1) }, { "o", tZONE, HOUR( 2) }, { "p", tZONE, HOUR( 3) }, { "q", tZONE, HOUR( 4) }, { "r", tZONE, HOUR( 5) }, { "s", tZONE, HOUR( 6) }, { "t", tZONE, HOUR( 7) }, { "u", tZONE, HOUR( 8) }, { "v", tZONE, HOUR( 9) }, { "w", tZONE, HOUR( 10) }, { "x", tZONE, HOUR( 11) }, { "y", tZONE, HOUR( 12) }, { "z", tZONE, HOUR( 0) }, { NULL, 0, 0 } }; static inline const char * bypassSpaces( const char *s) { while (TclIsSpaceProc(*s)) { s++; } return s; } /* * Dump error messages in the bit bucket. */ static void TclDateerror( YYLTYPE* location, DateInfo* infoPtr, const char *s) { Tcl_Obj* t; if (!infoPtr->messages) { TclNewObj(infoPtr->messages); } Tcl_AppendToObj(infoPtr->messages, infoPtr->separatrix, -1); Tcl_AppendToObj(infoPtr->messages, s, -1); Tcl_AppendToObj(infoPtr->messages, " (characters ", -1); TclNewIntObj(t, location->first_column); Tcl_IncrRefCount(t); Tcl_AppendObjToObj(infoPtr->messages, t); Tcl_DecrRefCount(t); Tcl_AppendToObj(infoPtr->messages, "-", -1); TclNewIntObj(t, location->last_column); Tcl_IncrRefCount(t); Tcl_AppendObjToObj(infoPtr->messages, t); Tcl_DecrRefCount(t); Tcl_AppendToObj(infoPtr->messages, ")", -1); infoPtr->separatrix = "\n"; } int ToSeconds( int Hours, int Minutes, int Seconds, MERIDIAN Meridian) { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) { return -1; } switch (Meridian) { case MER24: if (Hours < 0 || Hours > 23) { return -1; } return (Hours * 60 + Minutes) * 60 + Seconds; case MERam: if (Hours < 1 || Hours > 12) { return -1; } return ((Hours % 12) * 60 + Minutes) * 60 + Seconds; case MERpm: if (Hours < 1 || Hours > 12) { return -1; } return (((Hours % 12) + 12) * 60 + Minutes) * 60 + Seconds; } return -1; /* Should never be reached */ } static int LookupWord( YYSTYPE* yylvalPtr, char *buff) { char *p; char *q; const TABLE *tp; int i, abbrev; /* * Make it lowercase. */ Tcl_UtfToLower(buff); if (*buff == 'a' && (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0)) { yylvalPtr->Meridian = MERam; return tMERIDIAN; } if (*buff == 'p' && (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0)) { yylvalPtr->Meridian = MERpm; return tMERIDIAN; } /* * See if we have an abbreviation for a month. */ |
︙ | ︙ | |||
2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 | YYLTYPE* location, DateInfo *info) { char c; char *p; char buff[20]; int Count; location->first_column = yyInput - info->dateStart; for ( ; ; ) { | > | > > > | > > | | > > > | | > | > | > > > > > | | < > | > | > > > > | > > > | > < < | > > | < > | | | | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < < < | | | | | | | < | | < < < < < < | < < < | < | | < > | < < < < | | > > > > > > > > > > > > > > > > | > | | > | < | < | > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 | YYLTYPE* location, DateInfo *info) { char c; char *p; char buff[20]; int Count; const char *tokStart; location->first_column = yyInput - info->dateStart; for ( ; ; ) { if (isspace(UCHAR(*yyInput))) { yyInput = bypassSpaces(yyInput); /* ignore space at end of text and before some words */ c = *yyInput; if (c != '\0' && !isalpha(UCHAR(c))) { return SP; } } tokStart = yyInput; if (isdigit(UCHAR(c = *yyInput))) { /* INTL: digit */ /* * Count the number of digits. */ p = (char *)yyInput; while (isdigit(UCHAR(*++p))) {}; yyDigitCount = p - yyInput; /* * A number with 12 or 14 digits is considered an ISO 8601 date. */ if (yyDigitCount == 14 || yyDigitCount == 12) { /* long form of ISO 8601 (without separator), either * YYYYMMDDhhmmss or YYYYMMDDhhmm, so reduce to date * (8 chars is isodate) */ p = (char *)yyInput+8; if (TclAtoWIe(&yylvalPtr->Number, yyInput, p, 1) != TCL_OK) { return tID; /* overflow*/ } yyDigitCount = 8; yyInput = p; location->last_column = yyInput - info->dateStart - 1; return tISOBASL; } /* * Convert the string into a number */ if (TclAtoWIe(&yylvalPtr->Number, yyInput, p, 1) != TCL_OK) { return tID; /* overflow*/ } yyInput = p; /* * A number with 6 or more digits is considered an ISO 8601 base. */ location->last_column = yyInput - info->dateStart - 1; if (yyDigitCount >= 6) { if (yyDigitCount == 8) { return tISOBAS8; } if (yyDigitCount == 6) { return tISOBAS6; } } /* ignore spaces after digits (optional) */ yyInput = bypassSpaces(yyInput); return tUNUMBER; } if (!(c & 0x80) && isalpha(UCHAR(c))) { /* INTL: ISO only. */ int ret; for (p = buff; isalpha(UCHAR(c = *yyInput++)) /* INTL: ISO only. */ || c == '.'; ) { if (p < &buff[sizeof(buff) - 1]) { *p++ = c; } } *p = '\0'; yyInput--; location->last_column = yyInput - info->dateStart - 1; ret = LookupWord(yylvalPtr, buff); /* * lookahead: * for spaces to consider word boundaries (for instance * literal T in isodateTisotimeZ is not a TZ, but Z is UTC); * for +/- digit, to differentiate between "GMT+1000 day" and "GMT +1000 day"; * bypass spaces after token (but ignore by TZ+OFFS), because should * recognize next SP token, if TZ only. */ if (ret == tZONE || ret == tDAYZONE) { c = *yyInput; if (isdigit(UCHAR(c))) { /* literal not a TZ */ yyInput = tokStart; return *yyInput++; } if ((c == '+' || c == '-') && isdigit(UCHAR(*(yyInput+1)))) { if ( !isdigit(UCHAR(*(yyInput+2))) || !isdigit(UCHAR(*(yyInput+3)))) { /* GMT+1, GMT-10, etc. */ return tZONEwO2; } if ( isdigit(UCHAR(*(yyInput+4))) && !isdigit(UCHAR(*(yyInput+5)))) { /* GMT+1000, etc. */ return tZONEwO4; } } } yyInput = bypassSpaces(yyInput); return ret; } if (c != '(') { location->last_column = yyInput - info->dateStart; return *yyInput++; } Count = 0; do { c = *yyInput++; if (c == '\0') { location->last_column = yyInput - info->dateStart - 1; return c; } else if (c == '(') { Count++; } else if (c == ')') { Count--; } } while (Count > 0); } } int TclClockFreeScan( Tcl_Interp *interp, /* Tcl interpreter */ DateInfo *info) /* Input and result parameters */ { int status; #if YYDEBUG /* enable debugging if compiled with YYDEBUG */ yydebug = 1; #endif /* * yyInput = stringToParse; * * ClockInitDateInfo(info) should be executed to pre-init info; */ yyDSTmode = DSTmaybe; info->separatrix = ""; info->dateStart = yyInput; /* ignore spaces at begin */ yyInput = bypassSpaces(yyInput); /* parse */ status = yyparse(info); if (status == 1) { const char *msg = NULL; if (info->errFlags & CLF_HAVEDATE) { msg = "more than one date in string"; } else if (info->errFlags & CLF_TIME) { msg = "more than one time of day in string"; } else if (info->errFlags & CLF_ZONE) { msg = "more than one time zone in string"; } else if (info->errFlags & CLF_DAYOFWEEK) { msg = "more than one weekday in string"; } else if (info->errFlags & CLF_ORDINALMONTH) { msg = "more than one ordinal month in string"; } if (msg) { Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1)); Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", (char *)NULL); } else { Tcl_SetObjResult(interp, info->messages ? info->messages : Tcl_NewObj()); info->messages = NULL; Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "PARSE", (char *)NULL); } status = TCL_ERROR; } else if (status == 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("memory exhausted", -1)); Tcl_SetErrorCode(interp, "TCL", "MEMORY", (char *)NULL); status = TCL_ERROR; } else if (status != 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown status returned " "from date parser. Please " "report this error as a " "bug in Tcl.", -1)); Tcl_SetErrorCode(interp, "TCL", "BUG", (char *)NULL); status = TCL_ERROR; } if (info->messages) { Tcl_DecrRefCount(info->messages); } return status; } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ |
Added generic/tclDate.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | /* * 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!) */ #define CLF_OPTIONAL (1 << 0) /* token is non mandatory */ #define CLF_POSIXSEC (1 << 1) #define CLF_LOCALSEC (1 << 2) #define CLF_JULIANDAY (1 << 3) #define CLF_TIME (1 << 4) #define CLF_ZONE (1 << 5) #define CLF_CENTURY (1 << 6) #define CLF_DAYOFMONTH (1 << 7) #define CLF_DAYOFYEAR (1 << 8) #define CLF_MONTH (1 << 9) #define CLF_YEAR (1 << 10) #define CLF_DAYOFWEEK (1 << 11) #define CLF_ISO8601YEAR (1 << 12) #define CLF_ISO8601WEAK (1 << 13) #define CLF_ISO8601CENTURY (1 << 14) #define CLF_SIGNED (1 << 15) /* extra flags used outside of scan/format-tokens too (int, not a short int) */ #define CLF_RELCONV (1 << 17) #define CLF_ORDINALMONTH (1 << 18) /* On demand (lazy) assemble flags */ #define CLF_ASSEMBLE_DATE (1 << 28) /* assemble year, month, etc. using julianDay */ #define CLF_ASSEMBLE_JULIANDAY (1 << 29) /* assemble julianDay using year, month, etc. */ #define CLF_ASSEMBLE_SECONDS (1 << 30) /* assemble localSeconds (and seconds at end) */ #define CLF_HAVEDATE (CLF_DAYOFMONTH|CLF_MONTH|CLF_YEAR) #define CLF_DATE (CLF_JULIANDAY | CLF_DAYOFMONTH | CLF_DAYOFYEAR | \ CLF_MONTH | CLF_YEAR | CLF_ISO8601YEAR | \ CLF_DAYOFWEEK | CLF_ISO8601WEAK) #define TCL_MIN_SECONDS -0x00F0000000000000L #define TCL_MAX_SECONDS 0x00F0000000000000L #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] */ #define 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] */ #define CLF_VALIDATE_S1 (1 << 0) #define CLF_VALIDATE_S2 (1 << 1) #define CLF_VALIDATE (CLF_VALIDATE_S1|CLF_VALIDATE_S2) #define CLF_EXTENDED (1 << 4) #define CLF_STRICT (1 << 8) #define CLF_LOCALE_USED (1 << 15) typedef struct ClockFmtScnCmdArgs { void *clientData; /* Opaque 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 int type; unsigned short int flags; unsigned short int clearFlags; unsigned short int minSize; unsigned short int maxSize; unsigned short int offs; ClockScanTokenProc *parser; const void *data; } ClockScanTokenMap; struct ClockScanToken { ClockScanTokenMap *map; struct { const char *start; const char *end; } tokWord; unsigned short int endDistance; unsigned short int lookAhMin; unsigned short int lookAhMax; unsigned short int 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; #define CLFMT_INCR (1 << 3) #define CLFMT_DECR (1 << 4) #define CLFMT_CALC (1 << 5) #define 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 int type; const char *tostr; unsigned short int width; unsigned short int flags; unsigned short int divider; unsigned short int divmod; unsigned short int offs; ClockFormatTokenProc *fmtproc; void *data; } ClockFormatTokenMap; struct ClockFormatToken { 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 int scnTokC; unsigned int scnSpaceCount; /* Count of mandatory spaces used in format */ ClockFormatToken *fmtTok; unsigned int 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(void *clientData, 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(void *clientData, 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 int 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); #endif /* _TCLCLOCK_H */ |
Changes to generic/tclDictObj.c.
︙ | ︙ | |||
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 | Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); return TCL_ERROR; } result = Tcl_DictObjSize(interp, objv[1], &size); if (result == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewWideIntObj(size)); } return result; } /* *---------------------------------------------------------------------- * * DictExistsCmd -- | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 | Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); return TCL_ERROR; } result = Tcl_DictObjSize(interp, objv[1], &size); if (result == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewWideIntObj(size)); } return result; } /* *---------------------------------------------------------------------- * * TclDictObjSmartRef -- * * This function returns new tcl-object with the smart reference to * dictionary object. * * Object returned with this function is a smart reference (pointer), * so new object of type tclDictType, that directly references given * dictionary object (with internally increased refCount). * * The usage of such pointer objects allows to hold more as one * reference to the same real dictionary object, allows to make a pointer * to part of another dictionary, allows to change the dictionary without * regarding of the "shared" state of the dictionary object. * * Prevents "called with shared object" exception if object is multiple * referenced. * * Results: * The newly create object (contains smart reference) is returned. * The returned object has a ref count of 0. * * Side effects: * Increases ref count of the referenced dictionary. * *---------------------------------------------------------------------- */ Tcl_Obj * TclDictObjSmartRef( Tcl_Interp *interp, Tcl_Obj *dictPtr) { Tcl_Obj *result; Dict *dict; if (dictPtr->typePtr != &tclDictType && SetDictFromAny(interp, dictPtr) != TCL_OK) { return NULL; } DictGetInternalRep(dictPtr, dict); result = Tcl_NewObj(); DictSetInternalRep(result, dict); dict->refCount++; result->internalRep.twoPtrValue.ptr2 = NULL; result->typePtr = &tclDictType; return result; } /* *---------------------------------------------------------------------- * * DictExistsCmd -- |
︙ | ︙ |
Changes to generic/tclGetDate.y.
1 2 3 4 5 6 7 8 9 | /* * tclGetDate.y -- * * Contains yacc grammar for parsing date and time strings. The output of * this file should be the file tclDate.c which is used directly in the * Tcl sources. Note that this file is largely obsolete in Tcl 8.5; it is * only used when doing free-form date parsing, an ill-defined process * anyway. * | | | > | | > < < < | | < < > < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < | < > > > > > > > | | | | | < < | | | > | > > > | | > > | > > > | | | | | | | < > | < < | > > > > > > > > > > > | < < < | < < < < < < < < > > > > > > > > | > > > > | | | | > > > > | | > > > > > > | < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | /* * tclGetDate.y -- * * Contains yacc grammar for parsing date and time strings. The output of * this file should be the file tclDate.c which is used directly in the * Tcl sources. Note that this file is largely obsolete in Tcl 8.5; it is * only used when doing free-form date parsing, an ill-defined process * anyway. * * Copyright © 1992-1995 Karl Lehenbauer & Mark Diekhans. * Copyright © 1995-1997 Sun Microsystems, Inc. * Copyright © 2015 Sergey 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. */ %parse-param {DateInfo* info} %lex-param {DateInfo* info} %define api.pure /* %error-verbose would be nice, but our token names are meaningless */ %locations %{ /* * tclDate.c -- * * This file is generated from a yacc grammar defined in the file * tclGetDate.y. It should not be edited directly. * * Copyright © 1992-1995 Karl Lehenbauer & Mark Diekhans. * Copyright © 1995-1997 Sun Microsystems, Inc. * Copyright © 2015 Sergey 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. * */ #include "tclInt.h" /* * Bison generates several labels that happen to be unused. MS Visual C++ * doesn't like that, and complains. Tell it to shut up. */ #ifdef _MSC_VER #pragma warning( disable : 4102 ) #endif /* _MSC_VER */ #if 0 #define YYDEBUG 1 #endif /* * yyparse will accept a 'struct DateInfo' as its parameter; that's where the * parsed fields will be returned. */ #include "tclDate.h" #define YYMALLOC ckalloc #define YYFREE(x) (ckree((void*) (x))) #define EPOCH 1970 #define START_OF_TIME 1902 #define END_OF_TIME 2037 /* * The offset of tm_year of struct tm returned by localtime, gmtime, etc. * Posix requires 1900. */ #define TM_YEAR_BASE 1900 #define HOUR(x) ((60 * (int)(x))) #define IsLeapYear(x) (((x) % 4 == 0) && ((x) % 100 != 0 || (x) % 400 == 0)) #define yyIncrFlags(f) \ do { \ info->errFlags |= (info->flags & (f)); \ if (info->errFlags) { YYABORT; } \ info->flags |= (f); \ } while (0); /* * An entry in the lexical lookup table. */ typedef struct { const char *name; int type; int value; } TABLE; /* * Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; %} %union { Tcl_WideInt Number; enum _MERIDIAN Meridian; } %{ /* * Prototypes of internal functions. */ static int LookupWord(YYSTYPE* yylvalPtr, char *buff); static void TclDateerror(YYLTYPE* location, DateInfo* info, const char *s); static int TclDatelex(YYSTYPE* yylvalPtr, YYLTYPE* location, DateInfo* info); MODULE_SCOPE int yyparse(DateInfo*); %} %token tAGO %token tDAY %token tDAYZONE %token tID %token tMERIDIAN %token tMONTH %token tMONTH_UNIT %token tSTARDATE %token tSEC_UNIT %token tUNUMBER %token tZONE %token tZONEwO4 %token tZONEwO2 %token tEPOCH %token tDST %token tISOBAS8 %token tISOBAS6 %token tISOBASL %token tDAY_UNIT %token tNEXT %token SP %type <Number> tDAY %type <Number> tDAYZONE %type <Number> tMONTH %type <Number> tMONTH_UNIT %type <Number> tDST %type <Number> tSEC_UNIT %type <Number> tUNUMBER %type <Number> INTNUM %type <Number> tZONE %type <Number> tZONEwO4 %type <Number> tZONEwO2 %type <Number> tISOBAS8 %type <Number> tISOBAS6 %type <Number> tISOBASL %type <Number> tDAY_UNIT %type <Number> unit %type <Number> sign %type <Number> tNEXT %type <Number> tSTARDATE %type <Meridian> tMERIDIAN %type <Meridian> o_merid %% spec : /* NULL */ | spec item /* | spec SP item */ ; item : time { yyIncrFlags(CLF_TIME); } | zone { yyIncrFlags(CLF_ZONE); } | date { yyIncrFlags(CLF_HAVEDATE); } | ordMonth { yyIncrFlags(CLF_ORDINALMONTH); } | day { yyIncrFlags(CLF_DAYOFWEEK); } | relspec { info->flags |= CLF_RELCONV; } | iso { yyIncrFlags(CLF_TIME|CLF_HAVEDATE); } | trek { yyIncrFlags(CLF_TIME|CLF_HAVEDATE); info->flags |= CLF_RELCONV; } | numitem ; iextime : tUNUMBER ':' tUNUMBER ':' tUNUMBER { yyHour = $1; yyMinutes = $3; yySeconds = $5; } | tUNUMBER ':' tUNUMBER { yyHour = $1; yyMinutes = $3; yySeconds = 0; } ; time : tUNUMBER tMERIDIAN { yyHour = $1; yyMinutes = 0; yySeconds = 0; yyMeridian = $2; } | iextime o_merid { yyMeridian = $2; } ; zone : tZONE tDST { yyTimezone = $1; yyDSTmode = DSTon; } | tZONE { yyTimezone = $1; yyDSTmode = DSToff; } | tDAYZONE { yyTimezone = $1; yyDSTmode = DSTon; } | tZONEwO4 sign INTNUM { /* GMT+0100, GMT-1000, etc. */ yyTimezone = $1 - $2*($3 % 100 + ($3 / 100) * 60); yyDSTmode = DSToff; } | tZONEwO2 sign INTNUM { /* GMT+1, GMT-10, etc. */ yyTimezone = $1 - $2*($3 * 60); yyDSTmode = DSToff; } | sign INTNUM { /* +0100, -0100 */ yyTimezone = -$1*($2 % 100 + ($2 / 100) * 60); yyDSTmode = DSToff; } ; comma : ',' | ',' SP ; day : tDAY { yyDayOrdinal = 1; yyDayOfWeek = $1; } | tDAY comma { yyDayOrdinal = 1; yyDayOfWeek = $1; } | tUNUMBER tDAY { yyDayOrdinal = $1; yyDayOfWeek = $2; } | sign SP tUNUMBER tDAY { yyDayOrdinal = $1 * $3; yyDayOfWeek = $4; } | sign tUNUMBER tDAY { yyDayOrdinal = $1 * $2; yyDayOfWeek = $3; } | tNEXT tDAY { yyDayOrdinal = 2; yyDayOfWeek = $2; } ; iexdate : tUNUMBER '-' tUNUMBER '-' tUNUMBER { yyMonth = $3; yyDay = $5; yyYear = $1; } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; yyYear = $5; } | isodate | tUNUMBER '-' tMONTH '-' tUNUMBER { yyDay = $1; yyMonth = $3; yyYear = $5; } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER comma tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyMonth = $2; yyDay = $1; |
︙ | ︙ | |||
362 363 364 365 366 367 368 | yyMonth = $2; yyDay = $1; yyYear = $3; } ; ordMonth: tNEXT tMONTH { | | | | | | < > | | | | < < < | < < < < > > | < < < < | | | > > > | > > > > > > > > > | | | > > > | | | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | yyMonth = $2; yyDay = $1; yyYear = $3; } ; ordMonth: tNEXT tMONTH { yyMonthOrdinalIncr = 1; yyMonthOrdinal = $2; } | tNEXT tUNUMBER tMONTH { yyMonthOrdinalIncr = $2; yyMonthOrdinal = $3; } ; isosep : 'T'|SP ; isodate : tISOBAS8 { /* YYYYMMDD */ yyYear = $1 / 10000; yyMonth = ($1 % 10000)/100; yyDay = $1 % 100; } | tISOBAS6 { /* YYMMDD */ yyYear = $1 / 10000; yyMonth = ($1 % 10000)/100; yyDay = $1 % 100; } | iexdate ; isotime : tISOBAS6 { yyHour = $1 / 10000; yyMinutes = ($1 % 10000)/100; yySeconds = $1 % 100; } | iextime ; iso : isodate isosep isotime | tISOBASL tISOBAS6 { /* YYYYMMDDhhmmss */ yyYear = $1 / 10000; yyMonth = ($1 % 10000)/100; yyDay = $1 % 100; yyHour = $2 / 10000; yyMinutes = ($2 % 10000)/100; yySeconds = $2 % 100; } | tISOBASL tUNUMBER { /* YYYYMMDDhhmm */ if (yyDigitCount != 4) YYABORT; /* normally unreached */ yyYear = $1 / 10000; yyMonth = ($1 % 10000)/100; yyDay = $1 % 100; yyHour = $2 / 100; yyMinutes = ($2 % 100); yySeconds = 0; } ; trek : tSTARDATE INTNUM '.' tUNUMBER { /* * Offset computed year by -377 so that the returned years will be * in a range accessible with a 32 bit clock seconds value. */ yyYear = $2/1000 + 2323 - 377; yyDay = 1; yyMonth = 1; yyRelDay += (($2%1000)*(365 + IsLeapYear(yyYear)))/1000; yyRelSeconds += $4 * (144LL * 60LL); } ; relspec : relunits tAGO { yyRelSeconds *= -1; yyRelMonth *= -1; yyRelDay *= -1; } | relunits ; relunits : sign SP INTNUM unit { *yyRelPointer += $1 * $3 * $4; } | sign INTNUM unit { *yyRelPointer += $1 * $2 * $3; } | INTNUM unit { *yyRelPointer += $1 * $2; } | tNEXT unit { *yyRelPointer += $2; } | tNEXT INTNUM unit { *yyRelPointer += $2 * $3; } | unit { *yyRelPointer += $1; } ; |
︙ | ︙ | |||
470 471 472 473 474 475 476 | } | tMONTH_UNIT { $$ = $1; yyRelPointer = &yyRelMonth; } ; | | > > > > > > > > > | > > | | 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | } | tMONTH_UNIT { $$ = $1; yyRelPointer = &yyRelMonth; } ; INTNUM : tUNUMBER { $$ = $1; } | tISOBAS6 { $$ = $1; } | tISOBAS8 { $$ = $1; } ; numitem : tUNUMBER { if ((info->flags & (CLF_TIME|CLF_HAVEDATE|CLF_RELCONV)) == (CLF_TIME|CLF_HAVEDATE)) { yyYear = $1; } else { yyIncrFlags(CLF_TIME); if (yyDigitCount <= 2) { yyHour = $1; yyMinutes = 0; } else { yyHour = $1 / 100; yyMinutes = $1 % 100; } |
︙ | ︙ | |||
559 560 561 562 563 564 565 | { "tomorrow", tDAY_UNIT, 1 }, { "yesterday", tDAY_UNIT, -1 }, { "today", tDAY_UNIT, 0 }, { "now", tSEC_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tSEC_UNIT, 0 }, { "next", tNEXT, 1 }, | < < < < < < < < < < < < < < | 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | { "tomorrow", tDAY_UNIT, 1 }, { "yesterday", tDAY_UNIT, -1 }, { "today", tDAY_UNIT, 0 }, { "now", tSEC_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tSEC_UNIT, 0 }, { "next", tNEXT, 1 }, { "ago", tAGO, 1 }, { "epoch", tEPOCH, 0 }, { "stardate", tSTARDATE, 0 }, { NULL, 0, 0 } }; /* |
︙ | ︙ | |||
672 673 674 675 676 677 678 | }; /* * Military timezone table. */ static const TABLE MilitaryTable[] = { | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > | | | | | | | | | | 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | }; /* * Military timezone table. */ static const TABLE MilitaryTable[] = { { "a", tZONE, -HOUR( 1) }, { "b", tZONE, -HOUR( 2) }, { "c", tZONE, -HOUR( 3) }, { "d", tZONE, -HOUR( 4) }, { "e", tZONE, -HOUR( 5) }, { "f", tZONE, -HOUR( 6) }, { "g", tZONE, -HOUR( 7) }, { "h", tZONE, -HOUR( 8) }, { "i", tZONE, -HOUR( 9) }, { "k", tZONE, -HOUR(10) }, { "l", tZONE, -HOUR(11) }, { "m", tZONE, -HOUR(12) }, { "n", tZONE, HOUR( 1) }, { "o", tZONE, HOUR( 2) }, { "p", tZONE, HOUR( 3) }, { "q", tZONE, HOUR( 4) }, { "r", tZONE, HOUR( 5) }, { "s", tZONE, HOUR( 6) }, { "t", tZONE, HOUR( 7) }, { "u", tZONE, HOUR( 8) }, { "v", tZONE, HOUR( 9) }, { "w", tZONE, HOUR( 10) }, { "x", tZONE, HOUR( 11) }, { "y", tZONE, HOUR( 12) }, { "z", tZONE, HOUR( 0) }, { NULL, 0, 0 } }; static inline const char * bypassSpaces( const char *s) { while (TclIsSpaceProc(*s)) { s++; } return s; } /* * Dump error messages in the bit bucket. */ static void TclDateerror( YYLTYPE* location, DateInfo* infoPtr, const char *s) { Tcl_Obj* t; if (!infoPtr->messages) { TclNewObj(infoPtr->messages); } Tcl_AppendToObj(infoPtr->messages, infoPtr->separatrix, -1); Tcl_AppendToObj(infoPtr->messages, s, -1); Tcl_AppendToObj(infoPtr->messages, " (characters ", -1); TclNewIntObj(t, location->first_column); Tcl_IncrRefCount(t); Tcl_AppendObjToObj(infoPtr->messages, t); Tcl_DecrRefCount(t); Tcl_AppendToObj(infoPtr->messages, "-", -1); TclNewIntObj(t, location->last_column); Tcl_IncrRefCount(t); Tcl_AppendObjToObj(infoPtr->messages, t); Tcl_DecrRefCount(t); Tcl_AppendToObj(infoPtr->messages, ")", -1); infoPtr->separatrix = "\n"; } int ToSeconds( int Hours, int Minutes, int Seconds, MERIDIAN Meridian) { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) { return -1; } switch (Meridian) { case MER24: if (Hours < 0 || Hours > 23) { return -1; } return (Hours * 60 + Minutes) * 60 + Seconds; case MERam: if (Hours < 1 || Hours > 12) { return -1; } return ((Hours % 12) * 60 + Minutes) * 60 + Seconds; case MERpm: if (Hours < 1 || Hours > 12) { return -1; } return (((Hours % 12) + 12) * 60 + Minutes) * 60 + Seconds; } return -1; /* Should never be reached */ } static int LookupWord( YYSTYPE* yylvalPtr, char *buff) { char *p; char *q; const TABLE *tp; int i, abbrev; /* * Make it lowercase. */ Tcl_UtfToLower(buff); if (*buff == 'a' && (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0)) { yylvalPtr->Meridian = MERam; return tMERIDIAN; } if (*buff == 'p' && (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0)) { yylvalPtr->Meridian = MERpm; return tMERIDIAN; } /* * See if we have an abbreviation for a month. */ |
︙ | ︙ | |||
891 892 893 894 895 896 897 898 899 900 | YYLTYPE* location, DateInfo *info) { char c; char *p; char buff[20]; int Count; location->first_column = yyInput - info->dateStart; for ( ; ; ) { | > | > > > | > > | | > > > | | > | > | > > > > > | | < > | > | > > > > | > > > | > < < | > > | < > | | | | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < < < | | | | | | | < | | < < < < < < | < < < | < | | < > | < < < < | | > > > > > > > > > > > > > > > > | > | | > | < | < | > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | YYLTYPE* location, DateInfo *info) { char c; char *p; char buff[20]; int Count; const char *tokStart; location->first_column = yyInput - info->dateStart; for ( ; ; ) { if (isspace(UCHAR(*yyInput))) { yyInput = bypassSpaces(yyInput); /* ignore space at end of text and before some words */ c = *yyInput; if (c != '\0' && !isalpha(UCHAR(c))) { return SP; } } tokStart = yyInput; if (isdigit(UCHAR(c = *yyInput))) { /* INTL: digit */ /* * Count the number of digits. */ p = (char *)yyInput; while (isdigit(UCHAR(*++p))) {}; yyDigitCount = p - yyInput; /* * A number with 12 or 14 digits is considered an ISO 8601 date. */ if (yyDigitCount == 14 || yyDigitCount == 12) { /* long form of ISO 8601 (without separator), either * YYYYMMDDhhmmss or YYYYMMDDhhmm, so reduce to date * (8 chars is isodate) */ p = (char *)yyInput+8; if (TclAtoWIe(&yylvalPtr->Number, yyInput, p, 1) != TCL_OK) { return tID; /* overflow*/ } yyDigitCount = 8; yyInput = p; location->last_column = yyInput - info->dateStart - 1; return tISOBASL; } /* * Convert the string into a number */ if (TclAtoWIe(&yylvalPtr->Number, yyInput, p, 1) != TCL_OK) { return tID; /* overflow*/ } yyInput = p; /* * A number with 6 or more digits is considered an ISO 8601 base. */ location->last_column = yyInput - info->dateStart - 1; if (yyDigitCount >= 6) { if (yyDigitCount == 8) { return tISOBAS8; } if (yyDigitCount == 6) { return tISOBAS6; } } /* ignore spaces after digits (optional) */ yyInput = bypassSpaces(yyInput); return tUNUMBER; } if (!(c & 0x80) && isalpha(UCHAR(c))) { /* INTL: ISO only. */ int ret; for (p = buff; isalpha(UCHAR(c = *yyInput++)) /* INTL: ISO only. */ || c == '.'; ) { if (p < &buff[sizeof(buff) - 1]) { *p++ = c; } } *p = '\0'; yyInput--; location->last_column = yyInput - info->dateStart - 1; ret = LookupWord(yylvalPtr, buff); /* * lookahead: * for spaces to consider word boundaries (for instance * literal T in isodateTisotimeZ is not a TZ, but Z is UTC); * for +/- digit, to differentiate between "GMT+1000 day" and "GMT +1000 day"; * bypass spaces after token (but ignore by TZ+OFFS), because should * recognize next SP token, if TZ only. */ if (ret == tZONE || ret == tDAYZONE) { c = *yyInput; if (isdigit(UCHAR(c))) { /* literal not a TZ */ yyInput = tokStart; return *yyInput++; } if ((c == '+' || c == '-') && isdigit(UCHAR(*(yyInput+1)))) { if ( !isdigit(UCHAR(*(yyInput+2))) || !isdigit(UCHAR(*(yyInput+3)))) { /* GMT+1, GMT-10, etc. */ return tZONEwO2; } if ( isdigit(UCHAR(*(yyInput+4))) && !isdigit(UCHAR(*(yyInput+5)))) { /* GMT+1000, etc. */ return tZONEwO4; } } } yyInput = bypassSpaces(yyInput); return ret; } if (c != '(') { location->last_column = yyInput - info->dateStart; return *yyInput++; } Count = 0; do { c = *yyInput++; if (c == '\0') { location->last_column = yyInput - info->dateStart - 1; return c; } else if (c == '(') { Count++; } else if (c == ')') { Count--; } } while (Count > 0); } } int TclClockFreeScan( Tcl_Interp *interp, /* Tcl interpreter */ DateInfo *info) /* Input and result parameters */ { int status; #if YYDEBUG /* enable debugging if compiled with YYDEBUG */ yydebug = 1; #endif /* * yyInput = stringToParse; * * ClockInitDateInfo(info) should be executed to pre-init info; */ yyDSTmode = DSTmaybe; info->separatrix = ""; info->dateStart = yyInput; /* ignore spaces at begin */ yyInput = bypassSpaces(yyInput); /* parse */ status = yyparse(info); if (status == 1) { const char *msg = NULL; if (info->errFlags & CLF_HAVEDATE) { msg = "more than one date in string"; } else if (info->errFlags & CLF_TIME) { msg = "more than one time of day in string"; } else if (info->errFlags & CLF_ZONE) { msg = "more than one time zone in string"; } else if (info->errFlags & CLF_DAYOFWEEK) { msg = "more than one weekday in string"; } else if (info->errFlags & CLF_ORDINALMONTH) { msg = "more than one ordinal month in string"; } if (msg) { Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1)); Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", (char *)NULL); } else { Tcl_SetObjResult(interp, info->messages ? info->messages : Tcl_NewObj()); info->messages = NULL; Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "PARSE", (char *)NULL); } status = TCL_ERROR; } else if (status == 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("memory exhausted", -1)); Tcl_SetErrorCode(interp, "TCL", "MEMORY", (char *)NULL); status = TCL_ERROR; } else if (status != 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown status returned " "from date parser. Please " "report this error as a " "bug in Tcl.", -1)); Tcl_SetErrorCode(interp, "TCL", "BUG", (char *)NULL); status = TCL_ERROR; } if (info->messages) { Tcl_DecrRefCount(info->messages); } return status; } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ |
Changes to generic/tclInt.h.
︙ | ︙ | |||
3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 | Tcl_Namespace *ensembleNamespacePtr, int flags); MODULE_SCOPE void TclDeleteNamespaceVars(Namespace *nsPtr); MODULE_SCOPE void TclDeleteNamespaceChildren(Namespace *nsPtr); MODULE_SCOPE int TclFindDictElement(Tcl_Interp *interp, const char *dict, Tcl_Size dictLength, const char **elementPtr, const char **nextPtr, Tcl_Size *sizePtr, int *literalPtr); /* TIP #280 - Modified token based evaluation, with line information. */ MODULE_SCOPE int TclEvalEx(Tcl_Interp *interp, const char *script, Tcl_Size numBytes, int flags, Tcl_Size line, Tcl_Size *clNextOuter, const char *outerScript); MODULE_SCOPE Tcl_ObjCmdProc TclFileAttrsCmd; MODULE_SCOPE Tcl_ObjCmdProc TclFileCopyCmd; MODULE_SCOPE Tcl_ObjCmdProc TclFileDeleteCmd; | > | 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 | Tcl_Namespace *ensembleNamespacePtr, int flags); MODULE_SCOPE void TclDeleteNamespaceVars(Namespace *nsPtr); MODULE_SCOPE void TclDeleteNamespaceChildren(Namespace *nsPtr); MODULE_SCOPE int TclFindDictElement(Tcl_Interp *interp, const char *dict, Tcl_Size dictLength, const char **elementPtr, const char **nextPtr, Tcl_Size *sizePtr, int *literalPtr); MODULE_SCOPE Tcl_Obj * TclDictObjSmartRef(Tcl_Interp *interp, Tcl_Obj *); /* TIP #280 - Modified token based evaluation, with line information. */ MODULE_SCOPE int TclEvalEx(Tcl_Interp *interp, const char *script, Tcl_Size numBytes, int flags, Tcl_Size line, Tcl_Size *clNextOuter, const char *outerScript); MODULE_SCOPE Tcl_ObjCmdProc TclFileAttrsCmd; MODULE_SCOPE Tcl_ObjCmdProc TclFileCopyCmd; MODULE_SCOPE Tcl_ObjCmdProc TclFileDeleteCmd; |
︙ | ︙ |
Added generic/tclStrIdxTree.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | /* * tclStrIdxTree.c -- * * Contains the routines for managing string index tries in Tcl. * * This code is back-ported from the tclSE engine, by Serg G. Brester. * * Copyright (c) 2016 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. * * ----------------------------------------------------------------------- * * String index tries are prepaired structures used for fast greedy search of the string * (index) by unique string prefix as key. * * Index tree build for two lists together can be explained in the following datagram * * Lists: * * {Januar Februar Maerz April Mai Juni Juli August September Oktober November Dezember} * {Jnr Fbr Mrz Apr Mai Jni Jli Agt Spt Okt Nvb Dzb} * * Index-Tree: * * j 0 * ... * anuar 1 * * u 0 * a 0 * ni 6 * pril 4 * li 7 * ugust 8 * n 0 * gt 8 * r 1 * s 9 * i 6 * eptember 9 * li 7 * pt 9 * f 2 * oktober 10 * ebruar 2 * n 11 * br 2 * ovember 11 * m 0 * vb 11 * a 0 * d 12 * erz 3 * ezember 12 * i 5 * zb 12 * rz 3 * * ... * * Thereby value 0 shows pure group items (corresponding ambigous matches). * But the group may have a value if it contains only same values * (see for example group "f" above). * * StrIdxTree's are very fast, so: * build of above-mentioned tree takes about 10 microseconds. * search of string index in this tree takes fewer as 0.1 microseconds. * */ #include "tclInt.h" #include "tclStrIdxTree.h" /* *---------------------------------------------------------------------- * * TclStrIdxTreeSearch -- * * Find largest part of string "start" in indexed tree (case sensitive). * * Also used for building of string index tree. * * Results: * Return position of UTF character in start after last equal character * and found item (with parent). * * Side effects: * None. * *---------------------------------------------------------------------- */ const char* TclStrIdxTreeSearch( TclStrIdxTree **foundParent, /* Return value of found sub tree (used for tree build) */ TclStrIdx **foundItem, /* Return value of found item */ TclStrIdxTree *tree, /* Index tree will be browsed */ const char *start, /* UTF string to find in tree */ const char *end) /* End of string */ { TclStrIdxTree *parent = tree, *prevParent = tree; TclStrIdx *item = tree->firstPtr, *prevItem = NULL; const char *s = start, *f, *cin, *cinf, *prevf = NULL; Tcl_Size offs = 0; if (item == NULL) { goto done; } /* search in tree */ do { cinf = cin = TclGetString(item->key) + offs; f = TclUtfFindEqualNCInLwr(s, end, cin, cin + item->length - offs, &cinf); /* if something was found */ if (f > s) { /* if whole string was found */ if (f >= end) { start = f; goto done; }; /* set new offset and shift start string */ offs += cinf - cin; s = f; /* if match item, go deeper as long as possible */ if (offs >= item->length && item->childTree.firstPtr) { /* save previuosly found item (if not ambigous) for * possible fallback (few greedy match) */ if (item->value != NULL) { prevf = f; prevItem = item; prevParent = parent; } parent = &item->childTree; item = item->childTree.firstPtr; continue; } /* no children - return this item and current chars found */ start = f; goto done; } item = item->nextPtr; } while (item != NULL); /* fallback (few greedy match) not ambigous (has a value) */ if (prevItem != NULL) { item = prevItem; parent = prevParent; start = prevf; } done: if (foundParent) *foundParent = parent; if (foundItem) *foundItem = item; return start; } void TclStrIdxTreeFree( TclStrIdx *tree) { while (tree != NULL) { TclStrIdx *t; Tcl_DecrRefCount(tree->key); if (tree->childTree.firstPtr != NULL) { TclStrIdxTreeFree(tree->childTree.firstPtr); } t = tree, tree = tree->nextPtr; ckfree(t); } } /* * Several bidirectional list primitives */ static inline void TclStrIdxTreeInsertBranch( TclStrIdxTree *parent, TclStrIdx *item, TclStrIdx *child) { if (parent->firstPtr == child) parent->firstPtr = item; if (parent->lastPtr == child) parent->lastPtr = item; if ( (item->nextPtr = child->nextPtr) ) { item->nextPtr->prevPtr = item; child->nextPtr = NULL; } if ( (item->prevPtr = child->prevPtr) ) { item->prevPtr->nextPtr = item; child->prevPtr = NULL; } item->childTree.firstPtr = child; item->childTree.lastPtr = child; } static inline void TclStrIdxTreeAppend( TclStrIdxTree *parent, TclStrIdx *item) { if (parent->lastPtr != NULL) { parent->lastPtr->nextPtr = item; } item->prevPtr = parent->lastPtr; item->nextPtr = NULL; parent->lastPtr = item; if (parent->firstPtr == NULL) { parent->firstPtr = item; } } /* *---------------------------------------------------------------------- * * TclStrIdxTreeBuildFromList -- * * Build or extend string indexed tree from tcl list. * If the values not given the values of built list are indices starts with 1. * Value of 0 is thereby reserved to the ambigous values. * * Important: by multiple lists, optimal tree can be created only if list with * larger strings used firstly. * * Results: * Returns a standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ int TclStrIdxTreeBuildFromList( TclStrIdxTree *idxTree, Tcl_Size lstc, Tcl_Obj **lstv, void **values) { Tcl_Obj **lwrv; Tcl_Size i; int ret = TCL_ERROR; void *val; const char *s, *e, *f; TclStrIdx *item; /* create lowercase reflection of the list keys */ lwrv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*) * lstc); if (lwrv == NULL) { return TCL_ERROR; } for (i = 0; i < lstc; i++) { lwrv[i] = Tcl_DuplicateObj(lstv[i]); if (lwrv[i] == NULL) { return TCL_ERROR; } Tcl_IncrRefCount(lwrv[i]); lwrv[i]->length = Tcl_UtfToLower(TclGetString(lwrv[i])); } /* build index tree of the list keys */ for (i = 0; i < lstc; i++) { TclStrIdxTree *foundParent = idxTree; e = s = TclGetString(lwrv[i]); e += lwrv[i]->length; val = values ? values[i] : INT2PTR(i+1); /* ignore empty keys (impossible to index it) */ if (lwrv[i]->length == 0) continue; item = NULL; if (idxTree->firstPtr != NULL) { TclStrIdx *foundItem; f = TclStrIdxTreeSearch(&foundParent, &foundItem, idxTree, s, e); /* if common prefix was found */ if (f > s) { /* ignore element if fulfilled or ambigous */ if (f == e) { continue; } /* if shortest key was found with the same value, * just replace its current key with longest key */ if ( foundItem->value == val && foundItem->length <= lwrv[i]->length && foundItem->length <= (f - s) /* only if found item is covered in full */ && foundItem->childTree.firstPtr == NULL ) { TclSetObjRef(foundItem->key, lwrv[i]); foundItem->length = lwrv[i]->length; continue; } /* split tree (e. g. j->(jan,jun) + jul == j->(jan,ju->(jun,jul)) ) * but don't split by fulfilled child of found item ( ii->iii->iiii ) */ if (foundItem->length != (f - s)) { /* first split found item (insert one between parent and found + new one) */ item = (TclStrIdx *)ckalloc(sizeof(TclStrIdx)); if (item == NULL) { goto done; } TclInitObjRef(item->key, foundItem->key); item->length = f - s; /* set value or mark as ambigous if not the same value of both */ item->value = (foundItem->value == val) ? val : NULL; /* insert group item between foundParent and foundItem */ TclStrIdxTreeInsertBranch(foundParent, item, foundItem); foundParent = &item->childTree; } else { /* the new item should be added as child of found item */ foundParent = &foundItem->childTree; } } } /* append item at end of found parent */ item = (TclStrIdx *)ckalloc(sizeof(TclStrIdx)); if (item == NULL) { goto done; } item->childTree.lastPtr = item->childTree.firstPtr = NULL; TclInitObjRef(item->key, lwrv[i]); item->length = lwrv[i]->length; item->value = val; TclStrIdxTreeAppend(foundParent, item); }; ret = TCL_OK; done: if (lwrv != NULL) { for (i = 0; i < lstc; i++) { Tcl_DecrRefCount(lwrv[i]); } ckfree(lwrv); } if (ret != TCL_OK) { if (idxTree->firstPtr != NULL) { TclStrIdxTreeFree(idxTree->firstPtr); } } return ret; } static void StrIdxTreeObj_DupIntRepProc(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); static void StrIdxTreeObj_FreeIntRepProc(Tcl_Obj *objPtr); static void StrIdxTreeObj_UpdateStringProc(Tcl_Obj *objPtr); Tcl_ObjType StrIdxTreeObjType = { "str-idx-tree", /* name */ StrIdxTreeObj_FreeIntRepProc, /* freeIntRepProc */ StrIdxTreeObj_DupIntRepProc, /* dupIntRepProc */ StrIdxTreeObj_UpdateStringProc, /* updateStringProc */ NULL, /* setFromAnyProc */ TCL_OBJTYPE_V0 }; Tcl_Obj* TclStrIdxTreeNewObj() { Tcl_Obj *objPtr = Tcl_NewObj(); objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &StrIdxTreeObjType; /* return tree root in internal representation */ return objPtr; } static void StrIdxTreeObj_DupIntRepProc(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr) { /* follow links (smart pointers) */ if ( srcPtr->internalRep.twoPtrValue.ptr1 != NULL && srcPtr->internalRep.twoPtrValue.ptr2 == NULL ) { srcPtr = (Tcl_Obj*)srcPtr->internalRep.twoPtrValue.ptr1; } /* create smart pointer to it (ptr1 != NULL, ptr2 = NULL) */ TclInitObjRef(*((Tcl_Obj **)©Ptr->internalRep.twoPtrValue.ptr1), srcPtr); copyPtr->internalRep.twoPtrValue.ptr2 = NULL; copyPtr->typePtr = &StrIdxTreeObjType; } static void StrIdxTreeObj_FreeIntRepProc(Tcl_Obj *objPtr) { /* follow links (smart pointers) */ if ( objPtr->internalRep.twoPtrValue.ptr1 != NULL && objPtr->internalRep.twoPtrValue.ptr2 == NULL ) { /* is a link */ TclUnsetObjRef(*((Tcl_Obj **)&objPtr->internalRep.twoPtrValue.ptr1)); } else { /* is a tree */ TclStrIdxTree *tree = (TclStrIdxTree*)&objPtr->internalRep.twoPtrValue.ptr1; if (tree->firstPtr != NULL) { TclStrIdxTreeFree(tree->firstPtr); } objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->internalRep.twoPtrValue.ptr2 = NULL; } objPtr->typePtr = NULL; }; static void StrIdxTreeObj_UpdateStringProc(Tcl_Obj *objPtr) { /* currently only dummy empty string possible */ objPtr->length = 0; objPtr->bytes = &tclEmptyString; }; TclStrIdxTree * TclStrIdxTreeGetFromObj(Tcl_Obj *objPtr) { /* follow links (smart pointers) */ if (objPtr->typePtr != &StrIdxTreeObjType) { return NULL; } if ( objPtr->internalRep.twoPtrValue.ptr1 != NULL && objPtr->internalRep.twoPtrValue.ptr2 == NULL ) { objPtr = (Tcl_Obj*)objPtr->internalRep.twoPtrValue.ptr1; } /* return tree root in internal representation */ return (TclStrIdxTree*)&objPtr->internalRep.twoPtrValue.ptr1; } /* * Several debug primitives */ #if 0 /* currently unused, debug resp. test purposes only */ static void TclStrIdxTreePrint( Tcl_Interp *interp, TclStrIdx *tree, int offs) { Tcl_Obj *obj[2]; const char *s; TclInitObjRef(obj[0], Tcl_NewStringObj("::puts", -1)); while (tree != NULL) { s = TclGetString(tree->key) + offs; TclInitObjRef(obj[1], Tcl_ObjPrintf("%*s%.*s\t:%d", offs, "", tree->length - offs, s, tree->value)); Tcl_PutsObjCmd(NULL, interp, 2, obj); TclUnsetObjRef(obj[1]); if (tree->childTree.firstPtr != NULL) { TclStrIdxTreePrint(interp, tree->childTree.firstPtr, tree->length); } tree = tree->nextPtr; } TclUnsetObjRef(obj[0]); } int TclStrIdxTreeTestObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { const char *cs, *cin, *ret; static const char *const options[] = { "index", "puts-index", "findequal", NULL }; enum optionInd { O_INDEX, O_PUTS_INDEX, O_FINDEQUAL }; int optionIndex; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, &optionIndex) != TCL_OK) { Tcl_SetErrorCode(interp, "CLOCK", "badOption", Tcl_GetString(objv[1]), (char *)NULL); return TCL_ERROR; } switch (optionIndex) { case O_FINDEQUAL: if (objc < 4) { Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } cs = TclGetString(objv[2]); cin = TclGetString(objv[3]); ret = TclUtfFindEqual( cs, cs + objv[1]->length, cin, cin + objv[2]->length); Tcl_SetObjResult(interp, Tcl_NewIntObj(ret - cs)); break; case O_INDEX: case O_PUTS_INDEX: { Tcl_Obj **lstv; int i, lstc; TclStrIdxTree idxTree = {NULL, NULL}; i = 1; while (++i < objc) { if (TclListObjGetElements(interp, objv[i], &lstc, &lstv) != TCL_OK) { return TCL_ERROR; }; TclStrIdxTreeBuildFromList(&idxTree, lstc, lstv, NULL); } if (optionIndex == O_PUTS_INDEX) { TclStrIdxTreePrint(interp, idxTree.firstPtr, 0); } TclStrIdxTreeFree(idxTree.firstPtr); } break; } return TCL_OK; } #endif /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ |
Added generic/tclStrIdxTree.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | /* * tclStrIdxTree.h -- * * Declarations of string index tries and other primitives currently * back-ported from tclSE. * * Copyright (c) 2016 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 _TCLSTRIDXTREE_H #define _TCLSTRIDXTREE_H /* * Main structures declarations of index tree and entry */ typedef struct TclStrIdxTree { struct TclStrIdx *firstPtr; struct TclStrIdx *lastPtr; } TclStrIdxTree; typedef struct TclStrIdx { struct TclStrIdxTree childTree; struct TclStrIdx *nextPtr; struct TclStrIdx *prevPtr; Tcl_Obj *key; Tcl_Size length; void *value; } TclStrIdx; /* *---------------------------------------------------------------------- * * TclUtfFindEqual, TclUtfFindEqualNC -- * * Find largest part of string cs in string cin (case sensitive and not). * * Results: * Return position of UTF character in cs after last equal character. * * Side effects: * None. * *---------------------------------------------------------------------- */ static inline const char * TclUtfFindEqual( const char *cs, /* UTF string to find in cin. */ const char *cse, /* End of cs */ const char *cin, /* UTF string will be browsed. */ const char *cine) /* End of cin */ { const char *ret = cs; Tcl_UniChar ch1, ch2; do { cs += TclUtfToUniChar(cs, &ch1); cin += TclUtfToUniChar(cin, &ch2); if (ch1 != ch2) break; } while ((ret = cs) < cse && cin < cine); return ret; } static inline const char * TclUtfFindEqualNC( const char *cs, /* UTF string to find in cin. */ const char *cse, /* End of cs */ const char *cin, /* UTF string will be browsed. */ const char *cine, /* End of cin */ const char **cinfnd) /* Return position in cin */ { const char *ret = cs; Tcl_UniChar ch1, ch2; do { cs += TclUtfToUniChar(cs, &ch1); cin += TclUtfToUniChar(cin, &ch2); if (ch1 != ch2) { ch1 = Tcl_UniCharToLower(ch1); ch2 = Tcl_UniCharToLower(ch2); if (ch1 != ch2) break; } *cinfnd = cin; } while ((ret = cs) < cse && cin < cine); return ret; } static inline const char * TclUtfFindEqualNCInLwr( const char *cs, /* UTF string (in anycase) to find in cin. */ const char *cse, /* End of cs */ const char *cin, /* UTF string (in lowercase) will be browsed. */ const char *cine, /* End of cin */ const char **cinfnd) /* Return position in cin */ { const char *ret = cs; Tcl_UniChar ch1, ch2; do { cs += TclUtfToUniChar(cs, &ch1); cin += TclUtfToUniChar(cin, &ch2); if (ch1 != ch2) { ch1 = Tcl_UniCharToLower(ch1); if (ch1 != ch2) break; } *cinfnd = cin; } while ((ret = cs) < cse && cin < cine); return ret; } /* * Primitives to safe set, reset and free references. */ #define TclUnsetObjRef(obj) \ do { \ if (obj != NULL) { \ Tcl_DecrRefCount(obj); \ obj = NULL; \ } \ } while (0) #define TclInitObjRef(obj, val) \ do { \ obj = val; \ if (obj) { \ Tcl_IncrRefCount(obj); \ } \ } while (0) #define TclSetObjRef(obj, val) \ do { \ Tcl_Obj *nval = val; \ if (obj != nval) { \ Tcl_Obj *prev = obj; \ TclInitObjRef(obj, nval); \ if (prev != NULL) { \ Tcl_DecrRefCount(prev); \ }; \ } \ } while (0) /* * Prototypes of module functions. */ MODULE_SCOPE const char* TclStrIdxTreeSearch(TclStrIdxTree **foundParent, TclStrIdx **foundItem, TclStrIdxTree *tree, const char *start, const char *end); MODULE_SCOPE int TclStrIdxTreeBuildFromList(TclStrIdxTree *idxTree, Tcl_Size lstc, Tcl_Obj **lstv, void **values); MODULE_SCOPE Tcl_Obj* TclStrIdxTreeNewObj(); MODULE_SCOPE TclStrIdxTree* TclStrIdxTreeGetFromObj(Tcl_Obj *objPtr); #if 0 /* currently unused, debug resp. test purposes only */ MODULE_SCOPE Tcl_ObjCmdProc TclStrIdxTreeTestObjCmd; #endif #endif /* _TCLSTRIDXTREE_H */ |
Changes to library/clock.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #---------------------------------------------------------------------- # # clock.tcl -- # # This file implements the portions of the [clock] ensemble that are # coded in Tcl. Refer to the users' manual to see the description of # the [clock] command and its subcommands. # # #---------------------------------------------------------------------- # # Copyright © 2004-2007 Kevin B. Kenny # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # #---------------------------------------------------------------------- | > | < < | < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #---------------------------------------------------------------------- # # clock.tcl -- # # This file implements the portions of the [clock] ensemble that are # coded in Tcl. Refer to the users' manual to see the description of # the [clock] command and its subcommands. # # #---------------------------------------------------------------------- # # Copyright © 2004-2007 Kevin B. Kenny # Copyright © 2015 Sergey 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. # #---------------------------------------------------------------------- # msgcat 1.7 features are used. package require msgcat 1.7 # Put the library directory into the namespace for the ensemble so that the # library code can find message catalogs and time zone definition files. namespace eval ::tcl::clock \ [list variable LibDir [info library]] |
︙ | ︙ | |||
54 55 56 57 58 59 60 | namespace export milliseconds namespace export scan namespace export seconds namespace export add # Import the message catalog commands that we use. | < < | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | namespace export milliseconds namespace export scan namespace export seconds namespace export add # Import the message catalog commands that we use. namespace import ::msgcat::mclocale namespace import ::msgcat::mcpackagelocale } #---------------------------------------------------------------------- # # ::tcl::clock::Initialize -- |
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 | variable MINWIDE -9223372036854775808 variable MAXWIDE 9223372036854775807 # Day before Leap Day variable FEB_28 58 # Translation table to map Windows TZI onto cities, so that the Olson # rules can apply. In some cases the mapping is ambiguous, so it's wise # to specify $::env(TCL_TZ) rather than simply depending on the system # time zone. # The keys are long lists of values obtained from the time zone # information in the Registry. In order, the list elements are: | > > > > > > > | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | variable MINWIDE -9223372036854775808 variable MAXWIDE 9223372036854775807 # Day before Leap Day variable FEB_28 58 # Default configuration ::tcl::unsupported::clock::configure -current-locale [mclocale] #::tcl::unsupported::clock::configure -default-locale C #::tcl::unsupported::clock::configure -year-century 2000 \ # -century-switch 38 # Translation table to map Windows TZI onto cities, so that the Olson # rules can apply. In some cases the mapping is ambiguous, so it's wise # to specify $::env(TCL_TZ) rather than simply depending on the system # time zone. # The keys are long lists of values obtained from the time zone # information in the Registry. In order, the list elements are: |
︙ | ︙ | |||
379 380 381 382 383 384 385 | {36000 0 3600 0 3 0 5 3 0 0 0 0 10 0 5 2 0 0 0} :Australia/Sydney {39600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Noumea {43200 0 3600 0 3 0 3 3 0 0 0 0 10 0 1 2 0 0 0} :Pacific/Auckland {43200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Fiji {46800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Tongatapu }] | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | {36000 0 3600 0 3 0 5 3 0 0 0 0 10 0 5 2 0 0 0} :Australia/Sydney {39600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Noumea {43200 0 3600 0 3 0 3 3 0 0 0 0 10 0 1 2 0 0 0} :Pacific/Auckland {43200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Fiji {46800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Tongatapu }] # Legacy time zones, used primarily for parsing RFC822 dates. variable LegacyTimeZone [dict create \ gmt +0000 \ ut +0000 \ utc +0000 \ bst +0100 \ |
︙ | ︙ | |||
621 622 623 624 625 626 627 | x -1100 \ y -1200 \ z +0000 \ ] # Caches | | | < < < < < < < < | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | < < < < < < < < < < < < < > | | > > | < < < < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | < | < < < < < > > | < | > | < | | < | < < | < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < < < < < < < < < | < < < < | < | < < | < < < < < < < < < < < < < < | < | < < < < < < < < < < < < < < < < < | < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | | | < < < < < < < < < < < < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < < | | < < | < < < < | < < < < < < < < < < < < < < < < < | < < < | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | < < < < < < < < < < < < | < < | < < < < < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | | < < < < < < | < | < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | < < < < < < < < | | < < < < < < < < < < < < < < < < < < < | < < | < < < < < | < < < > | < < < < < < < < < < | < < < < | < < < < < < < < | < < | < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < < < < < < < < < < < < < | | | < < < < < < < < < < | | < < < | < < < < | < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < | < < < < < < < < < < < < < < < < < < < < | < < < | < | < < < < | < < < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < | < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < | | < | < < < < < | > | > | | > > > > > | | > > | > | > > | > > > | > > | < < > | < | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 | x -1100 \ y -1200 \ z +0000 \ ] # Caches variable LocFmtMap [dict create]; # Dictionary with localized format maps variable TimeZoneBad [dict create]; # Dictionary whose keys are time zone # names and whose values are 1 if # the time zone is unknown and 0 # if it is known. variable TZData; # Array whose keys are time zone names # and whose values are lists of quads # comprising start time, UTC offset, # Daylight Saving Time indicator, and # time zone abbreviation. variable mcLocales [dict create]; # Dictionary with loaded locales variable mcMergedCat [dict create]; # Dictionary with merged locale catalogs } ::tcl::clock::Initialize #---------------------------------------------------------------------- # mcget -- # # Return the merged translation catalog for the ::tcl::clock namespace # Searching of catalog is similar to "msgcat::mc". # # Contrary to "msgcat::mc" may additionally load a package catalog # on demand. # # Arguments: # loc The locale used for translation. # # Results: # Returns the dictionary object as whole catalog of the package/locale. # proc ::tcl::clock::mcget {loc} { variable mcMergedCat switch -- $loc system { set loc [GetSystemLocale] } current { set loc [mclocale] } if {$loc ne {}} { set loc [string tolower $loc] } # try to retrieve now if already available: if {[dict exists $mcMergedCat $loc]} { return [dict get $mcMergedCat $loc] } # get locales list for given locale (de_de -> {de_de de {}}) variable mcLocales if {[dict exists $mcLocales $loc]} { set loclist [dict get $mcLocales $loc] } else { # save current locale: set prevloc [mclocale] # lazy load catalog on demand (set it will load the catalog) mcpackagelocale set $loc set loclist [msgcat::mcutil::getpreferences $loc] dict set $mcLocales $loc $loclist # restore: if {$prevloc ne $loc} { mcpackagelocale set $prevloc } } # get whole catalog: mcMerge $loclist } # mcMerge -- # # Merge message catalog dictionaries to one dictionary. # # Arguments: # locales List of locales to merge. # # Results: # Returns the (weak pointer) to merged dictionary of message catalog. # proc ::tcl::clock::mcMerge {locales} { variable mcMergedCat if {[dict exists $mcMergedCat [set loc [lindex $locales 0]]]} { return [dict get $mcMergedCat $loc] } # package msgcat currently does not provide possibility to get whole catalog: upvar ::msgcat::Msgs Msgs set ns ::tcl::clock # Merge sequential locales (in reverse order, e. g. {} -> en -> en_en): if {[llength $locales] > 1} { set mrgcat [mcMerge [lrange $locales 1 end]] if {[dict exists $Msgs $ns $loc]} { set mrgcat [dict merge $mrgcat [dict get $Msgs $ns $loc]] dict set mrgcat L $loc } else { # be sure a duplicate is created, don't overwrite {} (common) locale: set mrgcat [dict merge $mrgcat [dict create L $loc]] } } else { if {[dict exists $Msgs $ns $loc]} { set mrgcat [dict get $Msgs $ns $loc] dict set mrgcat L $loc } else { # be sure a duplicate is created, don't overwrite {} (common) locale: set mrgcat [dict create L $loc] } } dict set mcMergedCat $loc $mrgcat # return smart reference (shared dict as object with exact one ref-counter) return $mrgcat } #---------------------------------------------------------------------- # # GetSystemLocale -- # # Determines the system locale, which corresponds to "system" # keyword for locale parameter of 'clock' command. # # Parameters: # None. # # Results: # Returns the system locale. # # Side effects: # None. # #---------------------------------------------------------------------- proc ::tcl::clock::GetSystemLocale {} { if { $::tcl_platform(platform) ne {windows} } { # On a non-windows platform, the 'system' locale is the same as # the 'current' locale return [mclocale] } # On a windows platform, the 'system' locale is adapted from the # 'current' locale by applying the date and time formats from the # Control Panel. First, load the 'current' locale if it's not yet # loaded mcpackagelocale set [mclocale] # Make a new locale string for the system locale, and get the # Control Panel information set locale [mclocale]_windows if { ! [mcpackagelocale present $locale] } { LoadWindowsDateTimeFormats $locale } return $locale } #---------------------------------------------------------------------- # # EnterLocale -- # # Switch [mclocale] to a given locale if necessary # # Parameters: # locale -- Desired locale # # Results: # Returns the locale that was previously current. # # Side effects: # Does [mclocale]. If necessary, loades the designated locale's files. # #---------------------------------------------------------------------- proc ::tcl::clock::EnterLocale { locale } { switch -- $locale system { set locale [GetSystemLocale] } current { set locale [mclocale] } # Select the locale, eventually load it mcpackagelocale set $locale return $locale } #---------------------------------------------------------------------- # # _hasRegistry -- # # Helper that checks whether registry module is available (Windows only) # and loads it on demand. # #---------------------------------------------------------------------- proc ::tcl::clock::_hasRegistry {} { set res 0 if { $::tcl_platform(platform) eq {windows} } { if { [catch { package require registry 1.1 }] } { # try to load registry directly from root (if uninstalled / development env): if {[regexp {[/\\]library$} [info library]]} {catch { load [lindex \ [glob -tails -directory [file dirname [info nameofexecutable]] \ tclreg*[expr {[::tcl::pkgconfig get debug] ? {g} : {}}].dll] 0 \ ] registry }} } if { [namespace which -command ::registry] ne "" } { set res 1 } } proc ::tcl::clock::_hasRegistry {} [list return $res] return $res } #---------------------------------------------------------------------- # # LoadWindowsDateTimeFormats -- # # Load the date/time formats from the Control Panel in Windows and |
︙ | ︙ | |||
2359 2360 2361 2362 2363 2364 2365 | # default strings can be obtained if the Registry query fails. # #---------------------------------------------------------------------- proc ::tcl::clock::LoadWindowsDateTimeFormats { locale } { # Bail out if we can't find the Registry | < | | 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 | # default strings can be obtained if the Registry query fails. # #---------------------------------------------------------------------- proc ::tcl::clock::LoadWindowsDateTimeFormats { locale } { # Bail out if we can't find the Registry if { ![_hasRegistry] } return if { ![catch { registry get "HKEY_CURRENT_USER\\Control Panel\\International" \ sShortDate } string] } { set quote {} set datefmt {} |
︙ | ︙ | |||
2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 | # # Map away locale-dependent format groups in a clock format. # # Parameters: # locale -- Current [mclocale] locale, supplied to avoid # an extra call # format -- Format supplied to [clock scan] or [clock format] # # Results: # Returns the string with locale-dependent composite format groups # substituted out. # # Side effects: # None. # #---------------------------------------------------------------------- | > > | > | < | | < | | | | | | | | | | < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < | < < < < < < | < | < | < < | < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < < < < < < < < < < < < < < < < < < | < < | < | < < < < < < < < < < < < < < < < < < < | < < < < < < < < | < < | < | < | < | < < < < < | < < < < < < < < < < < < < < < < < < < < | < < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | | < | | | | > | < | > | | < < | | < < < < < < < < < < < < < < < < < < < < | < < < < < < < | | > | > > | | | > | | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 | # # Map away locale-dependent format groups in a clock format. # # Parameters: # locale -- Current [mclocale] locale, supplied to avoid # an extra call # format -- Format supplied to [clock scan] or [clock format] # mcd -- Message catalog dictionary for current locale (read-only, # don't store it to avoid shared references). # # Results: # Returns the string with locale-dependent composite format groups # substituted out. # # Side effects: # None. # #---------------------------------------------------------------------- proc ::tcl::clock::LocalizeFormat { locale format mcd } { variable LocFmtMap # get map list cached or build it: if {[dict exists $LocFmtMap $locale]} { set mlst [dict get $LocFmtMap $locale] } else { # Handle locale-dependent format groups by mapping them out of the format # string. Note that the order of the [string map] operations is # significant because later formats can refer to later ones; for example # %c can refer to %X, which in turn can refer to %T. set mlst { %% %% %D %m/%d/%Y %+ {%a %b %e %H:%M:%S %Z %Y} } lappend mlst %EY [string map $mlst [dict get $mcd LOCALE_YEAR_FORMAT]] lappend mlst %T [string map $mlst [dict get $mcd TIME_FORMAT_24_SECS]] lappend mlst %R [string map $mlst [dict get $mcd TIME_FORMAT_24]] lappend mlst %r [string map $mlst [dict get $mcd TIME_FORMAT_12]] lappend mlst %X [string map $mlst [dict get $mcd TIME_FORMAT]] lappend mlst %EX [string map $mlst [dict get $mcd LOCALE_TIME_FORMAT]] lappend mlst %x [string map $mlst [dict get $mcd DATE_FORMAT]] lappend mlst %Ex [string map $mlst [dict get $mcd LOCALE_DATE_FORMAT]] lappend mlst %c [string map $mlst [dict get $mcd DATE_TIME_FORMAT]] lappend mlst %Ec [string map $mlst [dict get $mcd LOCALE_DATE_TIME_FORMAT]] dict set LocFmtMap $locale $mlst } # translate copy of format (don't use format object here, because otherwise # it can lose its internal representation (string map - convert to unicode) set locfmt [string map $mlst [string range " $format" 1 end]] # Save original format as long as possible, because of internal # representation (performance). # Note that in this case such format will be never localized (also # using another locales). To prevent this return a duplicate (but # it may be slower). if {$locfmt eq $format} { set locfmt $format } return $locfmt } #---------------------------------------------------------------------- # # GetSystemTimeZone -- # # Determines the system time zone, which is the default for the # 'clock' command if no other zone is supplied. # # Parameters: # None. # # Results: # Returns the system time zone. # # Side effects: # Stores the system time zone in engine configuration, since # determining it may be an expensive process. # #---------------------------------------------------------------------- proc ::tcl::clock::GetSystemTimeZone {} { variable TimeZoneBad if {[set result [getenv TCL_TZ]] ne {}} { set timezone $result } elseif {[set result [getenv TZ]] ne {}} { set timezone $result } else { # ask engine for the cached timezone: set timezone [::tcl::unsupported::clock::configure -system-tz] if { $timezone ne "" } { return $timezone } if { $::tcl_platform(platform) eq {windows} } { set timezone [GuessWindowsTimeZone] } elseif { [file exists /etc/localtime] && ![catch {ReadZoneinfoFile \ Tcl/Localtime /etc/localtime}] } { set timezone :Tcl/Localtime } else { set timezone :localtime } } if { ![dict exists $TimeZoneBad $timezone] } { catch {set timezone [SetupTimeZone $timezone]} } if { [dict exists $TimeZoneBad $timezone] } { set timezone :localtime } # tell backend - current system timezone: ::tcl::unsupported::clock::configure -system-tz $timezone return $timezone } #---------------------------------------------------------------------- # # SetupTimeZone -- # # Given the name or specification of a time zone, sets up its in-memory # data. # # Parameters: # tzname - Name of a time zone # # Results: # Unless the time zone is ':localtime', sets the TZData array to contain # the lookup table for local<->UTC conversion. Returns an error if the # time zone cannot be parsed. # #---------------------------------------------------------------------- proc ::tcl::clock::SetupTimeZone { timezone {alias {}} } { variable TZData if {! [info exists TZData($timezone)] } { variable TimeZoneBad if { [dict exists $TimeZoneBad $timezone] } { return -code error \ -errorcode [list CLOCK badTimeZone $timezone] \ "time zone \"$timezone\" not found" } variable MINWIDE if { [regexp {^([-+])(\d\d)(?::?(\d\d)(?::?(\d\d))?)?} $timezone \ -> s hh mm ss] } then { # Make a fixed offset ::scan $hh %d hh if { $mm eq {} } { |
︙ | ︙ | |||
3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 | if { [catch { LoadTimeZoneFile [string range $timezone 1 end] }] && [catch { LoadZoneinfoFile [string range $timezone 1 end] }] } then { return -code error \ -errorcode [list CLOCK badTimeZone $timezone] \ "time zone \"$timezone\" not found" } } elseif { ![catch {ParsePosixTimeZone $timezone} tzfields] } { # This looks like a POSIX time zone - try to process it if { [catch {ProcessPosixTimeZone $tzfields} data opts] } { if { [lindex [dict get $opts -errorcode] 0] eq {CLOCK} } { dict unset opts -errorinfo } return -options $opts $data } else { set TZData($timezone) $data } } else { # We couldn't parse this as a POSIX time zone. Try again with a # time zone file - this time without a colon if { [catch { LoadTimeZoneFile $timezone }] && [catch { LoadZoneinfoFile $timezone } - opts] } { dict unset opts -errorinfo return -options $opts "time zone $timezone not found" } set TZData($timezone) $TZData(:$timezone) } } | > > > > > > > > > > > > > > > > > > | > | 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | if { [catch { LoadTimeZoneFile [string range $timezone 1 end] }] && [catch { LoadZoneinfoFile [string range $timezone 1 end] }] } then { dict set TimeZoneBad $timezone 1 return -code error \ -errorcode [list CLOCK badTimeZone $timezone] \ "time zone \"$timezone\" not found" } } elseif { ![catch {ParsePosixTimeZone $timezone} tzfields] } { # This looks like a POSIX time zone - try to process it if { [catch {ProcessPosixTimeZone $tzfields} data opts] } { if { [lindex [dict get $opts -errorcode] 0] eq {CLOCK} } { dict unset opts -errorinfo } dict set TimeZoneBad $timezone 1 return -options $opts $data } else { set TZData($timezone) $data } } else { variable LegacyTimeZone # We couldn't parse this as a POSIX time zone. Try again with a # time zone file - this time without a colon if { [catch { LoadTimeZoneFile $timezone }] && [catch { LoadZoneinfoFile $timezone } - opts] } { # Check may be a legacy zone: if { $alias eq {} && ![catch { set tzname [dict get $LegacyTimeZone [string tolower $timezone]] }] } { set tzname [::tcl::clock::SetupTimeZone $tzname $timezone] set TZData($timezone) $TZData($tzname) # tell backend - timezone is initialized and return shared timezone object: return [::tcl::unsupported::clock::configure -setup-tz $timezone] } dict unset opts -errorinfo dict set TimeZoneBad $timezone 1 return -options $opts "time zone $timezone not found" } set TZData($timezone) $TZData(:$timezone) } } # tell backend - timezone is initialized and return shared timezone object: ::tcl::unsupported::clock::configure -setup-tz $timezone } #---------------------------------------------------------------------- # # GuessWindowsTimeZone -- # # Determines the system time zone on windows. |
︙ | ︙ | |||
3164 3165 3166 3167 3168 3169 3170 | # zone that uses the same rules. If it finds one, it returns it; otherwise, # it constructs a Posix-style time zone string and returns that. # #---------------------------------------------------------------------- proc ::tcl::clock::GuessWindowsTimeZone {} { variable WinZoneInfo | < | | 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 | # zone that uses the same rules. If it finds one, it returns it; otherwise, # it constructs a Posix-style time zone string and returns that. # #---------------------------------------------------------------------- proc ::tcl::clock::GuessWindowsTimeZone {} { variable WinZoneInfo variable TimeZoneBad if { ![_hasRegistry] } { return :localtime } # Dredge time zone information out of the registry if { [catch { set rpath HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\TimeZoneInformation |
︙ | ︙ | |||
3205 3206 3207 3208 3209 3210 3211 | # Make up a Posix time zone specifier if we can't find one. Check here # that the tzdata file exists, in case we're running in an environment # (e.g. starpack) where tzdata is incomplete. (Bug 1237907) if { [dict exists $WinZoneInfo $data] } { set tzname [dict get $WinZoneInfo $data] if { ! [dict exists $TimeZoneBad $tzname] } { | | | | 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 | # Make up a Posix time zone specifier if we can't find one. Check here # that the tzdata file exists, in case we're running in an environment # (e.g. starpack) where tzdata is incomplete. (Bug 1237907) if { [dict exists $WinZoneInfo $data] } { set tzname [dict get $WinZoneInfo $data] if { ! [dict exists $TimeZoneBad $tzname] } { catch {set tzname [SetupTimeZone $tzname]} } } else { set tzname {} } if { $tzname eq {} || [dict exists $TimeZoneBad $tzname] } { lassign $data \ bias stdBias dstBias \ stdYear stdMonth stdDayOfWeek stdDayOfMonth \ stdHour stdMinute stdSecond stdMillisec \ dstYear dstMonth dstDayOfWeek dstDayOfMonth \ dstHour dstMinute dstSecond dstMillisec set stdDelta [expr { $bias + $stdBias }] |
︙ | ︙ | |||
3881 3882 3883 3884 3885 3886 3887 | proc ::tcl::clock::DeterminePosixDSTTime { z bound y } { variable FEB_28 # Determine the start or end day of DST | | | | 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 | proc ::tcl::clock::DeterminePosixDSTTime { z bound y } { variable FEB_28 # Determine the start or end day of DST set date [dict create era CE year $y gregorian 1] set doy [dict get $z ${bound}DayOfYear] if { $doy ne {} } { # Time was specified as a day of the year if { [dict get $z ${bound}J] ne {} && [IsGregorianLeapYear $date] && ( $doy > $FEB_28 ) } { incr doy } dict set date dayOfYear $doy set date [GetJulianDayFromEraYearDay $date[set date {}] 2361222] } else { # Time was specified as a day of the week within a month |
︙ | ︙ | |||
3935 3936 3937 3938 3939 3940 3941 | } else { set s [lindex [::scan $s %d] 0] } set tod [expr { ( $h * 60 + $m ) * 60 + $s }] return [expr { $seconds + $tod }] } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 | } else { set s [lindex [::scan $s %d] 0] } set tod [expr { ( $h * 60 + $m ) * 60 + $s }] return [expr { $seconds + $tod }] } #---------------------------------------------------------------------- # # GetJulianDayFromEraYearDay -- # # Given a year, month and day on the Gregorian calendar, determines # the Julian Day Number beginning at noon on that date. # |
︙ | ︙ | |||
4151 4152 4153 4154 4155 4156 4157 | proc ::tcl::clock::WeekdayOnOrBefore { weekday j } { set k [expr { ( $weekday + 6 ) % 7 }] return [expr { $j - ( $j - $k ) % 7 }] } #---------------------------------------------------------------------- # | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < | | > | < | < > | | < < | < | 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 | proc ::tcl::clock::WeekdayOnOrBefore { weekday j } { set k [expr { ( $weekday + 6 ) % 7 }] return [expr { $j - ( $j - $k ) % 7 }] } #---------------------------------------------------------------------- # # ChangeCurrentLocale -- # # The global locale was changed within msgcat. # Clears the buffered parse functions of the current locale. # # Parameters: # loclist (ignored) # # Results: # None. # # Side effects: # Buffered parse functions are cleared. # #---------------------------------------------------------------------- proc ::tcl::clock::ChangeCurrentLocale {args} { ::tcl::unsupported::clock::configure -current-locale [lindex $args 0] } #---------------------------------------------------------------------- # # ClearCaches -- # # Clears all caches to reclaim the memory used in [clock] # # Parameters: # None. # # Results: # None. # # Side effects: # Caches are cleared. # #---------------------------------------------------------------------- proc ::tcl::clock::ClearCaches {} { variable LocFmtMap variable mcMergedCat variable TimeZoneBad # tell backend - should invalidate: ::tcl::unsupported::clock::configure -clear # clear msgcat cache: set mcMergedCat [dict create] set LocFmtMap {} set TimeZoneBad {} InitTZData } |
Changes to library/init.tcl.
︙ | ︙ | |||
97 98 99 100 101 102 103 | {::tcl::MacOSXPkgUnknown ::tclPkgUnknown}} } else { package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} } # Set up the 'clock' ensemble | < | < < | | | < < < | < | > > | > < | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | {::tcl::MacOSXPkgUnknown ::tclPkgUnknown}} } else { package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} } # Set up the 'clock' ensemble proc clock args { set cmdmap [dict create] foreach cmd {add clicks format microseconds milliseconds scan seconds} { dict set cmdmap $cmd ::tcl::clock::$cmd } namespace inscope ::tcl::clock [list namespace ensemble create -command \ [uplevel 1 [list ::namespace origin [::lindex [info level 0] 0]]] \ -map $cmdmap] ::tcl::unsupported::clock::configure -init-complete uplevel 1 [info level 0] } } # Conditionalize for presence of exec. if {[namespace which -command exec] eq ""} { # Some machines do not have exec. Also, on all |
︙ | ︙ |
Changes to library/tclIndex.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | set auto_index(::auto_mkindex_parser::mkindex) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::hook) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::childhook) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::command) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::commandInit) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::fullname) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::indexEntry) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(foreachLine) [list ::tcl::Pkg::source [file join $dir foreachline.tcl]] set auto_index(::tcl::history) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(history) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistAdd) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistKeep) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistClear) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistInfo) [list ::tcl::Pkg::source [file join $dir history.tcl]] | > > > > > > > > > > > > > > > > > > > > > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | set auto_index(::auto_mkindex_parser::mkindex) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::hook) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::childhook) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::command) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::commandInit) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::fullname) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::indexEntry) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::tcl::clock::Initialize) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::mcget) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::mcMerge) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::GetSystemLocale) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::EnterLocale) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::_hasRegistry) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::LoadWindowsDateTimeFormats) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::LocalizeFormat) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::GetSystemTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::SetupTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::GuessWindowsTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::LoadTimeZoneFile) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::LoadZoneinfoFile) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::ReadZoneinfoFile) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::ParsePosixTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::ProcessPosixTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::DeterminePosixDSTTime) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::GetJulianDayFromEraYearDay) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::GetJulianDayFromEraYearMonthWeekDay) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::IsGregorianLeapYear) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::WeekdayOnOrBefore) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::ChangeCurrentLocale) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(::tcl::clock::ClearCaches) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(foreachLine) [list ::tcl::Pkg::source [file join $dir foreachline.tcl]] set auto_index(::tcl::history) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(history) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistAdd) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistKeep) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistClear) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistInfo) [list ::tcl::Pkg::source [file join $dir history.tcl]] |
︙ | ︙ |
Changes to tests-perf/clock.perf.tcl.
︙ | ︙ | |||
351 352 353 354 355 356 357 358 359 | proc test-other {{reptime 1000}} { _test_run $reptime { # Bad zone {catch {clock scan "1 day" -timezone BAD_ZONE -locale en}} # Scan : julian day (overflow) {catch {clock scan 5373485 -format %J}} # Scan : test rotate of GC objects (format is dynamic, so tcl-obj removed with last reference) | > > > | > | > > | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | proc test-other {{reptime 1000}} { _test_run $reptime { # Bad zone {catch {clock scan "1 day" -timezone BAD_ZONE -locale en}} # Scan : julian day (overflow) {catch {clock scan 5373485 -format %J}} setup {set _(org-reptime) $_(reptime); lset _(reptime) 1 50} # Scan : test rotate of GC objects (format is dynamic, so tcl-obj removed with last reference) setup {set i -1} {clock scan "[incr i] - 25.11.2015" -format "$i - %d.%m.%Y" -base 0 -gmt 1} # Scan : test reusability of GC objects (format is dynamic, so tcl-obj removed with last reference) setup {incr i; set j $i} {clock scan "[incr j -1] - 25.11.2015" -format "$j - %d.%m.%Y" -base 0 -gmt 1} setup {set _(reptime) $_(org-reptime); set j $i} {clock scan "[incr j -1] - 25.11.2015" -format "$j - %d.%m.%Y" -base 0 -gmt 1; if {!$j} {set j $i}} } } proc test-ensemble-perf {{reptime 1000}} { _test_run $reptime { # Clock clicks (ensemble) {clock clicks} |
︙ | ︙ |
Added tests/clock-ivm.test.
> > > > > > > > | 1 2 3 4 5 6 7 8 | # clock-ivm.test -- # # This test file covers the 'clock' command using inverted validity mode. # # See the file "clock.test" for more information. ::tcl::unsupported::clock::configure -valid [expr {![::tcl::unsupported::clock::configure -valid]}] source [file join [file dirname [info script]] clock.test] |
Changes to tests/clock.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # clock.test -- # # This test file covers the 'clock' command that manipulates time. # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright © 2004 Kevin B. Kenny. All rights reserved. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. if {"::tcltest" ni [namespace children]} { package require tcltest 2.5 namespace import -force ::tcltest::* } if {[testConstraint win]} { if {[catch { ::tcltest::loadTestedCommands | > < | > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | # clock.test -- # # This test file covers the 'clock' command that manipulates time. # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright © 2004 Kevin B. Kenny. All rights reserved. # Copyright © 2015 Sergey 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. if {"::tcltest" ni [namespace children]} { package require tcltest 2.5 namespace import -force ::tcltest::* } if {[testConstraint win]} { if {[catch { ::tcltest::loadTestedCommands }]} { # nothing to be done (registry loaded on demand) } } package require msgcat 1.4 testConstraint detroit \ [expr {![catch {clock format 0 -timezone :America/Detroit -format %z}]}] testConstraint y2038 \ [expr {[clock format 2158894800 -format %z -timezone :America/Detroit] eq {-0400}}] # Test with both validity modes - validate on / off: set valid_mode [::tcl::unsupported::clock::configure -valid] # Wrapper to show validity mode in the test-case name (for possible errors): proc test {args} { variable valid_mode lset args 0 [lindex $args 0].vm:$valid_mode tailcall ::tcltest::test {*}$args } puts [outputChannel] " Validity default mode: [expr {$valid_mode ? "on": "off"}]" testConstraint valid_off [expr {![::tcl::unsupported::clock::configure -valid]}] if {[namespace which -command ::tcl::unsupported::timerate] ne ""} { namespace import ::tcl::unsupported::timerate } # TEST PLAN # clock-0: # several base test-cases # # clock-1: # [clock format] - tests of bad and empty arguments # # clock-2 # formatting of year, month and day of month # # clock-3 |
︙ | ︙ | |||
246 247 248 249 250 251 252 253 254 255 256 | } if { ![dict exists $reg $path $key] } { return -code error "test case attempts to read unknown registry entry $path $key" } return [dict get $reg $path $key] } # Test some of the basics of [clock format] test clock-1.0 "clock format - wrong # args" { list [catch {clock format} msg] $msg $::errorCode | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | < < | > > | > > > > > > > > > > > > > > > | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | } if { ![dict exists $reg $path $key] } { return -code error "test case attempts to read unknown registry entry $path $key" } return [dict get $reg $path $key] } # Base test cases: test clock-0.1 "initial: auto-loading of ensemble and stubs on demand" -setup { set i [interp create]; # because clock can be used somewhere, test it in new interp: } -body { $i eval { lappend ret ens:[namespace ensemble exists ::clock] clock seconds; # init ensemble (but not yet stubs, loading of clock.tcl retarded) lappend ret ens:[namespace ensemble exists ::clock] lappend ret stubs:[expr {[namespace which -command ::tcl::clock::GetSystemTimeZone] ne ""}] clock format -now; # clock.tcl stubs expected lappend ret stubs:[expr {[namespace which -command ::tcl::clock::GetSystemTimeZone] ne ""}] } } -cleanup { interp delete $i } -result {ens:0 ens:1 stubs:0 stubs:1} test clock-0.1a "initial: safe interpreter shares clock command with parent" -setup { set i [interp create] $i eval {set sci [interp create -safe]} } -body { $i eval { lappend ret ens:[namespace ensemble exists ::clock] $sci eval { clock seconds }; # init ensemble (but not yet stubs, loading of clock.tcl retarded) lappend ret ens:[namespace ensemble exists ::clock] lappend ret stubs:[expr {[namespace which -command ::tcl::clock::GetSystemTimeZone] ne ""}] $sci eval { clock format -now }; # clock.tcl stubs expected lappend ret stubs:[expr {[namespace which -command ::tcl::clock::GetSystemTimeZone] ne ""}] } } -cleanup { interp delete $i } -result {ens:0 ens:1 stubs:0 stubs:1} test clock-0.2 "initial: loading of format/locale does not overwrite interp state (errorInfo)" -setup { # be sure - we have no cached locale/msgcat, etc: if {[namespace which -command ::tcl::clock::ClearCaches] ne ""} { ::tcl::clock::ClearCaches } } -body { if {[catch { return -level 0 -code error -errorcode {EXPERR TEST-ERROR} -errorinfo "ERROR expected error" test }]} { clock format -now -locale de; # should not overwrite error code/info list $::errorCode $::errorInfo } } -result {{EXPERR TEST-ERROR} {ERROR expected error}} # Test some of the basics of [clock format] set syntax "clockval|-now ?-format string? ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?" test clock-1.0 "clock format - wrong # args" { list [catch {clock format} msg] $msg $::errorCode } [subst {1 {wrong # args: should be "clock format $syntax"} {CLOCK wrongNumArgs}}] test clock-1.0.1 "clock format - wrong # args (compiled ensemble with invalid syntax)" { list [catch {clock format 0 -too-few-options-4-test} msg] $msg $::errorCode } [subst {1 {wrong # args: should be "clock format $syntax"} {CLOCK wrongNumArgs}}] test clock-1.1 "clock format - bad time" { list [catch {clock format foo} msg] $msg } {1 {expected integer but got "foo"}} test clock-1.2 "clock format - bad gmt val" { list [catch {clock format 0 -gmt foo} msg] $msg } {1 {expected boolean value but got "foo"}} test clock-1.3 "clock format - empty val" { clock format 0 -gmt 1 -format "" } {} test clock-1.4 "clock format - bad flag" { # range error message for possible extensions: list [catch {clock format 0 -oops badflag} msg] $msg $::errorCode } [subst {1 {bad option "-oops": must be -format, -gmt, -locale, or -timezone} {CLOCK badOption -oops}}] test clock-1.4.1 "clock format - unexpected option for this sub-command" { # range error message for possible extensions: list [catch {clock format 0 -base 0} msg] $msg $::errorCode } [subst {1 {bad option "-base": must be -format, -gmt, -locale, or -timezone} {CLOCK badOption -base}}] test clock-1.5 "clock format - bad timezone" { list [catch {clock format 0 -format "%s" -timezone :NOWHERE} msg] $msg $::errorCode } {1 {time zone ":NOWHERE" not found} {CLOCK badTimeZone :NOWHERE}} test clock-1.6 "clock format - gmt + timezone" { list [catch {clock format 0 -timezone :GMT -gmt true} msg] $msg $::errorCode } {1 {cannot use -gmt and -timezone in same call} {CLOCK gmtWithTimezone}} test clock-1.7 "clock format - option abbreviations" { clock format 0 -g true -f "%Y-%m-%d" } 1970-01-01 test clock-1.7.1 "clock format - command abbreviations (compat regression test)" { clock f 0 -g 1 -f "%Y-%m-%d" } 1970-01-01 test clock-1.8 "clock format -now" { # give one second more for test (if on boundary of the current second): set n [clock format [clock seconds] -g 1 -f "%s"] expr {[clock format -now -g 1 -f "%s"] in [list $n [incr n]]} } 1 test clock-1.9 "clock arguments: option doubly present" { list [catch {clock format 0 -gmt 1 -gmt 0} result] $result } {1 {bad option "-gmt": doubly present}} # BEGIN testcases2 # Test formatting of Gregorian year, month, day, all formats # Formats tested: %b %B %c %Ec %C %EC %d %Od %e %Oe %h %j %J %m %Om %N %x %Ex %y %Oy %Y %EY test clock-2.1 {conversion of 1872-01-01} { |
︙ | ︙ | |||
15287 15288 15289 15290 15291 15292 15293 15294 15295 15296 15297 15298 15299 15300 | } {23 xxiii 11 xi 23 xxiii 11 xi 59 lix PM pm 11:59:58 pm 23:59 58 lviii 23:59:58 23:59:58 xxiii h lix m lviii s Thu Jan 1 23:59:58 GMT 1970} test clock-4.96 { format time of day 23:59:59 } { clock format 86399 \ -format {%H %OH %I %OI %k %Ok %l %Ol %M %OM %p %P %r %R %S %OS %T %X %EX %+} \ -locale en_US_roman \ -gmt true } {23 xxiii 11 xi 23 xxiii 11 xi 59 lix PM pm 11:59:59 pm 23:59 59 lix 23:59:59 23:59:59 xxiii h lix m lix s Thu Jan 1 23:59:59 GMT 1970} # END testcases4 # BEGIN testcases5 # Test formatting of Daylight Saving Time test clock-5.1 {does Detroit exist} { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 15374 15375 15376 15377 15378 15379 15380 15381 15382 15383 15384 15385 15386 15387 15388 15389 15390 15391 15392 15393 15394 15395 15396 15397 15398 15399 15400 15401 15402 15403 15404 15405 15406 15407 15408 15409 15410 15411 15412 15413 15414 15415 15416 15417 15418 15419 15420 15421 15422 15423 15424 15425 15426 15427 15428 15429 15430 15431 15432 15433 15434 15435 15436 15437 15438 15439 15440 15441 15442 15443 15444 15445 15446 15447 15448 15449 15450 15451 15452 15453 15454 15455 15456 15457 15458 15459 15460 15461 15462 15463 15464 15465 | } {23 xxiii 11 xi 23 xxiii 11 xi 59 lix PM pm 11:59:58 pm 23:59 58 lviii 23:59:58 23:59:58 xxiii h lix m lviii s Thu Jan 1 23:59:58 GMT 1970} test clock-4.96 { format time of day 23:59:59 } { clock format 86399 \ -format {%H %OH %I %OI %k %Ok %l %Ol %M %OM %p %P %r %R %S %OS %T %X %EX %+} \ -locale en_US_roman \ -gmt true } {23 xxiii 11 xi 23 xxiii 11 xi 59 lix PM pm 11:59:59 pm 23:59 59 lix 23:59:59 23:59:59 xxiii h lix m lix s Thu Jan 1 23:59:59 GMT 1970} test clock-4.97.1 { format JDN/JD (calendar and astronomical) } { clock format 0 -format {%J %EJ %Ej} -gmt true } {2440588 2440588.0 2440587.5} test clock-4.97.2 { format JDN/JD (calendar and astronomical) } { clock format 43200 -format {%J %EJ %Ej} -gmt true } {2440588 2440588.5 2440588.0} test clock-4.97.3 { format JDN/JD (calendar and astronomical) } { clock format 86399 -format {%J %EJ %Ej} -gmt true } {2440588 2440588.99998843 2440588.49998843} test clock-4.97.4 { format JDN/JD (calendar and astronomical) } { clock format 86400 -format {%J %EJ %Ej} -gmt true } {2440589 2440589.0 2440588.5} test clock-4.97.5 { format JDN/JD (calendar and astronomical) } { clock format 129599 -format {%J %EJ %Ej} -gmt true } {2440589 2440589.49998843 2440588.99998843} test clock-4.97.6 { format JDN/JD (calendar and astronomical) } { clock format 129600 -format {%J %EJ %Ej} -gmt true } {2440589 2440589.5 2440589.0} test clock-4.97.7 { format JDN/JD (calendar and astronomical) } { set i 1548249092 list \ [clock format $i -format {%J %EJ %Ej} -gmt true] \ [clock format [incr i] -format {%J %EJ %Ej} -gmt true] \ [clock format [incr i] -format {%J %EJ %Ej} -gmt true] } {{2458507 2458507.54967593 2458507.04967593} {2458507 2458507.5496875 2458507.0496875} {2458507 2458507.54969907 2458507.04969907}} test clock-4.97.8 { format JDN/JD (calendar and astronomical) } { set res {} foreach i { -172800 -129600 -86400 -43200 -1 0 1 21600 43199 43200 86399 86400 86401 108000 129600 172800 } { lappend res $i [clock format [expr {-210866803200 - $i}] \ -format {%EE %Y-%m-%d %T -- %J %EJ %Ej} -gmt true] } set res } [list \ -172800 {B.C.E. 4713-01-03 00:00:00 -- 0000002 2.0 1.5} \ -129600 {B.C.E. 4713-01-02 12:00:00 -- 0000001 1.5 1.0} \ -86400 {B.C.E. 4713-01-02 00:00:00 -- 0000001 1.0 0.5} \ -43200 {B.C.E. 4713-01-01 12:00:00 -- 0000000 0.5 0.0} \ -1 {B.C.E. 4713-01-01 00:00:01 -- 0000000 0.00001157 -0.49998843} \ 0 {B.C.E. 4713-01-01 00:00:00 -- 0000000 0.0 -0.5} \ 1 {B.C.E. 4714-12-31 23:59:59 -- -000001 -0.00001157 -0.50001157} \ 21600 {B.C.E. 4714-12-31 18:00:00 -- -000001 -0.25 -0.75} \ 43199 {B.C.E. 4714-12-31 12:00:01 -- -000001 -0.49998843 -0.99998843} \ 43200 {B.C.E. 4714-12-31 12:00:00 -- -000001 -0.5 -1.0} \ 86399 {B.C.E. 4714-12-31 00:00:01 -- -000001 -0.99998843 -1.49998843} \ 86400 {B.C.E. 4714-12-31 00:00:00 -- -000001 -1.0 -1.5} \ 86401 {B.C.E. 4714-12-30 23:59:59 -- -000002 -1.00001157 -1.50001157} \ 108000 {B.C.E. 4714-12-30 18:00:00 -- -000002 -1.25 -1.75} \ 129600 {B.C.E. 4714-12-30 12:00:00 -- -000002 -1.5 -2.0} \ 172800 {B.C.E. 4714-12-30 00:00:00 -- -000002 -2.0 -2.5} \ ] test clock-4.97.9 { format JDN/JD (calendar and astronomical) } { set res {} foreach i { -86400 -43200 -1 0 1 43199 43200 43201 86400 } { lappend res $i [clock format [expr {653133196800 + $i}] \ -format {%Y-%m-%d %T -- %J %EJ %Ej} -gmt true] } set res } [list \ -86400 {22666-12-19 00:00:00 -- 9999999 9999999.0 9999998.5} \ -43200 {22666-12-19 12:00:00 -- 9999999 9999999.5 9999999.0} \ -1 {22666-12-19 23:59:59 -- 9999999 9999999.99998843 9999999.49998843} \ 0 {22666-12-20 00:00:00 -- 10000000 10000000.0 9999999.5} \ 1 {22666-12-20 00:00:01 -- 10000000 10000000.00001157 9999999.50001157} \ 43199 {22666-12-20 11:59:59 -- 10000000 10000000.49998843 9999999.99998843} \ 43200 {22666-12-20 12:00:00 -- 10000000 10000000.5 10000000.0} \ 43201 {22666-12-20 12:00:01 -- 10000000 10000000.50001157 10000000.00001157} \ 86400 {22666-12-21 00:00:00 -- 10000001 10000001.0 10000000.5} \ ] # END testcases4 # BEGIN testcases5 # Test formatting of Daylight Saving Time test clock-5.1 {does Detroit exist} { |
︙ | ︙ | |||
18535 18536 18537 18538 18539 18540 18541 | list [catch {clock scan -9223372036854775809 -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} test clock-6.10 {input of seconds - overflow} { list [catch {clock scan 9223372036854775808 -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} foreach sign {{} -} { | | | < | | < > | > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 18700 18701 18702 18703 18704 18705 18706 18707 18708 18709 18710 18711 18712 18713 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 18740 18741 18742 18743 18744 18745 18746 18747 18748 18749 18750 18751 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 18767 18768 18769 18770 18771 18772 18773 18774 18775 18776 18777 18778 18779 18780 18781 18782 18783 18784 18785 18786 18787 18788 18789 18790 18791 18792 18793 18794 18795 18796 18797 18798 18799 18800 18801 18802 18803 18804 18805 18806 18807 18808 18809 18810 18811 18812 18813 18814 18815 18816 18817 18818 18819 18820 18821 18822 18823 18824 18825 18826 18827 18828 18829 18830 18831 18832 18833 18834 18835 18836 18837 18838 18839 18840 18841 18842 18843 18844 18845 18846 18847 18848 18849 18850 18851 18852 18853 18854 18855 18856 18857 18858 18859 18860 18861 18862 18863 18864 18865 18866 18867 18868 18869 18870 18871 18872 18873 18874 18875 18876 18877 18878 18879 18880 18881 18882 18883 18884 18885 18886 18887 18888 18889 18890 18891 18892 18893 18894 18895 18896 18897 18898 18899 18900 18901 18902 18903 18904 18905 18906 18907 18908 18909 18910 18911 18912 18913 18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 18927 18928 18929 | list [catch {clock scan -9223372036854775809 -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} test clock-6.10 {input of seconds - overflow} { list [catch {clock scan 9223372036854775808 -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} foreach sign {{} -} { test clock-6.10a {input of seconds - overflow, bug [1f40aa83c5]} { list [catch {clock scan ${sign}27670116110564327423 -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} test clock-6.10b {input of seconds - overflow, bug [1f40aa83c5]} { list [catch {clock scan ${sign}27670116110564327424 -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} test clock-6.10c {input of seconds - no overflow, bug [1f40aa83c5]} { list [catch {clock scan ${sign}[string repeat 9 18] -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } [list 0 ${sign}[string repeat 9 18] {}] test clock-6.10d {input of seconds - overflow, bug [1f40aa83c5]} { list [catch {clock scan ${sign}[string repeat 9 19] -format %s -gmt true} result opt] $result [dict getd $opt -errorcode ""] } {1 {integer value too large to represent} {CLOCK dateTooLarge}} # both fololowing freescan test don't generate overflow error, # since it is a free scan, thus the token is simply not recognized further in yacc lexer, # therefore we get parse error (can be surely changed latter): test clock-6.10e {input of seconds - overflow (but since freescan parse error, but not boom), bug [1f40aa83c5]} -body { list [catch {clock scan ${sign}27670116110564327423 -gmt true} result opt] $result [dict getd $opt -errorcode ""] } -match glob -result {1 {unable to convert date-time string "*": syntax error *} {TCL VALUE DATE PARSE}} test clock-6.10f {input of seconds - overflow (but since freescan parse error, but not boom), bug [1f40aa83c5]} -body { list [catch {clock scan ${sign}27670116110564327424 -gmt true} result opt] $result [dict getd $opt -errorcode ""] } -match glob -result {1 {unable to convert date-time string "*": syntax error *} {TCL VALUE DATE PARSE}} }; unset sign test clock-6.11 {input of seconds - two values} { clock scan {1 2} -format {%s %s} -gmt true } 2 test clock-6.12.0 {input of short forms of locale token (%b)} { list [clock scan "12 Ja 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1] \ [clock scan "12 Au 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1] } {979257600 997574400} test clock-6.12.1 {input of all forms of unambiguous short locale token (%b)} { # find all unambiguous short forms and check it'll be scanned successful and correctly: set months {January February March April May June July August September October November December} set res {} foreach mon $months { set i 0 while {[incr i] < [string length $mon]} { # short month form: set shm [string range $mon 0 $i] # differentiate ambiguous: if {[llength [lsearch -all -glob $months "${shm}*"]] <= 1} { # unambiguous (expected date with wull month): set e "12 $mon 2001" } else { # ambiguous (expected error): set e "input string does not match supplied format" } set s "12 $shm 2001" # scan and format with full month name: catch {clock format \ [clock scan $s -format "%d %b %Y" -locale en_US_roman -gmt 1] \ -format "%d %B %Y" -locale en_US_roman -gmt 1} t # check it corresponds the full form: if {$t ne $e} { lappend res "unexpected result converting $s, expected \"$e\", got \"$t\"" } } } set res } {} test clock-6.13 {input of lowercase locale token (%b)} { list [clock scan "12 ja 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1] \ [clock scan "12 au 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1] } {979257600 997574400} test clock-6.14 {input of uppercase locale token (%b)} { list [clock scan "12 JA 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1] \ [clock scan "12 AU 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1] } {979257600 997574400} test clock-6.15 {input of ambiguous short locale token (%b)} { list [catch { clock scan "12 J 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1 } result] $result $errorCode } {1 {input string does not match supplied format} {CLOCK badInputString}} test clock-6.16 {input of ambiguous short locale token (%b)} { list [catch { clock scan "12 Ju 2001" -format "%d %b %Y" -locale en_US_roman -gmt 1 } result] $result $errorCode } {1 {input string does not match supplied format} {CLOCK badInputString}} test clock-6.17 {spaces are always optional in non-strict mode (default)} { list [clock scan "2009-06-30T18:30:00+02:00" -format "%Y-%m-%dT%H:%M:%S%z" -gmt 1] \ [clock scan "2009-06-30T18:30:00 +02:00" -format "%Y-%m-%dT%H:%M:%S%z" -gmt 1] \ [clock scan "2009-06-30T18:30:00Z" -format "%Y-%m-%dT%H:%M:%S%z" -timezone CET] \ [clock scan "2009-06-30T18:30:00 Z" -format "%Y-%m-%dT%H:%M:%S%z" -timezone CET] } {1246379400 1246379400 1246386600 1246386600} test clock-6.18 {zone token (%z) is optional} { list [clock scan "2009-06-30T18:30:00 -01:00" -format "%Y-%m-%dT%H:%M:%S%z" -gmt 1] \ [clock scan "2009-06-30T18:30:00" -format "%Y-%m-%dT%H:%M:%S%z" -gmt 1] \ [clock scan " 2009-06-30T18:30:00 " -format "%Y-%m-%dT%H:%M:%S%z" -gmt 1] \ } {1246390200 1246386600 1246386600} test clock-6.19 {no token parsing} { list [catch { clock scan "%E%O%" -format "%E%O%" }] \ [catch { clock scan "...%..." -format "...%%..." }] } {0 0} test clock-6.20 {special char tokens %n, %t} { clock scan "30\t06\t2009\n18\t30" -format "%d%t%m%t%Y%n%H%t%M" -gmt 1 } 1246386600 # Hi, Jeff! proc _testStarDates {s {days {366*2}} {step {86400}}} { set step [expr {int($step * 86400)}] # reconvert - arrange in order of stardate: set s [set i [clock scan [clock format $s -f "%Q" -g 1] -g 1]] # test: set wrong {} while {$i < $s + $days*86400} { set d [clock format $i -f "%Q" -g 1] if {![regexp {^Stardate \d+\.\d$} $d]} { lappend wrong "wrong: $d -- ($i) -- [clock format $i -g 1]" } if {[catch { set i2 [clock scan $d -f "%Q" -g 1] } msg]} { lappend wrong "$d -- ($i) -- [clock format $i -g 1]: $msg" } if {$i != $i2} { lappend wrong "$d -- ($i != $i2) -- [clock format $i -g 1]" } incr i $step } join $wrong \n } test clock-6.21.0 {Stardate 0 day} { list [set d [clock format -757382400 -format "%Q" -gmt 1]] \ [clock scan $d -format "%Q" -gmt 1] } [list "Stardate 00000.0" -757382400] test clock-6.21.0.1 {Stardate 0.1 - 1.9 (test negative clock value -> positive Stardate)} { _testStarDates -757382400 2 0.1 } {} test clock-6.21.0.2 {Stardate 10000.1 - 10002.9 (test negative clock value -> positive Stardate)} { _testStarDates [clock scan "Stardate 10000.1" -f %Q -g 1] 3 0.1 } {} test clock-6.21.0.2 {Stardate 80000.1 - 80002.9 (test positive clock value)} { _testStarDates [clock scan "Stardate 80001.1" -f %Q -g 1] 3 0.1 } {} test clock-6.21.1 {Stardate} { list [set d [clock format 1482857280 -format "%Q" -gmt 1]] \ [clock scan $d -format "%Q" -gmt 1] } [list "Stardate 70986.7" 1482857280] test clock-6.21.2 {Stardate next time} { list [set d [clock format 1482865920 -format "%Q" -gmt 1]] \ [clock scan $d -format "%Q" -gmt 1] } [list "Stardate 70986.8" 1482865920] test clock-6.21.3 {Stardate correct scan over year (leap year, begin, middle and end of the year)} { _testStarDates [clock scan "01.01.2016" -f "%d.%m.%Y" -g 1] [expr {366*2}] 1 } {} rename _testStarDates {} test clock-6.22.1 {Greedy match} { clock format [clock scan "111" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Mon Jan 01 00:00:00 GMT 2001} test clock-6.22.2 {Greedy match} { clock format [clock scan "1111" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Thu Jan 11 00:00:00 GMT 2001} test clock-6.22.3 {Greedy match} { clock format [clock scan "11111" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Sun Nov 11 00:00:00 GMT 2001} test clock-6.22.4 {Greedy match} { clock format [clock scan "111111" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Fri Nov 11 00:00:00 GMT 2011} test clock-6.22.5 {Greedy match} { clock format [clock scan "1 1 1" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Mon Jan 01 00:00:00 GMT 2001} test clock-6.22.6 {Greedy match} { clock format [clock scan "111 1" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Thu Jan 11 00:00:00 GMT 2001} test clock-6.22.7 {Greedy match} { clock format [clock scan "1 111" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Thu Nov 01 00:00:00 GMT 2001} test clock-6.22.8 {Greedy match} { clock format [clock scan "1 11 1" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Thu Nov 01 00:00:00 GMT 2001} test clock-6.22.9 {Greedy match} { clock format [clock scan "1 11 11" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Tue Nov 01 00:00:00 GMT 2011} test clock-6.22.10 {Greedy match} { clock format [clock scan "11 11 11" -format "%d%m%y" -gmt 1] -locale en -gmt 1 } {Fri Nov 11 00:00:00 GMT 2011} test clock-6.22.11 {Greedy match} { clock format [clock scan "1111 120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Sat Jan 01 01:02:00 GMT 2011} test clock-6.22.12 {Greedy match} { clock format [clock scan "11 1 120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Mon Jan 01 01:02:00 GMT 2001} test clock-6.22.13 {Greedy match} { clock format [clock scan "1 11 120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Mon Jan 01 01:02:00 GMT 2001} test clock-6.22.14 {Greedy match} { clock format [clock scan "111120" -format "%y%m%d%H%M%S" -gmt 1] -locale en -gmt 1 } {Mon Jan 01 01:02:00 GMT 2001} test clock-6.22.15 {Greedy match} { clock format [clock scan "1111120" -format "%y%m%d%H%M%S" -gmt 1] -locale en -gmt 1 } {Sat Jan 01 01:02:00 GMT 2011} test clock-6.22.16 {Greedy match} { clock format [clock scan "11121120" -format "%y%m%d%H%M%S" -gmt 1] -locale en -gmt 1 } {Thu Dec 01 01:02:00 GMT 2011} test clock-6.22.17 {Greedy match} { clock format [clock scan "111213120" -format "%y%m%d%H%M%S" -gmt 1] -locale en -gmt 1 } {Tue Dec 13 01:02:00 GMT 2011} test clock-6.22.17 {Greedy match (space wins as date-time separator)} { clock format [clock scan "1112 13120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Sun Jan 02 13:12:00 GMT 2011} test clock-6.22.18 {Greedy match (second space wins as date-time separator)} { clock format [clock scan "1112 13 120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Tue Dec 13 01:02:00 GMT 2011} test clock-6.22.19 {Greedy match (space wins as date-time separator)} { clock format [clock scan "111 213120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Mon Jan 01 21:31:20 GMT 2001} test clock-6.22.20 {Greedy match (second space wins as date-time separator)} { clock format [clock scan "111 2 13120" -format "%y%m%d %H%M%S" -gmt 1] -locale en -gmt 1 } {Sun Jan 02 13:12:00 GMT 2011} test clock-7.1 {Julian Day} { clock scan 0 -format %J -gmt true } -210866803200 test clock-7.2 {Julian Day} { clock format [clock scan 2440588 -format %J -gmt true] \ |
︙ | ︙ | |||
18619 18620 18621 18622 18623 18624 18625 18626 18627 18628 18629 18630 18631 18632 | set s0m24h [clock add $s0 -24 hours -timezone :UTC] set J0m24h [scan [clock format $s0m24h -format %J -gmt true] %lld] set s0m1s [clock add $s0 -1 seconds -timezone :UTC] set J0m1s [scan [clock format $s0m1s -format %J -gmt true] %lld] list $s0m1d $s0m24h $J0m24h $s0m1s $J0m1s $s0 $J0 \ [::tcl::mathop::== $s0m1d $s0m24h] [::tcl::mathop::== $J0m24h $J0m1s] } [list -210866889600 -210866889600 -1 -210866803201 -1 -210866803200 0 1 1] # BEGIN testcases8 # Test parsing of ccyymmdd test clock-8.1 {parse ccyymmdd} { clock scan {1970 Jan 02} -format {%C%y %b %d} -locale en_US_roman -gmt 1 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 18983 18984 18985 18986 18987 18988 18989 18990 18991 18992 18993 18994 18995 18996 18997 18998 18999 19000 19001 19002 19003 19004 19005 19006 19007 19008 19009 19010 19011 19012 19013 19014 19015 19016 19017 19018 19019 19020 19021 19022 19023 19024 19025 19026 19027 19028 19029 19030 19031 19032 19033 19034 19035 19036 19037 19038 19039 19040 19041 19042 19043 19044 19045 19046 19047 19048 19049 19050 19051 19052 19053 19054 19055 19056 19057 19058 19059 19060 19061 19062 19063 19064 19065 19066 19067 19068 19069 19070 19071 19072 19073 19074 19075 19076 19077 19078 19079 19080 19081 | set s0m24h [clock add $s0 -24 hours -timezone :UTC] set J0m24h [scan [clock format $s0m24h -format %J -gmt true] %lld] set s0m1s [clock add $s0 -1 seconds -timezone :UTC] set J0m1s [scan [clock format $s0m1s -format %J -gmt true] %lld] list $s0m1d $s0m24h $J0m24h $s0m1s $J0m1s $s0 $J0 \ [::tcl::mathop::== $s0m1d $s0m24h] [::tcl::mathop::== $J0m24h $J0m1s] } [list -210866889600 -210866889600 -1 -210866803201 -1 -210866803200 0 1 1] test clock-7.11.1 {Calendar vs Astronomical Julian Day (without and with time fraction)} { list \ [clock scan {2440588} -format {%J} -gmt true] \ [clock scan {2440588} -format {%EJ} -gmt true] \ [clock scan {2440588} -format {%Ej} -gmt true] \ [clock scan {2440588.5} -format {%EJ} -gmt true] \ [clock scan {2440588.5} -format {%Ej} -gmt true] \ } {0 0 43200 43200 86400} test clock-7.11.2 {Astronomical JDN/JD} { clock scan 0 -format %Ej -gmt true } -210866760000 test clock-7.12 {Astronomical JDN/JD} { clock format [clock scan 2440587.5 -format %Ej -gmt true] \ -format "%Y-%m-%d %T" -gmt true } "1970-01-01 00:00:00" test clock-7.13 {Astronomical JDN/JD} { clock format [clock scan 2451544.5 -format %Ej -gmt true] \ -format "%Y-%m-%d %T" -gmt true } "2000-01-01 00:00:00" test clock-7.13.1 {Astronomical JDN/JD} { clock format [clock scan 2488069.5 -format %Ej -gmt true] \ -format "%Y-%m-%d %T" -gmt true } "2100-01-01 00:00:00" test clock-7.14 {Astronomical JDN/JD} { clock format [clock scan 5373483.5 -format %Ej -gmt true] \ -format "%Y-%m-%d %T" -gmt true } "9999-12-31 00:00:00" test clock-7.14.1 {Astronomical JDN/JD} { clock format [clock scan 5373484 -format %Ej -gmt true] \ -format "%Y-%m-%d %T" -gmt true } "9999-12-31 12:00:00" test clock-7.14.2 {Astronomical JDN/JD} { clock format [clock scan 5373484.49999 -format %Ej -gmt true] \ -format "%Y-%m-%d %T" -gmt true } "9999-12-31 23:59:59" test clock-7.15 {Astronomical JDN/JD, bad} { list [catch { clock scan bogus -format %Ej } result] $result $errorCode } {1 {input string does not match supplied format} {CLOCK badInputString}} test clock-7.16 {Astronomical JDN/JD, overflow} { list [catch { clock scan 5373484.5 -format %Ej } result] $result $errorCode \ [catch { clock scan 5373485 -format %Ej } result] $result $errorCode \ [catch { clock scan 2147483648 -format %Ej } result] $result $errorCode \ [catch { clock scan 2147483648.5 -format %Ej } result] $result $errorCode } [lrepeat 4 1 {requested date too large to represent} {CLOCK dateTooLarge}] test clock-7.18 {Astronomical JDN/JD, same precedence as seconds (last wins} { list [clock scan {2440588 86400} -format {%Ej %s} -gmt true] \ [clock scan {2440589 0} -format {%Ej %s} -gmt true] \ [clock scan {86400 2440588} -format {%s %Ej} -gmt true] \ [clock scan {0 2440589} -format {%s %Ej} -gmt true] } {86400 0 43200 129600} test clock-7.19 {Astronomical JDN/JD, two values} { clock scan {2440588 2440589} -format {%Ej %Ej} -gmt true } 129600 test clock-7.20 {all JDN/JD are signed (and extended accept floats)} { set res {} foreach i {%J %EJ %Ej} { lappend res [clock scan "-1" -format $i -gmt 1] } foreach i {%EJ %Ej} { lappend res [clock scan "-1.5" -format $i -gmt 1] } set res } {-210866889600 -210866889600 -210866846400 -210866846400 -210866803200} # BEGIN testcases8 # Test parsing of ccyymmdd test clock-8.1 {parse ccyymmdd} { clock scan {1970 Jan 02} -format {%C%y %b %d} -locale en_US_roman -gmt 1 |
︙ | ︙ | |||
21030 21031 21032 21033 21034 21035 21036 | } 1009756800 # END testcases8 test clock-9.1 {seconds take precedence over ccyymmdd} { clock scan {0 20000101} -format {%s %Y%m%d} -gmt true } 0 | | > | > | > > > > > > > > > > > | 21479 21480 21481 21482 21483 21484 21485 21486 21487 21488 21489 21490 21491 21492 21493 21494 21495 21496 21497 21498 21499 21500 21501 21502 21503 21504 21505 21506 21507 21508 | } 1009756800 # END testcases8 test clock-9.1 {seconds take precedence over ccyymmdd} { clock scan {0 20000101} -format {%s %Y%m%d} -gmt true } 0 test clock-9.2 {Calendar julian day takes precedence over ccyymmdd} { list \ [clock scan {2440588 20000101} -format {%J %Y%m%d} -gmt true] \ [clock scan {2440588 20000101} -format {%EJ %Y%m%d} -gmt true] } {0 0} test clock-9.2.1 {Calendar julian day (with time fraction) takes precedence over date-time} { list \ [clock scan {2440588.0 20000101 010203} -format {%EJ %Y%m%d %H%M%S} -gmt true] \ [clock scan {2440588.5 20000101 010203} -format {%EJ %Y%m%d %H%M%S} -gmt true] } {0 43200} test clock-9.3 {Astro julian day takes always precedence over date-time} { list \ [clock scan {2440587.5 20000101 010203} -format {%Ej %Y%m%d %H%M%S} -gmt true] \ [clock scan {2440588 20000101 010203} -format {%Ej %Y%m%d %H%M%S} -gmt true] } {0 43200} # Test parsing of ccyyddd test clock-10.1 {parse ccyyddd} { clock scan {1970 001} -format {%Y %j} -locale en_US_roman -gmt 1 } 0 test clock-10.2 {parse ccyyddd} { |
︙ | ︙ | |||
21073 21074 21075 21076 21077 21078 21079 | test clock-10.10 {julian day takes precedence over ccyyddd} { list [clock scan {2440588 2000001} -format {%J %Y%j} -gmt true] \ [clock scan {2000001 2440588} -format {%Y%j %J} -gmt true] } {0 0} # BEGIN testcases11 | | > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | 21535 21536 21537 21538 21539 21540 21541 21542 21543 21544 21545 21546 21547 21548 21549 21550 21551 21552 21553 21554 21555 21556 21557 21558 21559 21560 21561 21562 21563 21564 21565 21566 21567 21568 21569 21570 21571 21572 21573 21574 21575 21576 21577 21578 21579 21580 21581 21582 21583 21584 21585 21586 21587 21588 21589 21590 21591 21592 21593 21594 21595 21596 21597 21598 21599 21600 21601 21602 21603 21604 21605 21606 21607 21608 21609 21610 21611 21612 21613 21614 21615 21616 21617 21618 21619 21620 21621 21622 21623 21624 21625 21626 21627 21628 21629 | test clock-10.10 {julian day takes precedence over ccyyddd} { list [clock scan {2440588 2000001} -format {%J %Y%j} -gmt true] \ [clock scan {2000001 2440588} -format {%Y%j %J} -gmt true] } {0 0} # BEGIN testcases11 # Test precedence yyyymmdd over yyyyddd if {!$valid_mode} { set res {-result 0} } else { set res {-returnCodes error -result "unable to convert input string: ambiguous day"} } test clock-11.1 {precedence of ccyymmdd over ccyyddd} -body { clock scan 19700101002 -format %Y%m%d%j -gmt 1 } {*}$res test clock-11.2 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01197001002 -format %m%Y%d%j -gmt 1 } {*}$res test clock-11.3 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01197001002 -format %d%Y%m%j -gmt 1 } {*}$res test clock-11.4 {precedence of ccyymmdd over ccyyddd} -body { clock scan 00219700101 -format %j%Y%m%d -gmt 1 } {*}$res test clock-11.5 {precedence of ccyymmdd over ccyyddd} -body { clock scan 19700100201 -format %Y%m%j%d -gmt 1 } {*}$res test clock-11.6 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01197000201 -format %m%Y%j%d -gmt 1 } {*}$res test clock-11.7 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01197000201 -format %d%Y%j%m -gmt 1 } {*}$res test clock-11.8 {precedence of ccyymmdd over ccyyddd} -body { clock scan 00219700101 -format %j%Y%d%m -gmt 1 } {*}$res test clock-11.9 {precedence of ccyymmdd over ccyyddd} -body { clock scan 19700101002 -format %Y%d%m%j -gmt 1 } {*}$res test clock-11.10 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01011970002 -format %m%d%Y%j -gmt 1 } {*}$res test clock-11.11 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01011970002 -format %d%m%Y%j -gmt 1 } {*}$res test clock-11.12 {precedence of ccyymmdd over ccyyddd} -body { clock scan 00201197001 -format %j%m%Y%d -gmt 1 } {*}$res test clock-11.13 {precedence of ccyymmdd over ccyyddd} -body { clock scan 19700100201 -format %Y%d%j%m -gmt 1 } {*}$res test clock-11.14 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01010021970 -format %m%d%j%Y -gmt 1 } {*}$res test clock-11.15 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01010021970 -format %d%m%j%Y -gmt 1 } {*}$res test clock-11.16 {precedence of ccyymmdd over ccyyddd} -body { clock scan 00201011970 -format %j%m%d%Y -gmt 1 } {*}$res test clock-11.17 {precedence of ccyymmdd over ccyyddd} -body { clock scan 19700020101 -format %Y%j%m%d -gmt 1 } {*}$res test clock-11.18 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01002197001 -format %m%j%Y%d -gmt 1 } {*}$res test clock-11.19 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01002197001 -format %d%j%Y%m -gmt 1 } {*}$res test clock-11.20 {precedence of ccyymmdd over ccyyddd} -body { clock scan 00201197001 -format %j%d%Y%m -gmt 1 } {*}$res test clock-11.21 {precedence of ccyymmdd over ccyyddd} -body { clock scan 19700020101 -format %Y%j%d%m -gmt 1 } {*}$res test clock-11.22 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01002011970 -format %m%j%d%Y -gmt 1 } {*}$res test clock-11.23 {precedence of ccyymmdd over ccyyddd} -body { clock scan 01002011970 -format %d%j%m%Y -gmt 1 } {*}$res test clock-11.24 {precedence of ccyymmdd over ccyyddd} -body { clock scan 00201011970 -format %j%d%m%Y -gmt 1 } {*}$res unset -nocomplain res # END testcases11 # BEGIN testcases12 # Test parsing of ccyyWwwd test clock-12.1 {parse ccyyWwwd} { |
︙ | ︙ | |||
21443 21444 21445 21446 21447 21448 21449 | clock scan {2002 W01 i} -format {%G W%V %Ou} -locale en_US_roman -gmt 1 } 1009756800 test clock-12.96 {parse ccyyWwwd} { clock scan {2002 W01 i} -format {%G W%V %Ow} -locale en_US_roman -gmt 1 } 1009756800 # END testcases12 | | | | 21912 21913 21914 21915 21916 21917 21918 21919 21920 21921 21922 21923 21924 21925 21926 21927 21928 21929 21930 | clock scan {2002 W01 i} -format {%G W%V %Ou} -locale en_US_roman -gmt 1 } 1009756800 test clock-12.96 {parse ccyyWwwd} { clock scan {2002 W01 i} -format {%G W%V %Ow} -locale en_US_roman -gmt 1 } 1009756800 # END testcases12 test clock-13.1 {test that %s takes precedence over ccyyWwwd} valid_off { list [clock scan {0 2000W011} -format {%s %GW%V%u} -gmt true] \ [clock scan {2000W011 0} -format {%GW%V%u %s} -gmt true] } {0 0} test clock-13.2 {test that %J takes precedence over ccyyWwwd} valid_off { list [clock scan {2440588 2000W011} -format {%J %GW%V%u} -gmt true] \ [clock scan {2000W011 2440588} -format {%GW%V%u %J} -gmt true] } {0 0} test clock-13.3 {invalid weekday} { catch {clock scan 2000W018 -format %GW%V%u -gmt true} result list $result $::errorCode } {{day of week is greater than 7} {CLOCK badDayOfWeek}} |
︙ | ︙ | |||
23783 23784 23785 23786 23787 23788 23789 | } {0 0} test clock-15.2 {yymmdd precedence below julian day} { list [clock scan {2440588 000101} -format {%J %y%m%d} -gmt true] \ [clock scan {000101 2440588} -format {%y%m%d %J} -gmt true] } {0 0} | | | 24252 24253 24254 24255 24256 24257 24258 24259 24260 24261 24262 24263 24264 24265 24266 | } {0 0} test clock-15.2 {yymmdd precedence below julian day} { list [clock scan {2440588 000101} -format {%J %y%m%d} -gmt true] \ [clock scan {000101 2440588} -format {%y%m%d %J} -gmt true] } {0 0} test clock-15.3 {yymmdd precedence below yyyyWwwd} valid_off { list [clock scan {1970W014000101} -format {%GW%V%u%y%m%d} -gmt true] \ [clock scan {0001011970W014} -format {%y%m%d%GW%V%u} -gmt true] } {0 0} # Test parsing of yyddd test clock-16.1 {parse yyddd} { |
︙ | ︙ | |||
23823 23824 23825 23826 23827 23828 23829 | list [clock scan {0 00001} -format {%s %y%j} -gmt true] \ [clock scan {00001 0} -format {%y%j %s} -gmt true] } {0 0} test clock-16.10 {julian day takes precedence over yyddd} { list [clock scan {2440588 00001} -format {%J %y%j} -gmt true] \ [clock scan {00001 2440588} -format {%Y%j %J} -gmt true] } {0 0} | | | 24292 24293 24294 24295 24296 24297 24298 24299 24300 24301 24302 24303 24304 24305 24306 | list [clock scan {0 00001} -format {%s %y%j} -gmt true] \ [clock scan {00001 0} -format {%y%j %s} -gmt true] } {0 0} test clock-16.10 {julian day takes precedence over yyddd} { list [clock scan {2440588 00001} -format {%J %y%j} -gmt true] \ [clock scan {00001 2440588} -format {%Y%j %J} -gmt true] } {0 0} test clock-16.11 {yyddd precedence below yyyyWwwd} valid_off { list [clock scan {1970W01400001} -format {%GW%V%u%y%j} -gmt true] \ [clock scan {000011970W014} -format {%y%j%GW%V%u} -gmt true] } {0 0} # BEGIN testcases17 # Test parsing of yyWwwd |
︙ | ︙ | |||
24124 24125 24126 24127 24128 24129 24130 | test clock-17.96 {parse yyWwwd} { clock scan {02 W01 i} -format {%g W%V %Ow} -locale en_US_roman -gmt 1 } 1009756800 # END testcases17 # Test precedence of yyWwwd | | | | | 24593 24594 24595 24596 24597 24598 24599 24600 24601 24602 24603 24604 24605 24606 24607 24608 24609 24610 24611 24612 24613 24614 24615 24616 24617 24618 24619 | test clock-17.96 {parse yyWwwd} { clock scan {02 W01 i} -format {%g W%V %Ow} -locale en_US_roman -gmt 1 } 1009756800 # END testcases17 # Test precedence of yyWwwd test clock-18.1 {seconds take precedence over yyWwwd} valid_off { list [clock scan {0 00W014} -format {%s %gW%V%u} -gmt true] \ [clock scan {00W014 0} -format {%gW%V%u %s} -gmt true] } {0 0} test clock-18.2 {julian day takes precedence over yyddd} { list [clock scan {2440588 00W014} -format {%J %gW%V%u} -gmt true] \ [clock scan {00W014 2440588} -format {%gW%V%u %J} -gmt true] } {0 0} test clock-18.3 {yyWwwd precedence below yyyymmdd} valid_off { list [clock scan {19700101 00W014} -format {%Y%m%d %gW%V%u} -gmt true] \ [clock scan {00W014 19700101} -format {%gW%V%u %Y%m%d} -gmt true] } {0 0} test clock-18.4 {yyWwwd precedence below yyyyddd} valid_off { list [clock scan {1970001 00W014} -format {%Y%j %gW%V%u} -gmt true] \ [clock scan {00W014 1970001} -format {%gW%V%u %Y%j} -gmt true] } {0 0} # BEGIN testcases19 # Test parsing of mmdd |
︙ | ︙ | |||
25948 25949 25950 25951 25952 25953 25954 | clock scan vii -format %Ou -locale en_US_roman -gmt 1 -base 1009411200 } 1009670400 test clock-26.48 {parse naked day of week} { clock scan ? -format %Ow -locale en_US_roman -gmt 1 -base 1009411200 } 1009670400 # END testcases26 | > > > > > | | | | | | | | | | | | | | | | | | > | 26417 26418 26419 26420 26421 26422 26423 26424 26425 26426 26427 26428 26429 26430 26431 26432 26433 26434 26435 26436 26437 26438 26439 26440 26441 26442 26443 26444 26445 26446 26447 26448 26449 26450 26451 26452 26453 26454 26455 26456 26457 26458 26459 26460 26461 26462 26463 26464 26465 26466 26467 26468 26469 26470 26471 26472 | clock scan vii -format %Ou -locale en_US_roman -gmt 1 -base 1009411200 } 1009670400 test clock-26.48 {parse naked day of week} { clock scan ? -format %Ow -locale en_US_roman -gmt 1 -base 1009411200 } 1009670400 # END testcases26 if {!$valid_mode} { set res {-result {0 0}} } else { set res {-returnCodes error -result "unable to convert input string: invalid day of week"} } test clock-27.1 {seconds take precedence over naked weekday} -body { list [clock scan {0 1} -format {%s %u} -gmt true -base 0] \ [clock scan {1 0} -format {%u %s} -gmt true -base 0] } {*}$res test clock-27.2 {julian day takes precedence over naked weekday} -body { list [clock scan {2440588 1} -format {%J %u} -gmt true -base 0] \ [clock scan {1 2440588} -format {%u %J} -gmt true -base 0] } {*}$res test clock-27.3 {yyyymmdd over naked weekday} -body { list [clock scan {19700101 1} -format {%Y%m%d %u} -gmt true -base 0] \ [clock scan {1 19700101} -format {%u %Y%m%d} -gmt true -base 0] } {*}$res test clock-27.4 {yyyyddd over naked weekday} -body { list [clock scan {1970001 1} -format {%Y%j %u} -gmt true -base 0] \ [clock scan {1 1970001} -format {%u %Y%j} -gmt true -base 0] } {*}$res test clock-27.5 {yymmdd over naked weekday} -body { list [clock scan {700101 1} -format {%y%m%d %u} -gmt true -base 0] \ [clock scan {1 700101} -format {%u %y%m%d} -gmt true -base 0] } {*}$res test clock-27.6 {yyddd over naked weekday} -body { list [clock scan {70001 1} -format {%y%j %u} -gmt true -base 0] \ [clock scan {1 70001} -format {%u %y%j} -gmt true -base 0] } {*}$res test clock-27.7 {mmdd over naked weekday} -body { list [clock scan {0101 1} -format {%m%d %u} -gmt true -base 0] \ [clock scan {1 0101} -format {%u %m%d} -gmt true -base 0] } {*}$res test clock-27.8 {ddd over naked weekday} -body { list [clock scan {001 1} -format {%j %u} -gmt true -base 0] \ [clock scan {1 001} -format {%u %j} -gmt true -base 0] } {*}$res test clock-27.9 {naked day of month over naked weekday} -body { list [clock scan {01 1} -format {%d %u} -gmt true -base 0] \ [clock scan {1 01} -format {%u %d} -gmt true -base 0] } {*}$res unset -nocomplain res test clock-28.1 {base date} { clock scan {} -format {} -gmt true -base 1234567890 } 1234483200 # BEGIN testcases29 |
︙ | ︙ | |||
34993 34994 34995 34996 34997 34998 34999 35000 35001 35002 35003 35004 35005 35006 35007 35008 | -format {%J %Ol:%M:%S %P} } 86399 test clock-29.1800 {time parsing} { clock scan {2440588 xi:lix:lix pm} \ -gmt true -locale en_US_roman \ -format {%J %Ol:%OM:%OS %P} } 86399 # END testcases29 test clock-30.1 {clock add years} { set t [clock scan 2000-01-01 -format %Y-%m-%d -timezone :UTC] set f [clock add $t 1 year -timezone :UTC] clock format $f -format %Y-%m-%d -timezone :UTC } {2001-01-01} test clock-30.2 {clock add years - leap day} { set t [clock scan 2000-02-29 -format %Y-%m-%d -timezone :UTC] | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 35468 35469 35470 35471 35472 35473 35474 35475 35476 35477 35478 35479 35480 35481 35482 35483 35484 35485 35486 35487 35488 35489 35490 35491 35492 35493 35494 35495 35496 35497 35498 35499 35500 35501 35502 35503 35504 35505 35506 35507 35508 35509 35510 | -format {%J %Ol:%M:%S %P} } 86399 test clock-29.1800 {time parsing} { clock scan {2440588 xi:lix:lix pm} \ -gmt true -locale en_US_roman \ -format {%J %Ol:%OM:%OS %P} } 86399 test clock-29.1811 {parsing of several localized formats} { set res {} foreach loc {en de fr} { foreach fmt {"%x %X" "%X %x"} { lappend res [clock scan \ [clock format 0 -format $fmt -locale $loc -gmt 1] \ -format $fmt -locale $loc -gmt 1] } } set res } [lrepeat 6 0] test clock-29.1812 {parsing of several localized formats} { set res {} foreach loc {en de fr} { foreach fmt {"%a %d-%m-%Y" "%a %b %x-%X" "%a, %x %X" "%b, %x %X"} { lappend res [clock scan \ [clock format 0 -format $fmt -locale $loc -gmt 1] \ -format $fmt -locale $loc -gmt 1] } } set res } [lrepeat 12 0] # END testcases29 # BEGIN testcases30 # Test [clock add] test clock-30.1 {clock add years} { set t [clock scan 2000-01-01 -format %Y-%m-%d -timezone :UTC] set f [clock add $t 1 year -timezone :UTC] clock format $f -format %Y-%m-%d -timezone :UTC } {2001-01-01} test clock-30.2 {clock add years - leap day} { set t [clock scan 2000-02-29 -format %Y-%m-%d -timezone :UTC] |
︙ | ︙ | |||
35239 35240 35241 35242 35243 35244 35245 35246 35247 35248 35249 35250 35251 35252 | set t [clock scan {2004-10-31 01:00:00 -0400} \ -format {%Y-%m-%d %H:%M:%S %z} \ -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] set f1 [clock add $t 3600 seconds -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] set x1 [clock format $f1 -format {%Y-%m-%d %H:%M:%S %z} \ -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] } {2004-10-31 01:00:00 -0500} test clock-31.1 {system locale} \ -constraints win \ -setup { namespace eval ::tcl::clock { namespace import -force ::testClock::registry } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 35741 35742 35743 35744 35745 35746 35747 35748 35749 35750 35751 35752 35753 35754 35755 35756 35757 35758 35759 35760 35761 35762 35763 35764 35765 35766 35767 35768 35769 35770 35771 35772 35773 35774 35775 35776 35777 35778 35779 35780 35781 35782 35783 35784 35785 35786 35787 35788 35789 35790 35791 35792 35793 35794 35795 35796 35797 35798 35799 35800 35801 35802 35803 35804 35805 35806 35807 35808 35809 35810 35811 35812 35813 35814 35815 35816 35817 35818 35819 35820 35821 35822 35823 35824 35825 35826 35827 35828 35829 35830 35831 35832 35833 35834 35835 35836 35837 35838 35839 35840 35841 35842 35843 | set t [clock scan {2004-10-31 01:00:00 -0400} \ -format {%Y-%m-%d %H:%M:%S %z} \ -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] set f1 [clock add $t 3600 seconds -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] set x1 [clock format $f1 -format {%Y-%m-%d %H:%M:%S %z} \ -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] } {2004-10-31 01:00:00 -0500} test clock-30.26 {clock add weekdays} { set t [clock scan {2013-11-20}] ;# Wednesday set f1 [clock add $t 3 weekdays] set x1 [clock format $f1 -format {%Y-%m-%d}] } {2013-11-25} test clock-30.27 {clock add weekdays starting on Saturday} { set t [clock scan {2013-11-23}] ;# Saturday set f1 [clock add $t 1 weekday] set x1 [clock format $f1 -format {%Y-%m-%d}] } {2013-11-25} test clock-30.28 {clock add weekdays starting on Sunday} { set t [clock scan {2013-11-24}] ;# Sunday set f1 [clock add $t 1 weekday] set x1 [clock format $f1 -format {%Y-%m-%d}] } {2013-11-25} test clock-30.29 {clock add 0 weekdays starting on a weekend} { set t [clock scan {2016-02-27}] ;# Saturday set f1 [clock add $t 0 weekdays] set x1 [clock format $f1 -format {%Y-%m-%d}] } {2016-02-27} test clock-30.30 {clock add weekdays and back} -body { set n [clock seconds] # we start on each day of the week for {set i 0} {$i < 7} {incr i} { set start [clock add $n $i days] set startu [clock format $start -format %u] # add 0 - 100 weekdays for {set j 0} {$j < 100} {incr j} { set forth [clock add $start $j weekdays] set back [clock add $forth -$j weekdays] # If $s was a weekday or $j was 0, $b must be the same day. # Otherwise, $b must be the immediately preceeding Friday set fail 0 if {$j == 0 || $startu < 6} { if {$start != $back} { set fail 1} } else { set friday [clock add $start -[expr {$startu % 5}] days] if {$friday != $back} { set fail 1 } } if {$fail} { set sdate [clock format $start -format {%Y-%m-%d}] set bdate [clock format $back -format {%Y-%m-%d}] return "$sdate + $j - $j := $bdate" } } } return "OK" } -result {OK} test clock-30.31 {regression test - add no int overflow} { list \ [list \ [clock add 0 1600000000 seconds 24856 days -gmt 1] \ [clock add 0 1600000000 seconds 815 months -gmt 1] \ [clock add 0 1600000000 seconds 69 years -gmt 1] \ [clock add 0 1600000000 seconds 596524 hours -gmt 1] \ [clock add 0 1600000000 seconds 35791395 minutes -gmt 1] \ [clock add 0 1600000000 seconds 0x7fffffff seconds -gmt 1] ] \ [list \ [clock add 1600000000 24856 days -gmt 1] \ [clock add 1600000000 815 months -gmt 1] \ [clock add 1600000000 69 years -gmt 1] \ [clock add 1600000000 596524 hours -gmt 1] \ [clock add 1600000000 35791395 minutes -gmt 1] \ [clock add 1600000000 0x7fffffff seconds -gmt 1] ] } [lrepeat 2 {3747558400 3743238400 3777452800 3747486400 3747483700 3747483647}] test clock-30.32 {regression test - add no int overflow} { list \ [list \ [clock add 3777452800 -1600000000 seconds -24856 days -gmt 1] \ [clock add 3777452800 -1600000000 seconds -815 months -gmt 1] \ [clock add 3777452800 -1600000000 seconds -69 years -gmt 1] \ [clock add 3777452800 -1600000000 seconds -596524 hours -gmt 1] \ [clock add 3777452800 -1600000000 seconds -35791395 minutes -gmt 1] \ [clock add 3777452800 -1600000000 seconds -0x7fffffff seconds -gmt 1] ] \ [list \ [clock add 2177452800 -24856 days -gmt 1] \ [clock add 2177452800 -815 months -gmt 1] \ [clock add 2177452800 -69 years -gmt 1] \ [clock add 2177452800 -596524 hours -gmt 1] \ [clock add 2177452800 -35791395 minutes -gmt 1] \ [clock add 2177452800 -0x7fffffff seconds -gmt 1] ] } [lrepeat 2 {29894400 34214400 0 29966400 29969100 29969153}] # END testcases30 test clock-31.1 {system locale} \ -constraints win \ -setup { namespace eval ::tcl::clock { namespace import -force ::testClock::registry } |
︙ | ︙ | |||
35608 35609 35610 35611 35612 35613 35614 35615 35616 | if { $t3 == $t1 } break set t1 $t3 } expr { $t2 / 1000 == $t3 } } {1} # clock scan test clock-34.1 {clock scan tests} { list [catch {clock scan} msg] $msg | > | | 36199 36200 36201 36202 36203 36204 36205 36206 36207 36208 36209 36210 36211 36212 36213 36214 36215 36216 | if { $t3 == $t1 } break set t1 $t3 } expr { $t2 / 1000 == $t3 } } {1} # clock scan set syntax "clock scan string ?-base seconds? ?-format string? ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE? ?-validate boolean?" test clock-34.1 {clock scan tests} { list [catch {clock scan} msg] $msg } [subst {1 {wrong # args: should be "$syntax"}}] test clock-34.2 {clock scan tests} {*}{ -body {clock scan "bad-string"} -returnCodes error -match glob -result {unable to convert date-time string "bad-string"*} } test clock-34.3 {clock scan tests} { |
︙ | ︙ | |||
35644 35645 35646 35647 35648 35649 35650 | } {Oct 23,1992 15:00 GMT} test clock-34.8 {clock scan tests} { set time [clock scan "Oct 23,1992 15:00" -gmt true] clock format $time -format {%b %d,%Y %H:%M GMT} -gmt true } {Oct 23,1992 15:00 GMT} test clock-34.9 {clock scan tests} { list [catch {clock scan "Jan 12" -bad arg} msg] $msg | | > > > > > > > > > | | > | | | | | | | | > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36236 36237 36238 36239 36240 36241 36242 36243 36244 36245 36246 36247 36248 36249 36250 36251 36252 36253 36254 36255 36256 36257 36258 36259 36260 36261 36262 36263 36264 36265 36266 36267 36268 36269 36270 36271 36272 36273 36274 36275 36276 36277 36278 36279 36280 36281 36282 36283 36284 36285 36286 36287 36288 36289 36290 36291 36292 36293 36294 36295 36296 36297 36298 36299 36300 36301 36302 36303 36304 36305 36306 36307 36308 36309 36310 36311 36312 36313 36314 36315 36316 36317 36318 36319 36320 36321 36322 36323 36324 36325 36326 36327 36328 36329 36330 36331 36332 36333 36334 36335 36336 36337 36338 36339 36340 36341 36342 36343 36344 36345 36346 36347 36348 36349 36350 36351 36352 36353 36354 36355 36356 36357 36358 36359 36360 36361 36362 36363 36364 36365 36366 36367 36368 36369 36370 36371 36372 36373 36374 36375 36376 36377 36378 36379 36380 36381 36382 36383 36384 36385 36386 36387 36388 36389 36390 36391 36392 36393 36394 36395 36396 36397 36398 36399 36400 36401 36402 36403 36404 36405 36406 36407 36408 36409 36410 36411 36412 36413 36414 36415 36416 36417 36418 36419 36420 36421 36422 36423 36424 36425 36426 36427 36428 36429 36430 36431 36432 36433 36434 36435 36436 36437 36438 36439 36440 36441 36442 36443 36444 36445 36446 36447 36448 36449 36450 36451 36452 36453 36454 36455 36456 | } {Oct 23,1992 15:00 GMT} test clock-34.8 {clock scan tests} { set time [clock scan "Oct 23,1992 15:00" -gmt true] clock format $time -format {%b %d,%Y %H:%M GMT} -gmt true } {Oct 23,1992 15:00 GMT} test clock-34.9 {clock scan tests} { list [catch {clock scan "Jan 12" -bad arg} msg] $msg } [subst {1 {bad option "-bad": must be -base, -format, -gmt, -locale, -timezone or -validate}}] # The following two two tests test the two year date policy test clock-34.10 {clock scan tests} { set time [clock scan "1/1/71" -gmt true] clock format $time -format {%b %d,%Y %H:%M GMT} -gmt true } {Jan 01,1971 00:00 GMT} test clock-34.11 {clock scan tests} { set time [clock scan "1/1/37" -gmt true] clock format $time -format {%b %d,%Y %H:%M GMT} -gmt true } {Jan 01,2037 00:00 GMT} test clock-34.11.1 {clock scan tests: same century switch} { set times [clock scan "1/1/37" -gmt true] } [clock scan "1/1/37" -format "%m/%d/%y" -gmt true] test clock-34.11.2 {clock scan tests: same century switch} { set times [clock scan "1/1/38" -gmt true] } [clock scan "1/1/38" -format "%m/%d/%y" -gmt true] test clock-34.11.3 {clock scan tests: same century switch} { set times [clock scan "1/1/39" -gmt true] } [clock scan "1/1/39" -format "%m/%d/%y" -gmt true] test clock-34.12 {clock scan, relative times} { set time [clock scan "Oct 23, 1992 -1 day" -gmt true] clock format $time -format {%b %d, %Y} -gmt true } "Oct 22, 1992" test clock-34.13 {clock scan, ISO 8601 base date format} { set time [clock scan "19921023" -gmt true] clock format $time -format {%b %d, %Y} -gmt true } "Oct 23, 1992" test clock-34.14 {clock scan, ISO 8601 expanded date format} { set time [clock scan "1992-10-23" -gmt true] clock format $time -format {%b %d, %Y} -gmt true } "Oct 23, 1992" test clock-34.15 {clock scan, DD-Mon-YYYY format} { set time [clock scan "23-Oct-1992" -gmt true] clock format $time -format {%b %d, %Y} -gmt true } "Oct 23, 1992" test clock-34.16 {clock scan, ISO 8601 point in time format} { set time [clock scan "19921023T235959" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.16.1a {clock scan, ISO 8601 T literal optional (YYYYMMDDhhmmss)} { set time [clock scan "19921023235959" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.16.1b {clock scan, ISO 8601 T literal optional (YYYYMMDDhhmm)} { set time [clock scan "199210232359" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:00" test clock-34.16.2 {clock scan, ISO 8601 extended date time} { set time [clock scan "1992-10-23T23:59:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.17 {clock scan, ISO 8601 point in time format} { set time [clock scan "19921023 235959" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.17.2a {clock scan, ISO 8601 extended date time (YYYY-MM-DD hh:mm:ss)} { set time [clock scan "1992-10-23 23:59:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.17.2b {clock scan, ISO 8601 extended date time (YYYY-MM-DDThh:mm:ss)} { set time [clock scan "1992-10-23T23:59:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.17.2c {clock scan, ISO 8601 extended date time (YYYY-MM-DD hh:mm)} { set time [clock scan "1992-10-23 23:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:00" test clock-34.17.2d {clock scan, ISO 8601 extended date time (YYYY-MM-DDThh:mm)} { set time [clock scan "1992-10-23T23:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:00" test clock-34.17.3 {clock scan, TZ-word boundaries - Z is not TZ here } -body { set time [clock scan "1992-10-23Z23:59:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } -returnCodes error -match glob \ -result {unable to convert date-time string*} test clock-34.17.4 {clock scan, TZ-word boundaries - Z is TZ UTC here} { set time [clock scan "1992-10-23 Z 23:59:59" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.17.5 {clock scan, ISO 8601 extended date time with UTC TZ} { set time [clock scan "1992-10-23T23:59:59Z" -timezone :America/Detroit] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 23:59:59" test clock-34.18 {clock scan, ISO 8601 point in time format} { set time [clock scan "19921023T000000" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 00:00:00" test clock-34.18.2 {clock scan, ISO 8601 extended date time} { set time [clock scan "1992-10-23T00:00:00" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 00:00:00" test clock-34.18.3 {clock scan, TZ-word boundaries - Z is not TZ here } -body { set time [clock scan "1992-10-23Z00:00:00" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } -returnCodes error -match glob \ -result {unable to convert date-time string*} test clock-34.18.4 {clock scan, TZ-word boundaries - Z is TZ UTC here} { set time [clock scan "1992-10-23 Z 00:00:00" -gmt true] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 00:00:00" test clock-34.18.5 {clock scan, ISO 8601 extended date time with UTC TZ} { set time [clock scan "1992-10-23T00:00:00Z" -timezone :America/Detroit] clock format $time -format {%b %d, %Y %H:%M:%S} -gmt true } "Oct 23, 1992 00:00:00" test clock-34.20.1 {clock scan tests (-TZ)} { set time [clock scan "31 Jan 14 23:59:59 -0100" -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Feb 01,2014 00:59:59 GMT} test clock-34.20.2 {clock scan tests (+TZ)} { set time [clock scan "31 Jan 14 23:59:59 +0100" -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 31,2014 22:59:59 GMT} test clock-34.20.3 {clock scan tests (-TZ)} { set time [clock scan "23:59:59 -0100" -base 0 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 02,1970 00:59:59 GMT} test clock-34.20.4 {clock scan tests (+TZ)} { set time [clock scan "23:59:59 +0100" -base 0 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 01,1970 22:59:59 GMT} test clock-34.20.5 {clock scan tests (TZ)} { set time [clock scan "Mon, 30 Jun 2014 23:59:59 CEST" -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jun 30,2014 21:59:59 GMT} test clock-34.20.6 {clock scan tests (TZ)} { set time [clock scan "Fri, 31 Jan 2014 23:59:59 CET" -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 31,2014 22:59:59 GMT} test clock-34.20.7 {clock scan tests (relspec, day unit not TZ)} { set time [clock scan "23:59:59 +15 day" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Feb 08,1970 23:59:59 GMT} test clock-34.20.8 {clock scan tests (relspec, day unit not TZ)} { set time [clock scan "23:59:59 -15 day" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 09,1970 23:59:59 GMT} test clock-34.20.9 {clock scan tests (merid and TZ)} { set time [clock scan "10:59 pm CET" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 24,1970 21:59:00 GMT} test clock-34.20.10 {clock scan tests (merid and TZ)} { set time [clock scan "10:59 pm +0100" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 24,1970 21:59:00 GMT} test clock-34.20.11 {clock scan tests (complex TZ)} { list [clock scan "GMT+1000" -base 100000000 -gmt 1] \ [clock scan "GMT+10" -base 100000000 -gmt 1] \ [clock scan "+1000" -base 100000000 -gmt 1] } [lrepeat 3 99964000] test clock-34.20.12 {clock scan tests (complex TZ)} { list [clock scan "GMT-1000" -base 100000000 -gmt 1] \ [clock scan "GMT-10" -base 100000000 -gmt 1] \ [clock scan "-1000" -base 100000000 -gmt 1] } [lrepeat 3 100036000] test clock-34.20.13 {clock scan tests (complex TZ)} { list [clock scan "GMT-0000" -base 100000000 -gmt 1] \ [clock scan "GMT+0000" -base 100000000 -gmt 1] \ [clock scan "GMT" -base 100000000 -gmt 1] } [lrepeat 3 100000000] test clock-34.20.14 {clock scan tests (complex TZ)} { list [clock scan "CET+1000" -base 100000000 -gmt 1] \ [clock scan "CET-1000" -base 100000000 -gmt 1] } {99960400 100032400} test clock-34.20.15 {clock scan tests (complex TZ)} { list [clock scan "CET-0000" -base 100000000 -gmt 1] \ [clock scan "CET+0000" -base 100000000 -gmt 1] \ [clock scan "CET" -base 100000000 -gmt 1] } [lrepeat 3 99996400] test clock-34.20.16 {clock scan tests (complex TZ)} { list [clock format [clock scan "00:00 GMT+1000" -base 100000000 -gmt 1] -gmt 1] \ [clock format [clock scan "00:00 GMT+10" -base 100000000 -gmt 1] -gmt 1] \ [clock format [clock scan "00:00 +1000" -base 100000000 -gmt 1] -gmt 1] \ [clock format [clock scan "00:00" -base 100000000 -timezone +1000] -gmt 1] } [lrepeat 4 "Fri Mar 02 14:00:00 GMT 1973"] test clock-34.20.17 {clock scan tests (complex TZ)} { list [clock format [clock scan "00:00 GMT+0100" -base 100000000 -gmt 1] -gmt 1] \ [clock format [clock scan "00:00 GMT+01" -base 100000000 -gmt 1] -gmt 1] \ [clock format [clock scan "00:00 GMT+1" -base 100000000 -gmt 1] -gmt 1] \ [clock format [clock scan "00:00" -base 100000000 -timezone +0100] -gmt 1] } [lrepeat 4 "Fri Mar 02 23:00:00 GMT 1973"] test clock-34.20.18 {clock scan tests (no TZ)} { list [clock scan "1000days" -base 100000000 -gmt 1] \ [clock scan "1000 days" -base 100000000 -gmt 1] \ [clock scan "+1000days" -base 100000000 -gmt 1] \ [clock scan "+1000 days" -base 100000000 -gmt 1] \ [clock scan "GMT +1000 days" -base 100000000 -gmt 1] \ [clock scan "00:00 GMT +1000 days" -base 100000000 -gmt 1] } [lrepeat 6 186364800] test clock-34.20.19 {clock scan tests (no TZ)} { list [clock scan "-1000days" -base 100000000 -gmt 1] \ [clock scan "-1000 days" -base 100000000 -gmt 1] \ [clock scan "GMT -1000days" -base 100000000 -gmt 1] \ [clock scan "00:00 GMT -1000 days" -base 100000000 -gmt 1] \ } [lrepeat 4 13564800] test clock-34.20.20 {clock scan tests (TZ, TZ + 1day)} { clock scan "00:00 GMT+1000 day" -base 100000000 -gmt 1 } 100015200 test clock-34.20.21 {clock scan tests (local date of base depends on given TZ, time apllied to different day)} { list [clock scan "23:59:59 -0100" -base 0 -timezone :CET] \ [clock scan "23:59:59 -0100" -base 0 -gmt 1] \ [clock scan "23:59:59 -0100" -base 0 -timezone -1400] \ [clock scan "23:59:59 -0100" -base 0 -timezone :Pacific/Apia] } {89999 89999 3599 3599} # CLOCK SCAN REAL TESTS # We use 5am PST, 31-12-1999 as the base for these scans because irrespective # of your local timezone it should always give us times on December 31, 1999 set 5amPST 946645200 test clock-34.19 {clock scan, number meridian} { set t1 [clock scan "5 am" -base $5amPST -gmt true] |
︙ | ︙ | |||
35776 35777 35778 35779 35780 35781 35782 35783 35784 35785 35786 35787 35788 35789 | clock format [clock scan "thursday" -base [clock scan 20000112]] \ -format {%b %d, %Y} } "Jan 13, 2000" test clock-34.40 {clock scan, next day of week} { clock format [clock scan "next thursday" -base [clock scan 20000112]] \ -format {%b %d, %Y} } "Jan 20, 2000" # weekday specification and base. test clock-34.41 {2nd monday in november} { set res {} foreach i {91 92 93 94 95 96} { set nov8th [clock scan 11/8/$i] set monday [clock scan monday -base $nov8th] | > > > > > > > > > > > > > > > > > > > > > | 36537 36538 36539 36540 36541 36542 36543 36544 36545 36546 36547 36548 36549 36550 36551 36552 36553 36554 36555 36556 36557 36558 36559 36560 36561 36562 36563 36564 36565 36566 36567 36568 36569 36570 36571 | clock format [clock scan "thursday" -base [clock scan 20000112]] \ -format {%b %d, %Y} } "Jan 13, 2000" test clock-34.40 {clock scan, next day of week} { clock format [clock scan "next thursday" -base [clock scan 20000112]] \ -format {%b %d, %Y} } "Jan 20, 2000" test clock-34.40.1 {clock scan, ordinal month after relative date} { # This will fail without the bug fix (clock.tcl), as still missing # month/julian day conversion before ordinal month increment clock format [ \ clock scan "5 years 18 months 387 days" -base 0 -gmt 1 ] -format {%a, %b %d, %Y} -gmt 1 -locale en_US_roman } "Sat, Jul 23, 1977" test clock-34.40.2 {clock scan, ordinal month after relative date} { # This will fail without the bug fix (clock.tcl), as still missing # month/julian day conversion before ordinal month increment clock format [ \ clock scan "5 years 18 months 387 days next Jan" -base 0 -gmt 1 ] -format {%a, %b %d, %Y} -gmt 1 -locale en_US_roman } "Mon, Jan 23, 1978" test clock-34.40.3 {clock scan, day of week after ordinal date} { # This will fail without the bug fix (clock.tcl), because the relative # week day should be applied after whole date conversion clock format [ \ clock scan "5 years 18 months 387 days next January Fri" -base 0 -gmt 1 ] -format {%a, %b %d, %Y} -gmt 1 -locale en_US_roman } "Fri, Jan 27, 1978" # weekday specification and base. test clock-34.41 {2nd monday in november} { set res {} foreach i {91 92 93 94 95 96} { set nov8th [clock scan 11/8/$i] set monday [clock scan monday -base $nov8th] |
︙ | ︙ | |||
35926 35927 35928 35929 35930 35931 35932 35933 35934 35935 35936 35937 35938 35939 | set time [clock scan "10:59 pm CET" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 24,1970 21:59:00 GMT} test clock-34.68 {clock scan tests (merid and TZ)} { set time [clock scan "10:59 pm +0100" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 24,1970 21:59:00 GMT} # clock seconds test clock-35.1 {clock seconds tests} { expr {[clock seconds] + 1} concat {} } {} test clock-35.2 {clock seconds tests} { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36708 36709 36710 36711 36712 36713 36714 36715 36716 36717 36718 36719 36720 36721 36722 36723 36724 36725 36726 36727 36728 36729 36730 36731 36732 36733 36734 36735 36736 36737 36738 36739 36740 36741 36742 36743 36744 36745 36746 36747 36748 36749 36750 36751 36752 36753 36754 36755 36756 36757 36758 36759 36760 36761 36762 36763 36764 36765 36766 36767 36768 36769 36770 36771 36772 36773 36774 36775 36776 36777 36778 36779 36780 36781 36782 36783 36784 36785 36786 36787 36788 36789 36790 36791 36792 36793 36794 36795 36796 36797 36798 36799 36800 36801 36802 36803 36804 36805 36806 36807 36808 36809 | set time [clock scan "10:59 pm CET" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 24,1970 21:59:00 GMT} test clock-34.68 {clock scan tests (merid and TZ)} { set time [clock scan "10:59 pm +0100" -base 2000000 -gmt true] clock format $time -format {%b %d,%Y %H:%M:%S %Z} -gmt true } {Jan 24,1970 21:59:00 GMT} test clock-34.69.1 {relative from base, date switch} { set base [clock scan "12/31/2016 23:59:59" -gmt 1] clock format [clock scan "+1 second" \ -base $base -gmt 1] -gmt 1 -format {%Y-%m-%d %H:%M:%S} } {2017-01-01 00:00:00} test clock-34.69.2 {relative time, daylight switch} { set base [clock scan "03/27/2016" -timezone CET] set res {} lappend res [clock format [clock scan "+1 hour" \ -base $base -timezone CET] -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}] lappend res [clock format [clock scan "+2 hour" \ -base $base -timezone CET] -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}] } {{2016-03-27 01:00:00 CET} {2016-03-27 03:00:00 CEST}} test clock-34.69.3 {relative time with day increment / daylight switch} { set base [clock scan "03/27/2016" -timezone CET] set res {} lappend res [clock format [clock scan "+5 day +25 hour" \ -base [expr {$base - 6*24*60*60}] -timezone CET] -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}] lappend res [clock format [clock scan "+5 day +26 hour" \ -base [expr {$base - 6*24*60*60}] -timezone CET] -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}] } {{2016-03-27 01:00:00 CET} {2016-03-27 03:00:00 CEST}} test clock-34.69.4 {relative time with month & day increment / daylight switch} { set base [clock scan "03/27/2016" -timezone CET] set res {} lappend res [clock format [clock scan "next Mar +5 day +25 hour" \ -base [expr {$base - 35*24*60*60}] -timezone CET] -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}] lappend res [clock format [clock scan "next Mar +5 day +26 hour" \ -base [expr {$base - 35*24*60*60}] -timezone CET] -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}] } {{2016-03-27 01:00:00 CET} {2016-03-27 03:00:00 CEST}} test clock-34.70.1 {check date in DST-hole: daylight switch CET -> CEST} { set res {} # forwards set base 1459033200 for {set i 0} {$i <= 3} {incr i} { set d [clock scan "+$i hour" -base $base -timezone CET] lappend res "$d = [clock format $d -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}]" } lappend res "#--" # backwards set base 1459044000 for {set i 0} {$i <= 3} {incr i} { set d [clock scan "-$i hour" -base $base -timezone CET] lappend res "$d = [clock format $d -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}]" } set res } [split [regsub -all {^\n|\n$} { 1459033200 = 2016-03-27 00:00:00 CET 1459036800 = 2016-03-27 01:00:00 CET 1459040400 = 2016-03-27 03:00:00 CEST 1459044000 = 2016-03-27 04:00:00 CEST #-- 1459044000 = 2016-03-27 04:00:00 CEST 1459040400 = 2016-03-27 03:00:00 CEST 1459036800 = 2016-03-27 01:00:00 CET 1459033200 = 2016-03-27 00:00:00 CET } {}] \n] test clock-34.70.2 {check date in DST-hole: daylight switch CEST -> CET} { set res {} # forwards set base 1477782000 for {set i 0} {$i <= 3} {incr i} { set d [clock scan "+$i hour" -base $base -timezone CET] lappend res "$d = [clock format $d -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}]" } lappend res "#--" # backwards set base 1477792800 for {set i 0} {$i <= 3} {incr i} { set d [clock scan "-$i hour" -base $base -timezone CET] lappend res "$d = [clock format $d -timezone CET -format {%Y-%m-%d %H:%M:%S %Z}]" } set res } [split [regsub -all {^\n|\n$} { 1477782000 = 2016-10-30 01:00:00 CEST 1477785600 = 2016-10-30 02:00:00 CEST 1477789200 = 2016-10-30 02:00:00 CET 1477792800 = 2016-10-30 03:00:00 CET #-- 1477792800 = 2016-10-30 03:00:00 CET 1477789200 = 2016-10-30 02:00:00 CET 1477785600 = 2016-10-30 02:00:00 CEST 1477782000 = 2016-10-30 01:00:00 CEST } {}] \n] # clock seconds test clock-35.1 {clock seconds tests} { expr {[clock seconds] + 1} concat {} } {} test clock-35.2 {clock seconds tests} { |
︙ | ︙ | |||
35957 35958 35959 35960 35961 35962 35963 | } "07.2000" test clock-36.3 {clock scan next monthname} { clock format [clock scan "next may" -base [clock scan "june 1, 2000"]] \ -format %m.%Y } "05.2001" test clock-37.1 {%s gmt testing} { | | > > | | > > > > > > > > > > > > > > > > > > | 36827 36828 36829 36830 36831 36832 36833 36834 36835 36836 36837 36838 36839 36840 36841 36842 36843 36844 36845 36846 36847 36848 36849 36850 36851 36852 36853 36854 36855 36856 36857 36858 36859 36860 36861 36862 36863 36864 36865 36866 36867 | } "07.2000" test clock-36.3 {clock scan next monthname} { clock format [clock scan "next may" -base [clock scan "june 1, 2000"]] \ -format %m.%Y } "05.2001" test clock-37.1 {%s gmt testing} { set s [clock scan "2017-05-10 09:00:00" -gmt 1] set a [clock format $s -format %s -gmt 0] set b [clock format $s -format %s -gmt 1] set c [clock scan $s -format %s -gmt 0] set d [clock scan $s -format %s -gmt 1] # %s, being the difference between local and Greenwich, does not # depend on the time zone. list [expr {$b-$a}] [expr {$d-$c}] } {0 0} test clock-37.2 {%Es gmt testing CET} { set s [clock scan "2017-01-10 09:00:00" -gmt 1] set a [clock format $s -format %Es -timezone CET] set b [clock format $s -format %Es -gmt 1] set c [clock scan $s -format %Es -timezone CET] set d [clock scan $s -format %Es -gmt 1] # %Es depend on the time zone (local seconds instead of posix seconds). list [expr {$b-$a}] [expr {$d-$c}] } {-3600 3600} test clock-37.3 {%Es gmt testing CEST} { set s [clock scan "2017-05-10 09:00:00" -gmt 1] set a [clock format $s -format %Es -timezone CET] set b [clock format $s -format %Es -gmt 1] set c [clock scan $s -format %Es -timezone CET] set d [clock scan $s -format %Es -gmt 1] # %Es depend on the time zone (local seconds instead of posix seconds). list [expr {$b-$a}] [expr {$d-$c}] } {-7200 7200} test clock-38.1 {regression - convertUTCToLocalViaC - east of Greenwich} \ -setup { if { [info exists env(TZ)] } { set oldTZ $env(TZ) } set env(TZ) CET-01:00CEST-02:00,M3.5.0/02:00,M10.5.0/03:00 |
︙ | ︙ | |||
36017 36018 36019 36020 36021 36022 36023 36024 36025 36026 36027 36028 36029 36030 | if { [info exists oldTclTZ] } { set env(TCL_TZ) $oldTclTZ unset oldTclTZ } } \ -result 1 test clock-39.1 {regression - synonym timezones} { clock format 0 -format {%H:%M:%S} -timezone :US/Eastern } {19:00:00} test clock-40.1 {regression - bad month with -timezone :localtime} \ -setup { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36907 36908 36909 36910 36911 36912 36913 36914 36915 36916 36917 36918 36919 36920 36921 36922 36923 36924 36925 36926 36927 36928 36929 36930 36931 36932 36933 36934 36935 36936 36937 36938 36939 36940 36941 36942 36943 36944 36945 36946 36947 36948 36949 36950 36951 36952 36953 36954 36955 36956 36957 36958 36959 36960 36961 36962 36963 36964 36965 36966 | if { [info exists oldTclTZ] } { set env(TCL_TZ) $oldTclTZ unset oldTclTZ } } \ -result 1 test clock-38.3sc {ensure cache of base is correct for :localtime if TZ-env changing / scan} \ -setup { if { [info exists env(TZ)] } { set oldTZ $env(TZ) } } \ -body { set res {} foreach env(TZ) {GMT-11:30 GMT-07:30 GMT-03:30 GMT} \ i {{07:30:00} {03:30:00} {23:30:00} {20:00:00}} \ { lappend res [clock scan $i -format "%H:%M:%S" -base [expr {20*60*60}] -timezone :localtime] } set res } \ -cleanup { if { [info exists oldTZ] } { set env(TZ) $oldTZ unset oldTZ } else { unset env(TZ) } } \ -result [lrepeat 4 [expr {20*60*60}]] test clock-38.3fm {ensure cache of base is correct for :localtime if TZ-env changing / format} \ -setup { if { [info exists env(TZ)] } { set oldTZ $env(TZ) } } \ -body { set res {} foreach env(TZ) {GMT-11:30 GMT-07:30 GMT-03:30 GMT} { lappend res [clock format [expr {20*60*60}] -format "%Y-%m-%dT%H:%M:%S %Z" -timezone :localtime] } set res } \ -cleanup { if { [info exists oldTZ] } { set env(TZ) $oldTZ unset oldTZ } else { unset env(TZ) } } \ -result {{1970-01-02T07:30:00 +1130} {1970-01-02T03:30:00 +0730} {1970-01-01T23:30:00 +0330} {1970-01-01T20:00:00 +0000}} test clock-39.1 {regression - synonym timezones} { clock format 0 -format {%H:%M:%S} -timezone :US/Eastern } {19:00:00} test clock-40.1 {regression - bad month with -timezone :localtime} \ -setup { |
︙ | ︙ | |||
36088 36089 36090 36091 36092 36093 36094 | set env(TZ) $oldTZ unset oldTZ } else { unset env(TZ) } } \ -result {12:34:56-0500} | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 37024 37025 37026 37027 37028 37029 37030 37031 37032 37033 37034 37035 37036 37037 37038 37039 37040 37041 37042 37043 37044 37045 37046 37047 37048 37049 37050 37051 37052 37053 37054 37055 37056 37057 37058 37059 37060 37061 37062 37063 37064 37065 37066 37067 37068 37069 37070 37071 37072 37073 37074 37075 37076 37077 37078 37079 37080 37081 37082 37083 37084 37085 37086 37087 37088 37089 37090 37091 37092 37093 37094 37095 37096 37097 37098 37099 37100 37101 37102 37103 37104 37105 37106 37107 37108 37109 37110 37111 37112 37113 37114 37115 37116 37117 37118 37119 37120 37121 37122 37123 37124 37125 37126 37127 37128 37129 37130 37131 37132 37133 37134 37135 37136 37137 37138 37139 37140 37141 37142 37143 37144 37145 37146 37147 37148 37149 37150 37151 37152 37153 37154 37155 37156 37157 37158 37159 37160 37161 37162 37163 37164 37165 37166 37167 37168 37169 37170 37171 37172 37173 37174 37175 37176 37177 37178 37179 37180 37181 37182 37183 37184 37185 37186 37187 37188 37189 37190 37191 37192 37193 37194 37195 37196 37197 37198 37199 37200 37201 37202 37203 37204 37205 37206 37207 37208 37209 37210 37211 37212 37213 37214 37215 37216 37217 37218 37219 37220 37221 37222 37223 37224 37225 37226 37227 37228 37229 37230 37231 37232 37233 37234 37235 37236 37237 37238 37239 37240 37241 37242 37243 37244 37245 37246 37247 37248 37249 37250 37251 37252 37253 37254 37255 37256 37257 37258 37259 37260 37261 37262 37263 37264 37265 37266 37267 37268 37269 37270 37271 37272 37273 37274 37275 37276 37277 37278 37279 37280 37281 37282 37283 37284 37285 37286 37287 37288 37289 37290 37291 37292 37293 37294 37295 37296 37297 37298 37299 37300 37301 37302 37303 37304 37305 37306 37307 37308 37309 37310 37311 37312 37313 37314 37315 37316 37317 37318 37319 37320 37321 37322 37323 37324 37325 37326 37327 37328 37329 37330 37331 37332 37333 37334 37335 37336 37337 37338 37339 37340 37341 37342 37343 37344 37345 37346 37347 37348 37349 37350 | set env(TZ) $oldTZ unset oldTZ } else { unset env(TZ) } } \ -result {12:34:56-0500} test clock-44.2 {regression test - time zone containing only two digits} \ -body { clock scan 1985-04-12T10:15:30+04 -format %Y-%m-%dT%H:%M:%S%Z } \ -result 482134530 test clock-45.1 {compat: scan regression on spaces (multiple spaces in format)} \ -body { list \ [clock scan "11/08/2018 0612" -format "%m/%d/%Y %H%M" -gmt 1] \ [clock scan "11/08/2018 0612" -format "%m/%d/%Y %H%M" -gmt 1] \ [clock scan "11/08/2018 0612" -format "%m/%d/%Y %H%M" -gmt 1] \ [clock scan " 11/08/2018 0612" -format " %m/%d/%Y %H%M" -gmt 1] \ [clock scan " 11/08/2018 0612" -format " %m/%d/%Y %H%M" -gmt 1] \ [clock scan " 11/08/2018 0612" -format " %m/%d/%Y %H%M" -gmt 1] \ [clock scan "11/08/2018 0612 " -format "%m/%d/%Y %H%M " -gmt 1] \ [clock scan "11/08/2018 0612 " -format "%m/%d/%Y %H%M " -gmt 1] \ [clock scan "11/08/2018 0612 " -format "%m/%d/%Y %H%M " -gmt 1] } -result [lrepeat 9 1541657520] test clock-45.2 {compat: scan regression on spaces (multiple leading/trailing spaces in input)} \ -body { set sp [string repeat " " 20] list \ [clock scan "NOV 7${sp}" -format "%b %d" -base 0 -gmt 1 -locale en] \ [clock scan "${sp}NOV 7" -format "%b %d" -base 0 -gmt 1 -locale en] \ [clock scan "${sp}NOV 7${sp}" -format "%b %d" -base 0 -gmt 1 -locale en] \ [clock scan "1970 NOV 7${sp}" -format "%Y %b %d" -gmt 1 -locale en] \ [clock scan "${sp}1970 NOV 7" -format "%Y %b %d" -gmt 1 -locale en] \ [clock scan "${sp}1970 NOV 7${sp}" -format "%Y %b %d" -gmt 1 -locale en] } -result [lrepeat 6 26784000] test clock-45.3 {compat: scan regression on spaces (shortest match)} \ -body { list \ [clock scan "11 1 120" -format "%y%m%d %H%M%S" -gmt 1] \ [clock scan "11 1 120 " -format "%y%m%d %H%M%S" -gmt 1] \ [clock scan " 11 1 120" -format "%y%m%d %H%M%S" -gmt 1] \ [clock scan "11 1 120 " -format "%y%m%d %H%M%S " -gmt 1] \ [clock scan " 11 1 120" -format " %y%m%d %H%M%S" -gmt 1] } -result [lrepeat 5 978310920] test clock-45.4 {compat: scan regression on spaces (mandatory leading/trailing spaces in format)} \ -body { list \ [catch {clock scan "11 1 120" -format "%y%m%d %H%M%S " -gmt 1} ret] $ret \ [catch {clock scan "11 1 120" -format " %y%m%d %H%M%S" -gmt 1} ret] $ret \ [catch {clock scan "11 1 120" -format " %y%m%d %H%M%S " -gmt 1} ret] $ret } -result [lrepeat 3 1 "input string does not match supplied format"] test clock-45.5 {regression test - freescan no int overflow} { # note that the relative date changes currently reset the time to 00:00, # this can be changed later (simply achievable by adding 00:00 if expected): list \ [clock scan "+24856 days" -base 1600000000 -gmt 1] \ [clock scan "+815 months" -base 1600000000 -gmt 1] \ [clock scan "+69 years" -base 1600000000 -gmt 1] \ [clock scan "+596524 hours" -base 1600000000 -gmt 1] \ [clock scan "+35791395 minutes" -base 1600000000 -gmt 1] \ [clock scan "+2147483647 seconds" -base 1600000000 -gmt 1] } {3747513600 3743193600 3777408000 3747486400 3747483700 3747483647} test clock-45.6 {regression test - freescan no int overflow} { # note that the relative date changes currently reset the time to 00:00, # this can be changed later (simply achievable by adding 00:00 if expected): list \ [clock scan "-24856 days" -base 2177452800 -gmt 1] \ [clock scan "-815 months" -base 2177452800 -gmt 1] \ [clock scan "-69 years" -base 2177452800 -gmt 1] \ [clock scan "-596524 hours" -base 2177452800 -gmt 1] \ [clock scan "-35791395 minutes" -base 2177452800 -gmt 1] \ [clock scan "-2147483647 seconds" -base 2177452800 -gmt 1] } {29894400 34214400 0 29966400 29969100 29969153} test clock-46.1 {regression test - month zero} -constraints valid_off \ -body { clock scan 2004-00-00 -format %Y-%m-%d } -result [clock scan 2003-11-30 -format %Y-%m-%d] test clock-46.2 {regression test - month zero} -constraints valid_off \ -body { clock scan 20040000 } -result [clock scan 2003-11-30 -format %Y-%m-%d] test clock-46.3 {regression test - month thirteen} -constraints valid_off \ -body { clock scan 2004-13-01 -format %Y-%m-%d } -result [clock scan 2005-01-01 -format %Y-%m-%d] test clock-46.4 {regression test - month thirteen} -constraints valid_off \ -body { clock scan 20041301 } -result [clock scan 2005-01-01 -format %Y-%m-%d] test clock-46.5 {regression test - good time} \ -body { # 12:01 apm are valid input strings... list [clock scan "12:01 am" -base 0 -gmt 1] \ [clock scan "12:01 pm" -base 0 -gmt 1] } -result {60 43260} test clock-46.6 {freescan: regression test - bad time} -constraints valid_off \ -body { # 13:00 am/pm are invalid input strings... list [clock scan "13:00 am" -base 0 -gmt 1] \ [clock scan "13:00 pm" -base 0 -gmt 1] } -result {-1 -1} proc _invalid_test {args} { global valid_mode # ensure validation works TZ independently, since the conversion # of local time to UTC may adjust date/time tokens, depending on TZ: set res {} foreach tz {:GMT :CET {} :Europe/Berlin :localtime} { foreach {v} $args { if {$valid_mode} { # globally -valid 1 lappend res [catch {clock scan $v -timezone $tz} msg] $msg } else { lappend res [catch {clock scan $v -valid 1 -timezone $tz} msg] $msg } } } set res } # test without and with relative offsets: foreach {idx relstr} {"" "" "+rel" "+ 15 month + 40 days + 30 hours + 80 minutes +9999 seconds"} { test clock-46.10$idx {freescan: validation rules: invalid time} \ -body { # 13:00 am/pm are invalid input strings... _invalid_test "13:00 am$relstr" "13:00 pm$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid time (hour)}] test clock-46.11$idx {freescan: validation rules: invalid time} \ -body { # invalid minutes in input strings... _invalid_test "23:70$relstr" "11:80 pm$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid time (minutes)}] test clock-46.12$idx {freescan: validation rules: invalid time} \ -body { # invalid seconds in input strings... _invalid_test "23:00:70$relstr" "11:00:80 pm$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid time}] test clock-46.13$idx {freescan: validation rules: invalid day} \ -body { _invalid_test "29 Feb 2017$relstr" "30 Feb 2016$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid day}] test clock-46.14$idx {freescan: validation rules: invalid day} \ -body { _invalid_test "0 Feb 2017$relstr" "00 Feb 2017$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid day}] test clock-46.15$idx {freescan: validation rules: invalid month} \ -body { _invalid_test "13/13/2017$relstr" "00/00/2017$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid month}] test clock-46.16$idx {freescan: validation rules: invalid day of week} \ -body { _invalid_test "Sat Jan 02 00:00:00 1970$relstr" "Thu Jan 04 00:00:00 1970$relstr" } -result [lrepeat 10 1 {unable to convert input string: invalid day of week}] test clock-46.17$idx {scan: validation rules: invalid year} -setup { set orgcfg [list -min-year [::tcl::unsupported::clock::configure -min-year] -max-year [::tcl::unsupported::clock::configure -max-year] \ -year-century [::tcl::unsupported::clock::configure -year-century] -century-switch [::tcl::unsupported::clock::configure -century-switch]] ::tcl::unsupported::clock::configure -min-year 2000 -max-year 2100 -year-century 2000 -century-switch 38 } -body { _invalid_test "70-01-01$relstr" "1870-01-01$relstr" "9570-01-01$relstr" } -result [lrepeat 15 1 {unable to convert input string: invalid year}] -cleanup { ::tcl::unsupported::clock::configure {*}$orgcfg unset -nocomplain orgcfg } }; # foreach rename _invalid_test {} unset -nocomplain idx relstr set dst_hole_check { {":Europe/Berlin" "2017-03-26 01:59:59" "2017-03-26 02:00:00" "2017-03-26 02:59:59" "2017-03-26 03:00:00" "2017-10-29 01:59:59" "2017-10-29 02:00:00"} {":Europe/Berlin" "2018-03-25 01:59:59" "2018-03-25 02:00:00" "2018-03-25 02:59:59" "2018-03-25 03:00:00" "2018-10-28 01:59:59" "2018-10-28 02:00:00"} {":America/New_York" "2017-03-12 01:59:59" "2017-03-12 02:00:00" "2017-03-12 02:59:59" "2017-03-12 03:00:00" "2017-11-05 01:59:59" "2017-11-05 02:00:00"} {":America/New_York" "2018-03-11 01:59:59" "2018-03-11 02:00:00" "2018-03-11 02:59:59" "2018-03-11 03:00:00" "2018-11-04 01:59:59" "2018-11-04 02:00:00"} } test clock-46.19-1 {free-scan: validation rules: invalid time (DST-hole, out of range in time-zone)} \ -body { set res {} foreach tz $dst_hole_check { set dt [lassign $tz tz]; foreach dt $dt { lappend res [set v [catch {clock scan $dt -timezone $tz -valid 1} msg]] if {$v} { lappend res $msg } }} set res } -cleanup { unset -nocomplain res v dt tz } -result [lrepeat 4 \ {*}[list 0 {*}[lrepeat 2 1 {unable to convert input string: invalid time (does not exist in this time-zone)}] 0 0 0]] test clock-46.19-2 {free-scan: validation rules regression: all scans successful, if -valid 0} \ -body { set res {} set res {} foreach tz $dst_hole_check { set dt [lassign $tz tz]; foreach dt $dt { lappend res [set v [catch {clock scan $dt -timezone $tz} msg]] }} set res } -cleanup { unset -nocomplain res v dt tz } -result [lrepeat 4 {*}[if {$valid_mode} {list 0 1 1 0 0 0} else {list 0 0 0 0 0 0}]] test clock-46.19-3 {scan: validation rules: invalid time (DST-hole, out of range in time-zone)} \ -body { set res {} foreach tz $dst_hole_check { set dt [lassign $tz tz]; foreach dt $dt { lappend res [set v [catch {clock scan $dt -timezone $tz -format "%Y-%m-%d %H:%M:%S" -valid 1} msg]] if {$v} { lappend res $msg } }} set res } -cleanup { unset -nocomplain res v dt tz } -result [lrepeat 4 \ {*}[list 0 {*}[lrepeat 2 1 {unable to convert input string: invalid time (does not exist in this time-zone)}] 0 0 0]] test clock-46.19-4 {scan: validation rules regression: all scans successful, if -valid 0} \ -body { set res {} set res {} foreach tz $dst_hole_check { set dt [lassign $tz tz]; foreach dt $dt { lappend res [set v [catch {clock scan $dt -timezone $tz -format "%Y-%m-%d %H:%M:%S"} msg]] }} set res } -cleanup { unset -nocomplain res v dt tz } -result [lrepeat 4 {*}[if {$valid_mode} {list 0 1 1 0 0 0} else {list 0 0 0 0 0 0}]] unset -nocomplain dst_hole_check proc _invalid_test {args} { global valid_mode # ensure validation works TZ independently, since the conversion # of local time to UTC may adjust date/time tokens, depending on TZ: set res {} foreach tz {:GMT :CET {} :Europe/Berlin :localtime} { foreach {v fmt} $args { if {$valid_mode} { # globally -valid 1 lappend res [catch {clock scan $v -format $fmt -timezone $tz} msg] $msg } else { lappend res [catch {clock scan $v -format $fmt -valid 1 -timezone $tz} msg] $msg } } } set res } test clock-46.20 {scan: validation rules: invalid time} \ -body { # 13:00 am/pm are invalid input strings... _invalid_test "13:00 am" "%H:%M %p" "13:00 pm" "%H:%M %p" } -result [lrepeat 10 1 {unable to convert input string: invalid time (hour)}] test clock-46.21 {scan: validation rules: invalid time} \ -body { # invalid minutes in input strings... _invalid_test "23:70" "%H:%M" "11:80 pm" "%H:%M %p" } -result [lrepeat 10 1 {unable to convert input string: invalid time (minutes)}] test clock-46.22 {scan: validation rules: invalid time} \ -body { # invalid seconds in input strings... _invalid_test "23:00:70" "%H:%M:%S" "11:00:80 pm" "%H:%M:%S %p" } -result [lrepeat 10 1 {unable to convert input string: invalid time}] test clock-46.23 {scan: validation rules: invalid day} \ -body { _invalid_test "29 Feb 2017" "%d %b %Y" "30 Feb 2016" "%d %b %Y" } -result [lrepeat 10 1 {unable to convert input string: invalid day}] test clock-46.24 {scan: validation rules: invalid day} \ -body { _invalid_test "0 Feb 2017" "%d %b %Y" "00 Feb 2017" "%d %b %Y" } -result [lrepeat 10 1 {unable to convert input string: invalid day}] test clock-46.25 {scan: validation rules: invalid month} \ -body { _invalid_test "13/13/2017" "%m/%d/%Y" "00/01/2017" "%m/%d/%Y" } -result [lrepeat 10 1 {unable to convert input string: invalid month}] test clock-46.26 {scan: validation rules: ambiguous day} \ -body { _invalid_test "1970-01-02--004" "%Y-%m-%d--%j" "70-01-02--004" "%y-%m-%d--%j" } -result [lrepeat 10 1 {unable to convert input string: ambiguous day}] test clock-46.27 {scan: validation rules: ambiguous year} \ -body { _invalid_test "19700106 00W014" "%Y%m%d %gW%V%u" "1970006 00W014" "%Y%j %gW%V%u" } -result [lrepeat 10 1 {unable to convert input string: ambiguous year}] test clock-46.28 {scan: validation rules: invalid day of week} \ -body { _invalid_test "Sat Jan 02 00:00:00 1970" "%a %b %d %H:%M:%S %Y" } -result [lrepeat 5 1 {unable to convert input string: invalid day of week}] test clock-46.29-1 {scan: validation rules: invalid day of year} \ -body { _invalid_test "000-2017" "%j-%Y" "366-2017" "%j-%Y" "000-2017" "%j-%G" "366-2017" "%j-%G" } -result [lrepeat 20 1 {unable to convert input string: invalid day of year}] test clock-46.29-2 {scan: validation rules: valid day of leap/not leap year} \ -body { list [clock format [clock scan "366-2016" -format "%j-%Y" -valid 1 -gmt 1] -format "%d-%m-%Y"] \ [clock format [clock scan "365-2017" -format "%j-%Y" -valid 1 -gmt 1] -format "%d-%m-%Y"] \ [clock format [clock scan "366-2016" -format "%j-%G" -valid 1 -gmt 1] -format "%d-%m-%Y"] \ [clock format [clock scan "365-2017" -format "%j-%G" -valid 1 -gmt 1] -format "%d-%m-%Y"] } -result {31-12-2016 31-12-2017 31-12-2016 31-12-2017} test clock-46.30 {scan: validation rules: invalid year} -setup { set orgcfg [list -min-year [::tcl::unsupported::clock::configure -min-year] -max-year [::tcl::unsupported::clock::configure -max-year] \ -year-century [::tcl::unsupported::clock::configure -year-century] -century-switch [::tcl::unsupported::clock::configure -century-switch]] ::tcl::unsupported::clock::configure -min-year 2000 -max-year 2100 -year-century 2000 -century-switch 38 } -body { _invalid_test "01-01-70" "%d-%m-%y" "01-01-1870" "%d-%m-%C%y" "01-01-1970" "%d-%m-%Y" } -result [lrepeat 15 1 {unable to convert input string: invalid year}] -cleanup { ::tcl::unsupported::clock::configure {*}$orgcfg unset -nocomplain orgcfg } test clock-46.31 {scan: validation rules: invalid iso year} -setup { set orgcfg [list -min-year [::tcl::unsupported::clock::configure -min-year] -max-year [::tcl::unsupported::clock::configure -max-year] \ -year-century [::tcl::unsupported::clock::configure -year-century] -century-switch [::tcl::unsupported::clock::configure -century-switch]] ::tcl::unsupported::clock::configure -min-year 2000 -max-year 2100 -year-century 2000 -century-switch 38 } -body { _invalid_test "01-01-70" "%d-%m-%g" "01-01-9870" "%d-%m-%C%g" "01-01-9870" "%d-%m-%G" } -result [lrepeat 15 1 {unable to convert input string: invalid iso year}] -cleanup { ::tcl::unsupported::clock::configure {*}$orgcfg unset -nocomplain orgcfg } rename _invalid_test {} test clock-47.1 {regression test - four-digit time} { clock scan 0012 } [clock scan 0012 -format %H%M] test clock-47.2 {regression test - four digit time} { clock scan 0039 } [clock scan 0039 -format %H%M] |
︙ | ︙ | |||
36950 36951 36952 36953 36954 36955 36956 | clock scan "1 DECEMBER 2000" -gmt true -format "%d %b %Y" } [clock scan "2000-12-01" -gmt true -format "%Y-%m-%d"] test clock-61.1 {overflow of a wide integer on output} {*}{ -body { clock format 0x8000000000000000 -format %s -gmt true } | | | | | | | | > | > > > > > > | 38176 38177 38178 38179 38180 38181 38182 38183 38184 38185 38186 38187 38188 38189 38190 38191 38192 38193 38194 38195 38196 38197 38198 38199 38200 38201 38202 38203 38204 38205 38206 38207 38208 38209 38210 38211 38212 | clock scan "1 DECEMBER 2000" -gmt true -format "%d %b %Y" } [clock scan "2000-12-01" -gmt true -format "%Y-%m-%d"] test clock-61.1 {overflow of a wide integer on output} {*}{ -body { clock format 0x8000000000000000 -format %s -gmt true } -result {expected integer but got "0x8000000000000000"} -returnCodes error } test clock-61.2 {overflow of a wide integer on output} {*}{ -body { clock format -0x8000000000000001 -format %s -gmt true } -result {expected integer but got "-0x8000000000000001"} -returnCodes error } test clock-61.3 {near-miss overflow of a wide integer on output, very large datetime (upper range)} { clock format 0x00F0000000000000 -format "%s %Y %EE" -gmt true } [list [expr 0x00F0000000000000] 2140702833 C.E.] test clock-61.4 {near-miss overflow of a wide integer on output, very small datetime (lower range)} { clock format -0x00F0000000000000 -format "%s %Y %EE" -gmt true } [list [expr -0x00F0000000000000] 2140654939 B.C.E.] test clock-61.5 {overflow of possible date-time (upper range)} -body { clock format 0x00F0000000000001 -gmt true } -returnCodes error -result {integer value too large to represent} test clock-61.6 {overflow of possible date-time (lower range)} -body { clock format -0x00F0000000000001 -gmt true } -returnCodes error -result {integer value too large to represent} test clock-62.1 {Bug 1902423} {*}{ -setup {::tcl::clock::ClearCaches} -body { set s 1204049747 set f1 [clock format $s -format {%Y-%m-%d %T} -locale C] set f2 [clock format $s -format {%Y-%m-%d %H:%M:%S} -locale C] |
︙ | ︙ | |||
37072 37073 37074 37075 37076 37077 37078 | expr {$res == [clock scan "01/01/1970" -locale current -format %x -gmt 1]} } -cleanup { msgcat::mclocale $current } -result {1} # cleanup | < > > > > > > > > > > > | 38305 38306 38307 38308 38309 38310 38311 38312 38313 38314 38315 38316 38317 38318 38319 38320 38321 38322 38323 38324 38325 38326 38327 38328 38329 | expr {$res == [clock scan "01/01/1970" -locale current -format %x -gmt 1]} } -cleanup { msgcat::mclocale $current } -result {1} # cleanup ::tcl::clock::ClearCaches rename test {} namespace import -force ::tcltest::* # adjust expected skipped (valid_off is an artificial constraint): if {$valid_mode && [info exists ::tcltest::skippedBecause(valid_off)]} { incr ::tcltest::numTests(Total) -$::tcltest::skippedBecause(valid_off) incr ::tcltest::numTests(Skipped) -$::tcltest::skippedBecause(valid_off) unset ::tcltest::skippedBecause(valid_off) } ::tcltest::cleanupTests namespace delete ::testClock unset valid_mode return # Local Variables: # mode: tcl # End: |
Changes to unix/Makefile.in.
︙ | ︙ | |||
296 297 298 299 300 301 302 | tclThreadTest.o tclUnixTest.o XTTEST_OBJS = xtTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \ tclThreadTest.o tclUnixTest.o tclXtNotify.o tclXtTest.o GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \ tclArithSeries.o tclAssembly.o tclAsync.o tclBasic.o tclBinary.o \ | | | | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | tclThreadTest.o tclUnixTest.o XTTEST_OBJS = xtTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \ tclThreadTest.o tclUnixTest.o tclXtNotify.o tclXtTest.o GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \ tclArithSeries.o tclAssembly.o tclAsync.o tclBasic.o tclBinary.o \ tclCkalloc.o tclClock.o tclClockFmt.o tclCmdAH.o tclCmdIL.o tclCmdMZ.o \ tclCompCmds.o tclCompCmdsGR.o tclCompCmdsSZ.o tclCompExpr.o \ tclCompile.o tclConfig.o tclDate.o tclDictObj.o tclDisassemble.o \ tclEncoding.o tclEnsemble.o \ tclEnv.o tclEvent.o tclExecute.o tclFCmd.o tclFileName.o tclGet.o \ tclHash.o tclHistory.o tclIndexObj.o tclInterp.o tclIO.o tclIOCmd.o \ tclIORChan.o tclIORTrans.o tclIOGT.o tclIOSock.o tclIOUtil.o \ tclLink.o tclListObj.o \ tclLiteral.o tclLoad.o tclMain.o tclNamesp.o tclNotify.o \ tclObj.o tclOptimize.o tclPanic.o tclParse.o tclPathObj.o tclPipe.o \ tclPkg.o tclPkgConfig.o tclPosixStr.o \ tclPreserve.o tclProc.o tclProcess.o tclRegexp.o \ tclResolve.o tclResult.o tclScan.o tclStringObj.o tclStrIdxTree.o \ tclStrToD.o tclThread.o \ tclThreadAlloc.o tclThreadJoin.o tclThreadStorage.o tclStubInit.o \ tclTimer.o tclTrace.o tclUtf.o tclUtil.o tclVar.o tclZlib.o \ tclTomMathInterface.o tclZipfs.o OO_OBJS = tclOO.o tclOOBasic.o tclOOCall.o tclOODefineCmds.o tclOOInfo.o \ tclOOMethod.o tclOOStubInit.o |
︙ | ︙ | |||
404 405 406 407 408 409 410 411 412 413 414 415 416 417 | $(GENERIC_DIR)/tclArithSeries.c \ $(GENERIC_DIR)/tclAssembly.c \ $(GENERIC_DIR)/tclAsync.c \ $(GENERIC_DIR)/tclBasic.c \ $(GENERIC_DIR)/tclBinary.c \ $(GENERIC_DIR)/tclCkalloc.c \ $(GENERIC_DIR)/tclClock.c \ $(GENERIC_DIR)/tclCmdAH.c \ $(GENERIC_DIR)/tclCmdIL.c \ $(GENERIC_DIR)/tclCmdMZ.c \ $(GENERIC_DIR)/tclCompCmds.c \ $(GENERIC_DIR)/tclCompCmdsGR.c \ $(GENERIC_DIR)/tclCompCmdsSZ.c \ $(GENERIC_DIR)/tclCompExpr.c \ | > | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | $(GENERIC_DIR)/tclArithSeries.c \ $(GENERIC_DIR)/tclAssembly.c \ $(GENERIC_DIR)/tclAsync.c \ $(GENERIC_DIR)/tclBasic.c \ $(GENERIC_DIR)/tclBinary.c \ $(GENERIC_DIR)/tclCkalloc.c \ $(GENERIC_DIR)/tclClock.c \ $(GENERIC_DIR)/tclClockFmt.c \ $(GENERIC_DIR)/tclCmdAH.c \ $(GENERIC_DIR)/tclCmdIL.c \ $(GENERIC_DIR)/tclCmdMZ.c \ $(GENERIC_DIR)/tclCompCmds.c \ $(GENERIC_DIR)/tclCompCmdsGR.c \ $(GENERIC_DIR)/tclCompCmdsSZ.c \ $(GENERIC_DIR)/tclCompExpr.c \ |
︙ | ︙ | |||
459 460 461 462 463 464 465 466 467 468 469 470 471 472 | $(GENERIC_DIR)/tclProcess.c \ $(GENERIC_DIR)/tclRegexp.c \ $(GENERIC_DIR)/tclResolve.c \ $(GENERIC_DIR)/tclResult.c \ $(GENERIC_DIR)/tclScan.c \ $(GENERIC_DIR)/tclStubInit.c \ $(GENERIC_DIR)/tclStringObj.c \ $(GENERIC_DIR)/tclStrToD.c \ $(GENERIC_DIR)/tclTest.c \ $(GENERIC_DIR)/tclTestObj.c \ $(GENERIC_DIR)/tclTestProcBodyObj.c \ $(GENERIC_DIR)/tclThread.c \ $(GENERIC_DIR)/tclThreadAlloc.c \ $(GENERIC_DIR)/tclThreadJoin.c \ | > | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | $(GENERIC_DIR)/tclProcess.c \ $(GENERIC_DIR)/tclRegexp.c \ $(GENERIC_DIR)/tclResolve.c \ $(GENERIC_DIR)/tclResult.c \ $(GENERIC_DIR)/tclScan.c \ $(GENERIC_DIR)/tclStubInit.c \ $(GENERIC_DIR)/tclStringObj.c \ $(GENERIC_DIR)/tclStrIdxTree.c \ $(GENERIC_DIR)/tclStrToD.c \ $(GENERIC_DIR)/tclTest.c \ $(GENERIC_DIR)/tclTestObj.c \ $(GENERIC_DIR)/tclTestProcBodyObj.c \ $(GENERIC_DIR)/tclThread.c \ $(GENERIC_DIR)/tclThreadAlloc.c \ $(GENERIC_DIR)/tclThreadJoin.c \ |
︙ | ︙ | |||
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 | MATHHDRS = $(GENERIC_DIR)/tclTomMath.h $(GENERIC_DIR)/tclTomMathDecls.h PARSEHDR = $(GENERIC_DIR)/tclParse.h NREHDR = $(GENERIC_DIR)/tclInt.h TRIMHDR = $(GENERIC_DIR)/tclStringTrim.h TCL_LOCATIONS = -DTCL_LIBRARY="\"${TCL_LIBRARY}\"" \ -DTCL_PACKAGE_PATH="\"${TCL_PACKAGE_PATH}\"" regcomp.o: $(REGHDRS) $(GENERIC_DIR)/regcomp.c $(GENERIC_DIR)/regc_lex.c \ $(GENERIC_DIR)/regc_color.c $(GENERIC_DIR)/regc_locale.c \ $(GENERIC_DIR)/regc_nfa.c $(GENERIC_DIR)/regc_cvec.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/regcomp.c regexec.o: $(REGHDRS) $(GENERIC_DIR)/regexec.c $(GENERIC_DIR)/rege_dfa.c | > | 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 | MATHHDRS = $(GENERIC_DIR)/tclTomMath.h $(GENERIC_DIR)/tclTomMathDecls.h PARSEHDR = $(GENERIC_DIR)/tclParse.h NREHDR = $(GENERIC_DIR)/tclInt.h TRIMHDR = $(GENERIC_DIR)/tclStringTrim.h TCL_LOCATIONS = -DTCL_LIBRARY="\"${TCL_LIBRARY}\"" \ -DTCL_PACKAGE_PATH="\"${TCL_PACKAGE_PATH}\"" TCLDATEHDR=$(GENERIC_DIR)/tclDate.h $(GENERIC_DIR)/tclStrIdxTree.h regcomp.o: $(REGHDRS) $(GENERIC_DIR)/regcomp.c $(GENERIC_DIR)/regc_lex.c \ $(GENERIC_DIR)/regc_color.c $(GENERIC_DIR)/regc_locale.c \ $(GENERIC_DIR)/regc_nfa.c $(GENERIC_DIR)/regc_cvec.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/regcomp.c regexec.o: $(REGHDRS) $(GENERIC_DIR)/regexec.c $(GENERIC_DIR)/rege_dfa.c |
︙ | ︙ | |||
1292 1293 1294 1295 1296 1297 1298 | tclBinary.o: $(GENERIC_DIR)/tclBinary.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclBinary.c tclCkalloc.o: $(GENERIC_DIR)/tclCkalloc.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCkalloc.c | | > > > | | 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 | tclBinary.o: $(GENERIC_DIR)/tclBinary.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclBinary.c tclCkalloc.o: $(GENERIC_DIR)/tclCkalloc.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCkalloc.c tclClock.o: $(GENERIC_DIR)/tclClock.c $(TCLDATEHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclClock.c tclClockFmt.o: $(GENERIC_DIR)/tclClockFmt.c $(TCLDATEHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclClockFmt.c tclCmdAH.o: $(GENERIC_DIR)/tclCmdAH.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCmdAH.c tclCmdIL.o: $(GENERIC_DIR)/tclCmdIL.c $(TCLREHDRS) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCmdIL.c tclCmdMZ.o: $(GENERIC_DIR)/tclCmdMZ.c $(TCLREHDRS) $(TRIMHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCmdMZ.c tclDate.o: $(GENERIC_DIR)/tclDate.c $(TCLDATEHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclDate.c tclCompCmds.o: $(GENERIC_DIR)/tclCompCmds.c $(COMPILEHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCompCmds.c tclCompCmdsGR.o: $(GENERIC_DIR)/tclCompCmdsGR.c $(COMPILEHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclCompCmdsGR.c |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 | $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclResult.c tclScan.o: $(GENERIC_DIR)/tclScan.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclScan.c tclStringObj.o: $(GENERIC_DIR)/tclStringObj.c $(MATHHDRS) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStringObj.c tclStrToD.o: $(GENERIC_DIR)/tclStrToD.c $(MATHHDRS) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStrToD.c tclStubInit.o: $(GENERIC_DIR)/tclStubInit.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStubInit.c | > > > | 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 | $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclResult.c tclScan.o: $(GENERIC_DIR)/tclScan.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclScan.c tclStringObj.o: $(GENERIC_DIR)/tclStringObj.c $(MATHHDRS) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStringObj.c tclStrIdxTree.o: $(GENERIC_DIR)/tclStrIdxTree.c $(GENERIC_DIR)/tclStrIdxTree.h $(MATHHDRS) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStrIdxTree.c tclStrToD.o: $(GENERIC_DIR)/tclStrToD.c $(MATHHDRS) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStrToD.c tclStubInit.o: $(GENERIC_DIR)/tclStubInit.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclStubInit.c |
︙ | ︙ |
Changes to win/Makefile.in.
︙ | ︙ | |||
285 286 287 288 289 290 291 292 293 294 295 296 297 298 | tclArithSeries.$(OBJEXT) \ tclAssembly.$(OBJEXT) \ tclAsync.$(OBJEXT) \ tclBasic.$(OBJEXT) \ tclBinary.$(OBJEXT) \ tclCkalloc.$(OBJEXT) \ tclClock.$(OBJEXT) \ tclCmdAH.$(OBJEXT) \ tclCmdIL.$(OBJEXT) \ tclCmdMZ.$(OBJEXT) \ tclCompCmds.$(OBJEXT) \ tclCompCmdsGR.$(OBJEXT) \ tclCompCmdsSZ.$(OBJEXT) \ tclCompExpr.$(OBJEXT) \ | > | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | tclArithSeries.$(OBJEXT) \ tclAssembly.$(OBJEXT) \ tclAsync.$(OBJEXT) \ tclBasic.$(OBJEXT) \ tclBinary.$(OBJEXT) \ tclCkalloc.$(OBJEXT) \ tclClock.$(OBJEXT) \ tclClockFmt.$(OBJEXT) \ tclCmdAH.$(OBJEXT) \ tclCmdIL.$(OBJEXT) \ tclCmdMZ.$(OBJEXT) \ tclCompCmds.$(OBJEXT) \ tclCompCmdsGR.$(OBJEXT) \ tclCompCmdsSZ.$(OBJEXT) \ tclCompExpr.$(OBJEXT) \ |
︙ | ︙ | |||
348 349 350 351 352 353 354 355 356 357 358 359 360 361 | tclProc.$(OBJEXT) \ tclProcess.$(OBJEXT) \ tclRegexp.$(OBJEXT) \ tclResolve.$(OBJEXT) \ tclResult.$(OBJEXT) \ tclScan.$(OBJEXT) \ tclStringObj.$(OBJEXT) \ tclStrToD.$(OBJEXT) \ tclStubInit.$(OBJEXT) \ tclThread.$(OBJEXT) \ tclThreadAlloc.$(OBJEXT) \ tclThreadJoin.$(OBJEXT) \ tclThreadStorage.$(OBJEXT) \ tclTimer.$(OBJEXT) \ | > | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | tclProc.$(OBJEXT) \ tclProcess.$(OBJEXT) \ tclRegexp.$(OBJEXT) \ tclResolve.$(OBJEXT) \ tclResult.$(OBJEXT) \ tclScan.$(OBJEXT) \ tclStringObj.$(OBJEXT) \ tclStrIdxTree.$(OBJEXT) \ tclStrToD.$(OBJEXT) \ tclStubInit.$(OBJEXT) \ tclThread.$(OBJEXT) \ tclThreadAlloc.$(OBJEXT) \ tclThreadJoin.$(OBJEXT) \ tclThreadStorage.$(OBJEXT) \ tclTimer.$(OBJEXT) \ |
︙ | ︙ |
Changes to win/makefile.vc.
︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 | $(TMP_DIR)\tclArithSeries.obj \ $(TMP_DIR)\tclAssembly.obj \ $(TMP_DIR)\tclAsync.obj \ $(TMP_DIR)\tclBasic.obj \ $(TMP_DIR)\tclBinary.obj \ $(TMP_DIR)\tclCkalloc.obj \ $(TMP_DIR)\tclClock.obj \ $(TMP_DIR)\tclCmdAH.obj \ $(TMP_DIR)\tclCmdIL.obj \ $(TMP_DIR)\tclCmdMZ.obj \ $(TMP_DIR)\tclCompCmds.obj \ $(TMP_DIR)\tclCompCmdsGR.obj \ $(TMP_DIR)\tclCompCmdsSZ.obj \ $(TMP_DIR)\tclCompExpr.obj \ | > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | $(TMP_DIR)\tclArithSeries.obj \ $(TMP_DIR)\tclAssembly.obj \ $(TMP_DIR)\tclAsync.obj \ $(TMP_DIR)\tclBasic.obj \ $(TMP_DIR)\tclBinary.obj \ $(TMP_DIR)\tclCkalloc.obj \ $(TMP_DIR)\tclClock.obj \ $(TMP_DIR)\tclClockFmt.obj \ $(TMP_DIR)\tclCmdAH.obj \ $(TMP_DIR)\tclCmdIL.obj \ $(TMP_DIR)\tclCmdMZ.obj \ $(TMP_DIR)\tclCompCmds.obj \ $(TMP_DIR)\tclCompCmdsGR.obj \ $(TMP_DIR)\tclCompCmdsSZ.obj \ $(TMP_DIR)\tclCompExpr.obj \ |
︙ | ︙ | |||
314 315 316 317 318 319 320 321 322 323 324 325 326 327 | $(TMP_DIR)\tclProc.obj \ $(TMP_DIR)\tclProcess.obj \ $(TMP_DIR)\tclRegexp.obj \ $(TMP_DIR)\tclResolve.obj \ $(TMP_DIR)\tclResult.obj \ $(TMP_DIR)\tclScan.obj \ $(TMP_DIR)\tclStringObj.obj \ $(TMP_DIR)\tclStrToD.obj \ $(TMP_DIR)\tclStubInit.obj \ $(TMP_DIR)\tclThread.obj \ $(TMP_DIR)\tclThreadAlloc.obj \ $(TMP_DIR)\tclThreadJoin.obj \ $(TMP_DIR)\tclThreadStorage.obj \ $(TMP_DIR)\tclTimer.obj \ | > | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | $(TMP_DIR)\tclProc.obj \ $(TMP_DIR)\tclProcess.obj \ $(TMP_DIR)\tclRegexp.obj \ $(TMP_DIR)\tclResolve.obj \ $(TMP_DIR)\tclResult.obj \ $(TMP_DIR)\tclScan.obj \ $(TMP_DIR)\tclStringObj.obj \ $(TMP_DIR)\tclStrIdxTree.obj \ $(TMP_DIR)\tclStrToD.obj \ $(TMP_DIR)\tclStubInit.obj \ $(TMP_DIR)\tclThread.obj \ $(TMP_DIR)\tclThreadAlloc.obj \ $(TMP_DIR)\tclThreadJoin.obj \ $(TMP_DIR)\tclThreadStorage.obj \ $(TMP_DIR)\tclTimer.obj \ |
︙ | ︙ |