summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-01-10 22:44:30 (GMT)
committersebres <sebres@users.sourceforge.net>2017-01-10 22:44:30 (GMT)
commit9ece7c7e6960bda4940f6b550d3522be072a1092 (patch)
tree4b03deccddc827f2270a58e5a84f607f7d60add3
parent5d100ec32832dcd2eb49c5e734a2664e1f240c41 (diff)
downloadtcl-9ece7c7e6960bda4940f6b550d3522be072a1092.zip
tcl-9ece7c7e6960bda4940f6b550d3522be072a1092.tar.gz
tcl-9ece7c7e6960bda4940f6b550d3522be072a1092.tar.bz2
several missing scan tokens added, test cases extended and fixed;
token "%s" used for seconds only (time zone independent), additionally "%Es" token added for local seconds (zone dependent seconds);
-rw-r--r--generic/tclClock.c16
-rw-r--r--generic/tclClockFmt.c174
-rw-r--r--generic/tclDate.h16
-rw-r--r--tests/clock.test49
4 files changed, 208 insertions, 47 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index 28a484f..a3a9332 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -87,8 +87,6 @@ static int ClockConfigureObjCmd(ClientData clientData,
static void GetYearWeekDay(TclDateFields *, int);
static void GetGregorianEraYearDay(TclDateFields *, int);
static void GetMonthDay(TclDateFields *);
-static void GetJulianDayFromEraYearWeekDay(TclDateFields *, int);
-static void GetJulianDayFromEraYearMonthDay(TclDateFields *, int);
static int WeekdayOnOrBefore(int, int);
static int ClockClicksObjCmd(
ClientData clientData, Tcl_Interp *interp,
@@ -2266,7 +2264,7 @@ GetMonthDay(
*----------------------------------------------------------------------
*/
-static void
+MODULE_SCOPE void
GetJulianDayFromEraYearWeekDay(
TclDateFields *fields, /* Date to convert */
int changeover) /* Julian Day Number of the Gregorian
@@ -2319,7 +2317,7 @@ GetJulianDayFromEraYearWeekDay(
*----------------------------------------------------------------------
*/
-static void
+MODULE_SCOPE void
GetJulianDayFromEraYearMonthDay(
TclDateFields *fields, /* Date to convert */
int changeover) /* Gregorian transition date as a Julian Day */
@@ -2415,7 +2413,7 @@ GetJulianDayFromEraYearMonthDay(
*----------------------------------------------------------------------
*/
-static void
+MODULE_SCOPE void
GetJulianDayFromEraYearDay(
TclDateFields *fields, /* Date to convert */
int changeover) /* Gregorian transition date as a Julian Day */
@@ -3169,9 +3167,11 @@ ClockScanObjCmd(
+ ( yySeconds % SECONDS_PER_DAY );
}
- if (ConvertLocalToUTC(clientData, interp, &yydate, opts.timezoneObj,
- GREGORIAN_CHANGE_DATE) != TCL_OK) {
- goto done;
+ if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY|CLF_LOCALSEC)) {
+ if (ConvertLocalToUTC(clientData, interp, &yydate, opts.timezoneObj,
+ GREGORIAN_CHANGE_DATE) != TCL_OK) {
+ goto done;
+ }
}
/* Increment UTC seconds with relative time */
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c
index 55e328c..7a6fa0b 100644
--- a/generic/tclClockFmt.c
+++ b/generic/tclClockFmt.c
@@ -50,7 +50,7 @@ _str2int(
int sign)
{
register time_t val = 0, prev = 0;
- if (sign > 0) {
+ if (sign >= 0) {
while (p < e) {
val = val * 10 + (*p++ - '0');
if (val < prev) {
@@ -80,7 +80,7 @@ _str2wideInt(
int sign)
{
register Tcl_WideInt val = 0, prev = 0;
- if (sign > 0) {
+ if (sign >= 0) {
while (p < e) {
val = val * 10 + (*p++ - '0');
if (val < prev) {
@@ -1296,13 +1296,91 @@ ClockScnToken_TimeZone_Proc(ClockFmtScnCmdArgs *opts,
return TCL_OK;
}
+static int
+ClockScnToken_StarDate_Proc(ClockFmtScnCmdArgs *opts,
+ DateInfo *info, ClockScanToken *tok)
+{
+ int minLen, maxLen;
+ register const char *p = yyInput, *end; const char *s;
+ time_t year, fractYear, fractDayDiv, fractDay;
+ static const char *stardatePref = "stardate ";
+
+ DetermineGreedySearchLen(opts, 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.era = CE;
+ 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.seconds =
+ -210866803200L
+ + ( SECONDS_PER_DAY * (Tcl_WideInt)yydate.julianDay )
+ + ( SECONDS_PER_DAY * fractDay / fractDayDiv );
+
+ return TCL_OK;
+}
+
static const char *ScnSTokenMapIndex =
- "dmbyYHMSpJjCgGVazs";
+ "dmbyYHMSpJjCgGVazUsntQ";
static ClockScanTokenMap ScnSTokenMap[] = {
/* %d %e */
{CTOKT_DIGIT, CLF_DAYOFMONTH, 0, 1, 2, TclOffset(DateInfo, date.dayOfMonth),
NULL},
- /* %m */
+ /* %m %N */
{CTOKT_DIGIT, CLF_MONTH, 0, 1, 2, TclOffset(DateInfo, date.month),
NULL},
/* %b %B %h */
@@ -1350,17 +1428,27 @@ static ClockScanTokenMap ScnSTokenMap[] = {
/* %z %Z */
{CTOKT_PARSER, CLF_OPTIONAL, 0, 0, 0, 0,
ClockScnToken_TimeZone_Proc, NULL},
+ /* %U %W */
+ {CTOKT_DIGIT, CLF_OPTIONAL, 0, 1, 2, 0, /* currently no capture, parse only token */
+ NULL},
/* %s */
- {CTOKT_DIGIT, CLF_LOCALSEC | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateInfo, date.localSeconds),
+ {CTOKT_DIGIT, CLF_POSIXSEC | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateInfo, date.seconds),
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_POSIXSEC, 0, 16, 30, 0,
+ ClockScnToken_StarDate_Proc, NULL},
};
static const char *ScnSTokenMapAliasIndex[2] = {
- "eNBhkIlPAuwZ",
- "dmbbHHHpaaaz"
+ "eNBhkIlPAuwZW",
+ "dmbbHHHpaaazU"
};
static const char *ScnETokenMapIndex =
- "Ey";
+ "Eys";
static ClockScanTokenMap ScnETokenMap[] = {
/* %EE */
{CTOKT_PARSER, 0, 0, 0, 0, TclOffset(DateInfo, date.year),
@@ -1368,6 +1456,9 @@ static ClockScanTokenMap ScnETokenMap[] = {
/* %Ey */
{CTOKT_PARSER, 0, 0, 0, 0, 0, /* currently no capture, parse only token */
ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
+ /* %Es */
+ {CTOKT_DIGIT, CLF_LOCALSEC | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateInfo, date.localSeconds),
+ NULL},
};
static const char *ScnETokenMapAliasIndex[2] = {
"",
@@ -1427,7 +1518,10 @@ EstimateTokenCount(
/* estimate token count by % char and format length */
tokcnt = 0;
while (p <= end) {
- if (*p++ == '%') tokcnt++;
+ if (*p++ == '%') {
+ tokcnt++;
+ p++;
+ }
}
p = fmt + tokcnt * 2;
if (p < end) {
@@ -1653,7 +1747,9 @@ ClockScan(
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_SPACE
+ && map->type != CTOKT_WORD
+ && map->type != CTOKT_CHAR )
) {
while (p < end && isspace(UCHAR(*p))) {
p++;
@@ -1710,27 +1806,28 @@ ClockScan(
if (size < map->minSize) {
/* missing input -> error */
if ((map->flags & CLF_OPTIONAL)) {
- yyInput = p;
continue;
}
goto not_match;
}
/* string 2 number, put number into info structure by offset */
- p = yyInput; x = p + size;
- if (!(map->flags & CLF_LOCALSEC)) {
- if (_str2int((time_t *)(((char *)info) + map->offs),
- p, x, sign) != TCL_OK) {
- goto overflow;
- }
- p = x;
- } else {
- if (_str2wideInt((Tcl_WideInt *)(((char *)info) + map->offs),
- p, x, sign) != TCL_OK) {
- goto overflow;
+ if (map->offs) {
+ p = yyInput; x = p + size;
+ if (!(map->flags & (CLF_LOCALSEC|CLF_POSIXSEC))) {
+ if (_str2int((time_t *)(((char *)info) + map->offs),
+ p, x, sign) != TCL_OK) {
+ goto overflow;
+ }
+ p = x;
+ } else {
+ if (_str2wideInt((Tcl_WideInt *)(((char *)info) + map->offs),
+ p, x, sign) != TCL_OK) {
+ goto overflow;
+ }
+ p = x;
}
- p = x;
+ flags = (flags & ~map->clearFlags) | map->flags;
}
- flags = (flags & ~map->clearFlags) | map->flags;
}
break;
case CTOKT_PARSER:
@@ -1771,7 +1868,14 @@ ClockScan(
goto not_match;
}
p = x;
- continue;
+ break;
+ case CTOKT_CHAR:
+ x = (char *)map->data;
+ if (*x != *p) {
+ /* no match -> error */
+ goto not_match;
+ }
+ p++;
break;
}
}
@@ -1802,7 +1906,7 @@ ClockScan(
*/
/* seconds token (%s) take precedence over all other tokens */
- if ((opts->flags & CLF_EXTENDED) || !(flags & CLF_LOCALSEC)) {
+ if ((opts->flags & CLF_EXTENDED) || !(flags & CLF_POSIXSEC)) {
if (flags & CLF_DATE) {
if (!(flags & CLF_JULIANDAY)) {
@@ -1876,7 +1980,7 @@ ClockScan(
}
/* if no time - reset time */
- if (!(flags & (CLF_TIME|CLF_LOCALSEC))) {
+ if (!(flags & (CLF_TIME|CLF_LOCALSEC|CLF_POSIXSEC))) {
info->flags |= CLF_ASSEMBLE_SECONDS;
yydate.localSeconds = 0;
}
@@ -1886,7 +1990,7 @@ ClockScan(
yySeconds = ToSeconds(yyHour, yyMinutes,
yySeconds, yyMeridian);
} else
- if (!(flags & CLF_LOCALSEC)) {
+ if (!(flags & (CLF_LOCALSEC|CLF_POSIXSEC))) {
info->flags |= CLF_ASSEMBLE_SECONDS;
yySeconds = yydate.localSeconds % SECONDS_PER_DAY;
}
@@ -1996,7 +2100,7 @@ ClockFmtToken_StarDate_Proc(
}
/* Put together the StarDate as "Stardate %02d%03d.%1d" */
- if (FrmResultAllocate(dateFmt, 20) != TCL_OK) { return TCL_ERROR; };
+ if (FrmResultAllocate(dateFmt, 30) != TCL_OK) { return TCL_ERROR; };
memcpy(dateFmt->output, "Stardate ", 9);
dateFmt->output += 9;
dateFmt->output = _itoaw(dateFmt->output,
@@ -2005,7 +2109,7 @@ ClockFmtToken_StarDate_Proc(
fractYear, '0', 3);
*dateFmt->output++ = '.';
dateFmt->output = _itoaw(dateFmt->output,
- dateFmt->date.localSeconds % SECONDS_PER_DAY / ( SECONDS_PER_DAY / 10 ), '0', 1);
+ dateFmt->date.seconds % SECONDS_PER_DAY / ( SECONDS_PER_DAY / 10 ), '0', 1);
return TCL_OK;
}
@@ -2249,9 +2353,9 @@ static ClockFormatTokenMap FmtSTokenMap[] = {
/* %s */
{CFMTT_WIDE, "0", 1, 0, 0, 0, TclOffset(DateFormat, date.seconds), NULL},
/* %n */
- {CFMTT_CHAR, "\n", 0, 0, 0, 0, 0, NULL},
+ {CTOKT_CHAR, "\n", 0, 0, 0, 0, 0, NULL},
/* %t */
- {CFMTT_CHAR, "\t", 0, 0, 0, 0, 0, NULL},
+ {CTOKT_CHAR, "\t", 0, 0, 0, 0, 0, NULL},
/* %Q */
{CFMTT_INT, NULL, 0, 0, 0, 0, 0,
ClockFmtToken_StarDate_Proc, NULL},
@@ -2262,7 +2366,7 @@ static const char *FmtSTokenMapAliasIndex[2] = {
};
static const char *FmtETokenMapIndex =
- "Ey";
+ "Eys";
static ClockFormatTokenMap FmtETokenMap[] = {
/* %EE */
{CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.era),
@@ -2270,6 +2374,8 @@ static ClockFormatTokenMap FmtETokenMap[] = {
/* %Ey %EC */
{CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.year),
ClockFmtToken_LocaleERAYear_Proc, NULL},
+ /* %Es */
+ {CFMTT_WIDE, "0", 1, 0, 0, 0, TclOffset(DateFormat, date.localSeconds), NULL},
};
static const char *FmtETokenMapAliasIndex[2] = {
"C",
@@ -2537,7 +2643,7 @@ ClockFormat(
}
}
break;
- case CFMTT_CHAR:
+ case CTOKT_CHAR:
if (FrmResultAllocate(dateFmt, 1) != TCL_OK) { goto error; };
*dateFmt->output++ = *map->tostr;
break;
diff --git a/generic/tclDate.h b/generic/tclDate.h
index 7c68b2a..2ce629d 100644
--- a/generic/tclDate.h
+++ b/generic/tclDate.h
@@ -33,9 +33,10 @@
#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_LOCALSEC (1 << 5)
#define CLF_CENTURY (1 << 6)
#define CLF_DAYOFMONTH (1 << 7)
#define CLF_DAYOFYEAR (1 << 8)
@@ -355,8 +356,8 @@ typedef int ClockScanTokenProc(
typedef enum _CLCKTOK_TYPE {
- CTOKT_DIGIT = 1, CTOKT_PARSER, CTOKT_SPACE, CTOKT_WORD,
- CFMTT_INT, CFMTT_WIDE, CFMTT_CHAR, CFMTT_PROC
+ CTOKT_DIGIT = 1, CTOKT_PARSER, CTOKT_SPACE, CTOKT_WORD, CTOKT_CHAR,
+ CFMTT_INT, CFMTT_WIDE, CFMTT_PROC
} CLCKTOK_TYPE;
typedef struct ClockScanTokenMap {
@@ -449,6 +450,15 @@ typedef struct ClockFmtScnStorage {
MODULE_SCOPE time_t ToSeconds(time_t Hours, time_t Minutes,
time_t 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(ClientData clientData, Tcl_Interp *,
TclDateFields *, Tcl_Obj *timezoneObj, int);
MODULE_SCOPE Tcl_Obj *
diff --git a/tests/clock.test b/tests/clock.test
index b27d2a3..4b0587a 100644
--- a/tests/clock.test
+++ b/tests/clock.test
@@ -18605,6 +18605,40 @@ test clock-6.19 {no token parsing} {
[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!
+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.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)} -body {
+ set s [clock scan "01.01.2016" -f "%d.%m.%Y" -g 1]
+ set s [set i [clock scan [clock format $s -f "%Q" -g 1] -g 1]]
+ set wrong {}
+ while {[incr i 86400] < $s + 86400*366*2} {
+ set d [clock format $i -f "%Q" -g 1]
+ set i2 [clock scan $d -f "%Q" -g 1]
+ if {$i != $i2} {
+ lappend wrong "$d -- ($i != $i2) -- [clock format $i -g 1]"
+ }
+ }
+ join $wrong \n
+} -result {} -cleanup {
+ unset -nocomplain wrong i i2 s d
+}
+
+
test clock-7.1 {Julian Day} {
clock scan 0 -format %J -gmt true
} -210866803200
@@ -36080,10 +36114,21 @@ test clock-37.1 {%s gmt testing} {
set s [clock seconds]
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.
- set c [expr {$b-$a}]
-} {0}
+ list [expr {$b-$a}] [expr {$d-$c}]
+} {0 0}
+test clock-37.2 {%Es gmt testing} {
+ set s [clock seconds]
+ 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-38.1 {regression - convertUTCToLocalViaC - east of Greenwich} \
-setup {