From 0849ec1f7645230c58daa59087a96ec24e919e22 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 13 Mar 2019 00:21:13 +0000 Subject: implemented scan of astronomical julian day (JDN/ID) with token `%Ej`, corresponds julian date of sqlite-database. In opposite to calendar julian day `%J`, it starts the day at noon (and can parse float, so contains a time fraction). **TODO** implement `clock format ... -format %Ej` and test-cases for format of this token. **TODO** implement `%EJ` token for calendar JD with time fraction. --- doc/clock.n | 18 +++++++++++++- generic/tclClock.c | 17 +++++++------ generic/tclClockFmt.c | 63 +++++++++++++++++++++++++++++++++++++++++++++- generic/tclDate.h | 2 +- tests/clock.test | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+), 10 deletions(-) diff --git a/doc/clock.n b/doc/clock.n index 0dae993..4294412 100644 --- a/doc/clock.n +++ b/doc/clock.n @@ -400,6 +400,9 @@ seconds from the epoch, that group is used to determine the date. .IP [2] If the string contains a \fB%J\fR format group, representing the Julian Day Number, that group is used to determine the date. +Note, that in case of \fB%Ej\fR format group, representing +the astronomical Julian Date (with time fraction), this group is 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; @@ -550,6 +553,19 @@ 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 fraktion (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%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). @@ -605,7 +621,7 @@ 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 Julian Day Number. +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 diff --git a/generic/tclClock.c b/generic/tclClock.c index 26d327e..7f31411 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -235,7 +235,8 @@ TclClockInit( data->yearOfCenturySwitch = ClockDefaultCenturySwitch; data->validMinYear = INT_MIN; data->validMaxYear = INT_MAX; - data->maxJulianDay = 5373484; /* corresponds 9999-12-31 23:59:59 per default */ + /* corresponds max of JDN in sqlite - 9999-12-31 23:59:59 per default */ + data->maxJDN = 5373484.499999994; data->systemTimeZone = NULL; data->systemSetupTZData = NULL; @@ -1120,17 +1121,17 @@ ClockConfigureObjCmd( break; case CLOCK_MAX_JDN: if (i < objc) { - Tcl_WideInt jd; - if (TclGetWideIntFromObj(interp, objv[i], &jd) != TCL_OK) { + double jd; + if (Tcl_GetDoubleFromObj(interp, objv[i], &jd) != TCL_OK) { return TCL_ERROR; } - dataPtr->maxJulianDay = jd; + dataPtr->maxJDN = jd; Tcl_SetObjResult(interp, objv[i]); continue; } if (i+1 >= objc) { Tcl_SetObjResult(interp, - Tcl_NewWideIntObj(dataPtr->maxJulianDay)); + Tcl_NewDoubleObj(dataPtr->maxJDN)); } break; case CLOCK_VALIDATE: @@ -3719,8 +3720,10 @@ ClockScanCommit( /* some overflow checks */ if (info->flags & CLF_JULIANDAY) { - ClockClientData *dataPtr = opts->clientData; - if (yydate.julianDay > dataPtr->maxJulianDay) { + ClockClientData *dataPtr = 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", NULL); diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index 67a2ec4..67e191a 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -1584,6 +1584,64 @@ ClockScnToken_LocaleListMatcher_Proc(ClockFmtScnCmdArgs *opts, } static int +ClockScnToken_AstroJDN_Proc(ClockFmtScnCmdArgs *opts, + DateInfo *info, ClockScanToken *tok) +{ + int minLen, maxLen; + register const char *p = yyInput, *end; const char *s; + Tcl_WideInt intJD; int fractJD = 0, fractJDDiv = 1; + + DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); + + end = yyInput + maxLen; + + /* currently positive astronomic dates only */ + if (*p == '+') { p++; }; + s = p; + while (p < end && isdigit(UCHAR(*p))) { + p++; + } + if ( _str2wideInt(&intJD, s, p, 1) != TCL_OK) { + return TCL_RETURN; + }; + yyInput = p; + if (p >= end || *p++ != '.') { /* allow pure integer astronomical JDN */ + goto done; + } + 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 = (SECONDS_PER_DAY/2) + + (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 = + -210866803200L + + ( SECONDS_PER_DAY * intJD ) + + ( fractJD ); + + return TCL_OK; +} + +static int ClockScnToken_TimeZone_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { @@ -1815,11 +1873,14 @@ static const char *ScnSTokenMapAliasIndex[2] = { }; static const char *ScnETokenMapIndex = - "Eys"; + "Ejys"; static ClockScanTokenMap ScnETokenMap[] = { /* %EE */ {CTOKT_PARSER, 0, 0, 0, 0xffff, TclOffset(DateInfo, date.year), ClockScnToken_LocaleERA_Proc, (void *)MCLIT_LOCALE_NUMERALS}, + /* %Ej */ + {CTOKT_PARSER, CLF_JULIANDAY | CLF_POSIXSEC | CLF_SIGNED, 0, 1, 0xffff, 0, + ClockScnToken_AstroJDN_Proc, NULL}, /* %Ey */ {CTOKT_PARSER, 0, 0, 0, 0xffff, 0, /* currently no capture, parse only token */ ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, diff --git a/generic/tclDate.h b/generic/tclDate.h index 2751ee5..568aef1 100644 --- a/generic/tclDate.h +++ b/generic/tclDate.h @@ -310,7 +310,7 @@ typedef struct ClockClientData { int yearOfCenturySwitch; int validMinYear; int validMaxYear; - Tcl_WideInt maxJulianDay; + double maxJDN; Tcl_Obj *systemTimeZone; Tcl_Obj *systemSetupTZData; diff --git a/tests/clock.test b/tests/clock.test index 72354c2..50b6648 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -18900,6 +18900,72 @@ test clock-7.9 {Julian Day, two values} { clock scan {2440588 2440589} -format {%J %J} -gmt true } 86400 + +test clock-7.11 {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 + # BEGIN testcases8 # Test parsing of ccyymmdd @@ -21313,6 +21379,9 @@ test clock-9.1 {seconds take precedence over ccyymmdd} { test clock-9.2 {Julian day takes precedence over ccyymmdd} { clock scan {2440588 20000101} -format {%J %Y%m%d} -gmt true } 0 +test clock-9.2.1 {Astro julian day takes precedence over date-time} { + clock scan {2440587.5 20000101 010203} -format {%Ej %Y%m%d %H%M%S} -gmt true +} 0 # Test parsing of ccyyddd -- cgit v0.12 From 1ab33bd2400c55498d6d9d4603f55ce281c1daf6 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 13 Mar 2019 00:23:57 +0000 Subject: scan: extended with token `%EJ` to scan calendar julian day with time fraction (in opposite to astronomical JD `%Ej` it starts at midnight like `%J` token). --- doc/clock.n | 22 +++++++++++++++++----- generic/tclClockFmt.c | 28 ++++++++++++++++++++-------- tests/clock.test | 30 ++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/doc/clock.n b/doc/clock.n index 4294412..d75f32d 100644 --- a/doc/clock.n +++ b/doc/clock.n @@ -398,11 +398,12 @@ preprocessed format string. In order of preference: 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 format group, representing -the Julian Day Number, that group is used to determine the date. -Note, that in case of \fB%Ej\fR format group, representing -the astronomical Julian Date (with time fraction), this group is used -to determine the date and time. +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; @@ -566,6 +567,17 @@ 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 fraktion (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). diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index 67e191a..4582190 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -1584,7 +1584,7 @@ ClockScnToken_LocaleListMatcher_Proc(ClockFmtScnCmdArgs *opts, } static int -ClockScnToken_AstroJDN_Proc(ClockFmtScnCmdArgs *opts, +ClockScnToken_JDN_Proc(ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok) { int minLen, maxLen; @@ -1605,8 +1605,14 @@ ClockScnToken_AstroJDN_Proc(ClockFmtScnCmdArgs *opts, return TCL_RETURN; }; yyInput = p; - if (p >= end || *p++ != '.') { /* allow pure integer astronomical JDN */ - goto done; + 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))) { @@ -1624,12 +1630,12 @@ done: * Note, astronomical JDN starts at noon in opposite to calendar julianday. */ - fractJD = (SECONDS_PER_DAY/2) + 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; @@ -1638,6 +1644,8 @@ done: + ( SECONDS_PER_DAY * intJD ) + ( fractJD ); + info->flags |= CLF_POSIXSEC | CLF_SIGNED; + return TCL_OK; } @@ -1873,14 +1881,17 @@ static const char *ScnSTokenMapAliasIndex[2] = { }; static const char *ScnETokenMapIndex = - "Ejys"; + "EJjys"; static ClockScanTokenMap ScnETokenMap[] = { /* %EE */ {CTOKT_PARSER, 0, 0, 0, 0xffff, TclOffset(DateInfo, date.year), ClockScnToken_LocaleERA_Proc, (void *)MCLIT_LOCALE_NUMERALS}, + /* %EJ */ + {CTOKT_PARSER, CLF_JULIANDAY, 0, 1, 0xffff, 0, /* calendar JDN starts at midnight */ + ClockScnToken_JDN_Proc, NULL}, /* %Ej */ - {CTOKT_PARSER, CLF_JULIANDAY | CLF_POSIXSEC | CLF_SIGNED, 0, 1, 0xffff, 0, - ClockScnToken_AstroJDN_Proc, NULL}, + {CTOKT_PARSER, CLF_JULIANDAY, 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}, @@ -2385,6 +2396,7 @@ ClockScan( /* * Invalidate result */ + flags |= info->flags; /* seconds token (%s) take precedence over all other tokens */ if ((opts->flags & CLF_EXTENDED) || !(flags & CLF_POSIXSEC)) { diff --git a/tests/clock.test b/tests/clock.test index 50b6648..fa21a26 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -18899,6 +18899,14 @@ test clock-7.8 {Julian Day, precedence below seconds} { test clock-7.9 {Julian Day, two values} { clock scan {2440588 2440589} -format {%J %J} -gmt true } 86400 +test clock-7.10 {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 {Astronomical JDN/JD} { @@ -21376,12 +21384,22 @@ 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 {Julian day takes precedence over ccyymmdd} { - clock scan {2440588 20000101} -format {%J %Y%m%d} -gmt true -} 0 -test clock-9.2.1 {Astro julian day takes precedence over date-time} { - clock scan {2440587.5 20000101 010203} -format {%Ej %Y%m%d %H%M%S} -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 -- cgit v0.12 From 72f8c81ad189b22d76ff2d8ca8dddd06a0a53958 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 13 Mar 2019 00:24:25 +0000 Subject: format: add support of new JDN-tokens (calendar JD `%EJ`, astronomical JD `%Ej`) with time fraction. --- generic/tclClockFmt.c | 69 +++++++++++++++++++++++++++++++++- tests/clock.test | 102 +++++++++++++++++++++++++------------------------- 2 files changed, 119 insertions(+), 52 deletions(-) diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index 4582190..d4e82ad 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -2665,6 +2665,67 @@ ClockFmtToken_WeekOfYear_Proc( return TCL_OK; } static int +ClockFmtToken_JDN_Proc( + ClockFmtScnCmdArgs *opts, + DateFormat *dateFmt, + ClockFormatToken *tok, + int *val) + { + 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, @@ -2900,11 +2961,17 @@ static const char *FmtSTokenMapAliasIndex[2] = { }; static const char *FmtETokenMapIndex = - "Eys"; + "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, TclOffset(DateFormat, date.year), ClockFmtToken_LocaleERAYear_Proc, NULL}, diff --git a/tests/clock.test b/tests/clock.test index fa21a26..9a949a9 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -15358,32 +15358,32 @@ test clock-4.96 { format time of day 23:59:59 } { -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 julian day } { - clock format 0 -format {%J} -gmt true -} {2440588} -test clock-4.97.2 { format julian day } { - clock format 43200 -format {%J} -gmt true -} {2440588} -test clock-4.97.3 { format julian day } { - clock format 86399 -format {%J} -gmt true -} {2440588} -test clock-4.97.4 { format julian day } { - clock format 86400 -format {%J} -gmt true -} {2440589} -test clock-4.97.5 { format julian day } { - clock format 129599 -format {%J} -gmt true -} {2440589} -test clock-4.97.6 { format julian day } { - clock format 129600 -format {%J} -gmt true -} {2440589} -test clock-4.97.7 { format julian day } { +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} -gmt true] \ - [clock format [incr i] -format {%J} -gmt true] \ - [clock format [incr i] -format {%J} -gmt true] -} {2458507 2458507 2458507} -test clock-4.97.8 { format julian day } { + [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 @@ -15391,26 +15391,26 @@ test clock-4.97.8 { format julian day } { 86400 86401 108000 129600 172800 } { lappend res $i [clock format [expr -210866803200 - $i] \ - -format {%EE %Y-%m-%d %T -- %J} -gmt true] + -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} \ - -129600 {B.C.E. 4713-01-02 12:00:00 -- 0000001} \ - -86400 {B.C.E. 4713-01-02 00:00:00 -- 0000001} \ - -43200 {B.C.E. 4713-01-01 12:00:00 -- 0000000} \ - -1 {B.C.E. 4713-01-01 00:00:01 -- 0000000} \ - 0 {B.C.E. 4713-01-01 00:00:00 -- 0000000} \ - 1 {B.C.E. 4714-12-31 23:59:59 -- -000001} \ - 21600 {B.C.E. 4714-12-31 18:00:00 -- -000001} \ - 43199 {B.C.E. 4714-12-31 12:00:01 -- -000001} \ - 43200 {B.C.E. 4714-12-31 12:00:00 -- -000001} \ - 86399 {B.C.E. 4714-12-31 00:00:01 -- -000001} \ - 86400 {B.C.E. 4714-12-31 00:00:00 -- -000001} \ - 86401 {B.C.E. 4714-12-30 23:59:59 -- -000002} \ - 108000 {B.C.E. 4714-12-30 18:00:00 -- -000002} \ - 129600 {B.C.E. 4714-12-30 12:00:00 -- -000002} \ - 172800 {B.C.E. 4714-12-30 00:00:00 -- -000002} \ + -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 {} @@ -15420,19 +15420,19 @@ test clock-4.97.9 { format JDN/JD (calendar and astronomical) } { 43199 43200 43201 86400 } { lappend res $i [clock format [expr 653133196800 + $i] \ - -format {%Y-%m-%d %T -- %J} -gmt true] + -format {%Y-%m-%d %T -- %J %EJ %Ej} -gmt true] } set res } [list \ - -86400 {22666-12-19 00:00:00 -- 9999999} \ - -43200 {22666-12-19 12:00:00 -- 9999999} \ - -1 {22666-12-19 23:59:59 -- 9999999} \ - 0 {22666-12-20 00:00:00 -- 10000000} \ - 1 {22666-12-20 00:00:01 -- 10000000} \ - 43199 {22666-12-20 11:59:59 -- 10000000} \ - 43200 {22666-12-20 12:00:00 -- 10000000} \ - 43201 {22666-12-20 12:00:01 -- 10000000} \ - 86400 {22666-12-21 00:00:00 -- 10000001} \ + -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 -- cgit v0.12 From e9541d428e336896dbf1d2a0d128e81808fbc40e Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 13 Mar 2019 00:24:44 +0000 Subject: scan: all JDN/JD are signed, so allow parse negative Julian days --- generic/tclClockFmt.c | 12 ++++++------ tests/clock.test | 11 +++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index d4e82ad..8e14a7d 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -1596,12 +1596,12 @@ ClockScnToken_JDN_Proc(ClockFmtScnCmdArgs *opts, end = yyInput + maxLen; /* currently positive astronomic dates only */ - if (*p == '+') { p++; }; + if (*p == '+' || *p == '-') { p++; }; s = p; while (p < end && isdigit(UCHAR(*p))) { p++; } - if ( _str2wideInt(&intJD, s, p, 1) != TCL_OK) { + if ( _str2wideInt(&intJD, s, p, (*yyInput != '-' ? 1 : -1)) != TCL_OK) { return TCL_RETURN; }; yyInput = p; @@ -1644,7 +1644,7 @@ done: + ( SECONDS_PER_DAY * intJD ) + ( fractJD ); - info->flags |= CLF_POSIXSEC | CLF_SIGNED; + info->flags |= CLF_POSIXSEC; return TCL_OK; } @@ -1838,7 +1838,7 @@ static ClockScanTokenMap ScnSTokenMap[] = { {CTOKT_PARSER, 0, 0, 0, 0xffff, 0, ClockScnToken_amPmInd_Proc, NULL}, /* %J */ - {CTOKT_WIDE, CLF_JULIANDAY, 0, 1, 0xffff, TclOffset(DateInfo, date.julianDay), + {CTOKT_WIDE, CLF_JULIANDAY | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateInfo, date.julianDay), NULL}, /* %j */ {CTOKT_INT, CLF_DAYOFYEAR, 0, 1, 3, TclOffset(DateInfo, date.dayOfYear), @@ -1887,10 +1887,10 @@ static ClockScanTokenMap ScnETokenMap[] = { {CTOKT_PARSER, 0, 0, 0, 0xffff, TclOffset(DateInfo, date.year), ClockScnToken_LocaleERA_Proc, (void *)MCLIT_LOCALE_NUMERALS}, /* %EJ */ - {CTOKT_PARSER, CLF_JULIANDAY, 0, 1, 0xffff, 0, /* calendar JDN starts at midnight */ + {CTOKT_PARSER, CLF_JULIANDAY | CLF_SIGNED, 0, 1, 0xffff, 0, /* calendar JDN starts at midnight */ ClockScnToken_JDN_Proc, NULL}, /* %Ej */ - {CTOKT_PARSER, CLF_JULIANDAY, 0, 1, 0xffff, (SECONDS_PER_DAY/2), /* astro JDN starts at noon */ + {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 */ diff --git a/tests/clock.test b/tests/clock.test index 9a949a9..8a0ed13 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -18974,6 +18974,17 @@ 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 -- cgit v0.12