Hello.
I would like to share my `datediff.sh' script.
The script takes two dates and calculates time intervals between them in various units of time.
The fun is that it uses bash arithmetics and `bc' to perform
calculations. If available, `BSD/GNU date' programme is warped to
interpret date inputs, however the script works without package `date'
if input is ``ISO-8601 format''.
It took me a lot of hard work in the last two or so years to get the calculation to work correctly, but things started working better when I
read Dershowitz and Reingold paper/book of Calendrical Calculations.
I would like to highlight datediff.sh can calculate a compound time
interval, for example, the interval between some two dates may be `10
years + 2 months + 1 week + 10 hours', or you get single unit intervals,
for example, `10.18 years' or `239.59 months' and so forth.
The compound time range code was pretty hard to write, specially what Hroptatyr calls `Refinement Rules'. But also, I was able to implement
support for time offset and envar $TZ into the calculation. It can even generate the Unix time stamp and day of the week (RFC-5322) of the dates independently with shell arithmetics!
I hope this script may be useful as it works with bash 2.05b+ versions.
The script is really complex but I reckon I have finished developing it
(i.e. I don't reckon there is anything else I need it do). It is well
tested with millions of dates and the core code seems quite stable, IMHO.
If someone can find any bugs or shed advice for improvement, I would
hear it.
The ``datediff.sh'' script is published in GitHub at: github [dot] com/mountaineerbr/scripts[ snip 1000+ lines of code ]
Below is a copy (hopefully formatting is preserved!).
###
###
Cheers!
JSN
What does the script that AT&T date or GNU date doesn't support in this (time-intervals) respect?
Since I notice some mention of "easter"; which definitions of "easter"Oh, I just got that Easter script from Dershowitz and Reingold book and
does it support? (I presume you know that there's not a single one.)
Does your script support moon phases? (Something I recently looked for,
so I am curious.)
I don't get it. What's the point in using bash arithmetics and bc (or
only sometimes)? I mean it wouldn't occur to me that multi-precision arithmetic would be necessary for that. For floating point arithmetic,
OTOH, I'd use ksh (instead of bash) and generally omit bc.
Looks to me like the mentioned date programs or a simple GNU awk date implementation supports that already.
I used inherent TZ support already with dates used in scripts that use$TZ is mentioned because the script is aware of it even if no date package
GNU awk dates and in AT&T or ksh dates. Why do you mention TZ support
as something special here?
You could have avoided unnecessary bash specifics to make your script
run on other prominent shells (ksh, zsh, or even sh) as well. (Some
changes are trivial, like local in functions.)
I just skimmed through it and saw some strange constructs and comments;
e.g. "YYY-..." in a comment named ISO-date, or a $((++RET)) return value
with undefined and otherwise unused RET variable. (I know it works but implementing side effects on undefined global scoped variables that
will change the return/exit code of a function cal every time, and will
at times overflow the allowed return value range is not something that
looks trustworthy to me.)
There's a couple places where you use obsolete
constructs, mix legacy (-eq) and new syntax, or use unnecessary complex expressions (e.g. (( (yearA||yearA==0) ... )) in arithmetic context).
Must have been a pain to write and debug that large piece of code. So
please don't take my hints and comments too serious. I'm just critical
and skeptical if I see such code. And you asked for it.
On 11/10/22 21:46, Janis Papanagnou wrote:
Since I notice some mention of "easter"; which definitions of "easter"Oh, I just got that Easter script from Dershowitz and Reingold book and
does it support? (I presume you know that there's not a single one.)
they seem to agree with you about the style of it:
[[ Finally, the computer world is plagued with unintelligible code that seems to work
by magic. Consider the following Unix script for calculating the date of Easter:
Dc is used only in this function and really I haven't changed it. Also, I am not
sure what you mean by Easter definition?
Western Easter is generally on a different date than Eastern Orthodox Easter. For example the ncal man page mentions
-e Display date of easter (for western churches).
[...]
-o Display date of orthodox easter (Greek and Russian Orthodox Churches).
On 11/10/22 21:46, Janis Papanagnou wrote:
What does the script that AT&T date or GNU date doesn't support in this
(time-intervals) respect?
I am not sure that `awk' or `date' can be used to calculate time intervals other than in days, hours, minutes and seconds. So how can you calculate these time differences with awk? You could get to an _approximate_ result very easily but it gets more difficult as we need to compensate for a lot
of things, for example not all months have 4 weeks.
Since I notice some mention of "easter"; which definitions of "easter"Oh, I just got that Easter script from Dershowitz and Reingold book and
does it support? (I presume you know that there's not a single one.)
they seem to agree with you about the style of it:
[[ Finally, the computer world is plagued with unintelligible code that
seems to work
by magic. Consider the following Unix script for calculating the date of Easter:
echo $* ’[ddsf[lfp[too early
]Pq]s@1583>@ ddd19%1+sg100/1+d3*4/12-sx8*5+25/5-sz5*4/lx-10-sdlg11*20+lz+lx-30% d[30+]s@0>@d[[1+]s@lg11<@]s@25=@d[1+]s@24=@se44le-d[30+]s@21>@dld+7%-7+ [March ]smd[31-[April ]sm]s@31<@psnlmPpsn1z>p]splpx’ | dc
We want to provide transparent algorithms to replace the gobbledegook
that is so
common. ]]
Dc is used only in this function and really I haven't changed it.
Also, I am not sure what you mean by Easter definition?
Does your script support moon phases? (Something I recently looked for,
so I am curious.)
The script does not support moon phases. I just checked Dershowitz and Reingold
book and there is even some Lisp code examples. It seems that to find
out when
new moon is, you need first calculate moon and sun latitudes, and at least Venus and Jupiter influence seem worth correcting for... So that is rather
a complex astronomical calculation!
I don't get it. What's the point in using bash arithmetics and bc (or
only sometimes)? I mean it wouldn't occur to me that multi-precision
arithmetic would be necessary for that. For floating point arithmetic,
OTOH, I'd use ksh (instead of bash) and generally omit bc.
I use bc because you may want to know how many weeks are 10 days? Then
it needs to have decimal precision. I would use Ksh preferably, or Zsh, however I had to choose one shell and it was bash because I reckon it
is installed in most systems.
Looks to me like the mentioned date programs or a simple GNU awk date
implementation supports that already.
I have written some weird scripts that only later did I found out had
their functions already implemented in very well established C programmes.
If that is the case, I would like to know how to do it, because all
datediff shell scripts I have seem can only calculate up to the time unit
of days with precision. But I may be wrong.
I used inherent TZ support already with dates used in scripts that use$TZ is mentioned because the script is aware of it even if no date package
GNU awk dates and in AT&T or ksh dates. Why do you mention TZ support
as something special here?
is available, thus, it subtracts $TZ from both input dates. $TZ mostly influences how the processed dates are printed and it is used to find
the UTC time. All calculations are made using UTC times internally.
You could have avoided unnecessary bash specifics to make your script
run on other prominent shells (ksh, zsh, or even sh) as well. (Some
changes are trivial, like local in functions.)
I decided to use all the features of bash to make the script run as fast
as it possibly can. Using test [ instead of [[ makes the script runs
slower, using awk or sed for trivial things are also unnecessary.
I just skimmed through it and saw some strange constructs and comments;
e.g. "YYY-..." in a comment named ISO-date, or a $((++RET)) return value
with undefined and otherwise unused RET variable. (I know it works but
implementing side effects on undefined global scoped variables that
will change the return/exit code of a function cal every time, and will
at times overflow the allowed return value range is not something that
looks trustworthy to me.)
That is a good point, that variable use is a little weird. I thought to
exit
with the code of the same number of failed tests (that is used only in an isolated function to check if some year is leap or not, so if input is
three years and two of them are not leap years, then the function exits
with
2. I thought this may be more useful than exiting with only 1, and yes, it will overflow with more than 255 increments and may exit with 0.
So this logic is problematic because it is not explained in the help page
and is unstable. In this sense, I will change it to exit with 1 if any
input years is not leap.
There's a couple places where you use obsolete
constructs, mix legacy (-eq) and new syntax, or use unnecessary complex
expressions (e.g. (( (yearA||yearA==0) ... )) in arithmetic context).
These are great observations. I would elaborate on why I used legacy syntax [[-eq]] but I see your point and will change those instances with ((==)) tests.
Now I only disagree on unnecessary complex testing you pointed out!
In the expression "(( (yearA||yearA==0) ))" I do two tests, the first one will catch in case input is something not numerical, thus is $yearA is
some weird string, it will fail this test, but there is still the case when year may be 0000 and thus we also check if that is the case.
I am very interested in simplifying calculations, if you could point out
the unnecessary complexity, I will take a look on that.
Must have been a pain to write and debug that large piece of code. So
please don't take my hints and comments too serious. I'm just critical
and skeptical if I see such code. And you asked for it.
It was a pain to debug it, specially because I am no developer and not very good with mathematics. But I cannot see how the code could be less robust.
Hope the code can be minimally verified here so I can get more confident. However, Hroptatyr, who is the developer of C programme `datediff', didn't say anything bad about my script, in fact he starred it, so IDK.
On 11.11.2022 17:36, Janis Papanagnou wrote:
On 11.11.2022 04:34, castAway wrote:
On 11/10/22 21:46, Janis Papanagnou wrote:
What does the script that AT&T date or GNU date doesn't support in this >>>> (time-intervals) respect?
I am not sure that `awk' or `date' can be used to calculate time intervals >>> other than in days, hours, minutes and seconds. So how can you calculate >>> these time differences with awk? You could get to an _approximate_ result >>> very easily but it gets more difficult as we need to compensate for a lot >>> of things, for example not all months have 4 weeks.
[...]
On re-reading it occurred to me that you were probably not focusing
on the sub-second issue but on support for dates before, say, 1900 or
after 2100, or so. - Yes, Unix tools have different (often restricted) support for dates outside the Unix Epoch. - So, yes, depending on the
used tool you'd have to transform the external form to an internal representation that fits this accuracy.
On 11.11.2022 04:34, castAway wrote:
On 11/10/22 21:46, Janis Papanagnou wrote:
What does the script that AT&T date or GNU date doesn't support in this
(time-intervals) respect?
I am not sure that `awk' or `date' can be used to calculate time intervals >> other than in days, hours, minutes and seconds. So how can you calculate
these time differences with awk? You could get to an _approximate_ result
very easily but it gets more difficult as we need to compensate for a lot
of things, for example not all months have 4 weeks.
[...]
If I'd implement time and date calculations I'd separate the tasks in
three major components; a) transform various external representations
into an internal format, b) do some date and time calculations based
on a tool-chest of basic functions, c) provide output for any desired external representation. - I understood that in step b you're doing a difference on dates? - So I'd take the time/date-features that GNU
supports or the native features of tools that already support dates
(date, ksh, etc.), do the transformation, calculation, and output.
(But frankly, I may have misunderstood what your tool actually does.)
The point was not so much that you took some cryptic code and just
trusted it, it was that if you want to verify that code - in case
of errors or to anticipate any potential errors - you should very
well understand it or be enabled to do so. Neither the cryptic dc
code nor the absence of a comment or [specific] reference supports
that. In other words, the more unclear the construct is the more
inline documentation for support should be present. (Or other more
obvious and comprehensible solutions used.)
I was recently looking at some code and there's probably similar
cryptic formulas existing. I used an adapted _quantized_ version
(only 8 phases, and a plain integer calculation) for a web page:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
{
var diy = day_in_year (now);
var goldn = (now.getFullYear() % 19) + 1;
var epact = (11 * goldn + 18) % 30;
if ((epact == 25 && goldn > 11) || epact == 24)
epact++;
return Math.floor (((((diy + epact) * 6) + 11) % 177) / 22) % 8;
}
You could have avoided unnecessary bash specifics to make your script
run on other prominent shells (ksh, zsh, or even sh) as well. (Some
changes are trivial, like local in functions.)
As said, I just skimmed through the code because it appears to me too overloaded, and I don't want to spend more time on that. The sad fact
is that when I start I go into every even non-functional detail, say,
code like
TIME_ISO8601_FMT='%Y-%m-%dT%H:%M:%S%z'
...
INPUT_FMT="${TIME_ISO8601_FMT:0:17}" #%Y-%m-%dT%H:%M:%S
where you use a non-standard variable expansion unnecessarily and then commenting the actual value.
Or that you seem to have a lot duplicate code (e.g. there's a leap-year function but an explicit leap-year calculation can be found on several
other places as well.
(I better stop now.)
Based
on one's own expertise folks have to judge themselves whether it fits
them.
Janis
On 11/11/22 13:36, Janis Papanagnou wrote:
I was recently looking at some code and there's probably similar
cryptic formulas existing. I used an adapted _quantized_ version
(only 8 phases, and a plain integer calculation) for a web page:
[moon phase]
You convinced me that it may be possible to have a moon phase function
that is reasonable to implement within a certain error for the [near]
present time... I *shall* look into this code further and may implement
it at some point! I am not sure about ``a FP representation of it'' yet,
but I will post any follow-ups when I am able to dig into this study..
You could have avoided unnecessary bash specifics to make your script
run on other prominent shells (ksh, zsh, or even sh) as well. (Some
changes are trivial, like local in functions.)
Now that the calculation works, it should not be too hard to implement it
in sh. Maybe if I have got some spare time in the future, that would be
a godo exercise to widen script applicability to other shells, however,
the scripts relies on extended globbing to check and test input...
[...]
TIME_ISO8601_FMT='%Y-%m-%dT%H:%M:%S%z'
...
INPUT_FMT="${TIME_ISO8601_FMT:0:17}" #%Y-%m-%dT%H:%M:%S
where you use a non-standard variable expansion unnecessarily and then
commenting the actual value.
Or that you seem to have a lot duplicate code (e.g. there's a leap-year
function but an explicit leap-year calculation can be found on several
other places as well.
(I better stop now.)
Your tidbits on style are quite informative already and I appreciate them. For this specific example, I reckon it could be written standardly as
INPUT_FMT="${TIME_ISO8601_FMT%??}"
The reason why one can find lots of seemingly duplicate code is either the code is subtly different and would become more difficult to understand if
these differences were to be set on run time, or simply because of speed constraints. Millions of dates were tested on my intel i7 and amd
processors
and each testing run could take a couple of days..
So the fewest
functions were declared. The leap year checking code is so trivial and easy to check that is not worth bothering defining a function to it.
Function nesting makes the script run slower.
The organisation point is very important, however the script code is not
a mess as it is, either.
On 11.11.2022 20:15, castAway wrote:
On 11/11/22 13:36, Janis Papanagnou wrote:
I was recently looking at some code and there's probably similar
cryptic formulas existing. I used an adapted _quantized_ version
(only 8 phases, and a plain integer calculation) for a web page:
[moon phase]
You convinced me that it may be possible to have a moon phase function
that is reasonable to implement within a certain error for the [near]
present time... I *shall* look into this code further and may implement
it at some point! I am not sure about ``a FP representation of it'' yet,
but I will post any follow-ups when I am able to dig into this study..
(Please note that it was *not* a "Request for Feature". Feel free
to ignore it. There's so many things a time/date library could or
should support.)
You could have avoided unnecessary bash specifics to make your script >>>>> run on other prominent shells (ksh, zsh, or even sh) as well. (Some
changes are trivial, like local in functions.)
Now that the calculation works, it should not be too hard to implement it
in sh. Maybe if I have got some spare time in the future, that would be
a godo exercise to widen script applicability to other shells, however,
the scripts relies on extended globbing to check and test input...
Ksh supports that (ksh globbing, "extended" globbing) as standard
(i.e. without explicitly activating it) and Zsh as well
[...]
TIME_ISO8601_FMT='%Y-%m-%dT%H:%M:%S%z'
...
INPUT_FMT="${TIME_ISO8601_FMT:0:17}" #%Y-%m-%dT%H:%M:%S
where you use a non-standard variable expansion unnecessarily and then
commenting the actual value.
Or that you seem to have a lot duplicate code (e.g. there's a leap-year
function but an explicit leap-year calculation can be found on several
other places as well.
(I better stop now.)
Your tidbits on style are quite informative already and I appreciate them. >> For this specific example, I reckon it could be written standardly as
INPUT_FMT="${TIME_ISO8601_FMT%??}"
My point was rather to omit an unnecessary operation (both substring expansion or suffix expansion) by, e.g.,
INPUT_FMT="%Y-%m-%dT%H:%M:%S"
TIME_ISO8601_FMT="${INPUT_FMT}%z"
but preferences vary. Also "${TIME_ISO8601_FMT%??}" is misleading for
several reasons (there is not "one format") and might be interpreted,
e.g., as removing the last seconds-formatter; you'd need to inspect
more context. (As said, a matter of preference, style, or experience
what is more or less robust, more or less portable, etc.)
The reason why one can find lots of seemingly duplicate code is either the >> code is subtly different and would become more difficult to understand if
if (( !(year % 4) && ( year % 100 || !(year % 400) ) )) # your leap fun
if (( month == 2 && !(year % 4) && ( year % 100 || !(year % 400) ) ))
if (( month <= 2 && !(year % 4) && ( year % 100 || !(year % 400) ) ))
(( !(y_test % 4) && (y_test % 100 || !(y_test % 400) ) )) && ((++leapcount)) if (( (month_test == 2) && !(yearB % 4) && (yearB % 100 || !(yearB %
400) ) ))
if (( (month_test == 2) && !(yearA % 4) && (yearA % 100 || !(yearA %
400) ) ))
vs.
if (( !(year % 4) && ( year % 100 || !(year % 400) ) )) # your leap fun
if (( month == 2 )) && is_leapyear "${year}"
if (( month <= 2 )) && is_leapyear "${year}"
is_leapyear "${y_test}" && ((++leapcount))
if (( month_test == 2 )) && is_leapyear "${year_B}"
if (( month_test == 2 )) && is_leapyear "${year_A}"
Maintainability aside; are you really saying the code using a function
call is "more difficult to understand"?
And where is the "subtle difference"? - And why is any subtle difference easier to detect in unstructured copy/past code? - I'll bite!
these differences were to be set on run time, or simply because of speed
constraints. Millions of dates were tested on my intel i7 and amd
processors
and each testing run could take a couple of days..
The leapyear test code runs on my old legacy system for 1 million tests in
20 seconds (bash, expanded expr)
38 seconds (bash, function call)
3.3 seconds (ksh, expanded expr)
8.0 seconds (ksh, function call)
3 seconds (zsh, expanded expr)
12 seconds (zsh, function call)
So the fewest
functions were declared. The leap year checking code is so trivial and easy >> to check that is not worth bothering defining a function to it.
Function nesting makes the script run slower.
If you have a speed issue, again, I suggest to switch shells for a try.
Bash is slow (see above).
But using a slow shell and then sacrificing maintainability because of
that is even more an issue. YMMV.
In the concrete cases I'd certainly prefer
if is_leapyear "${year}" && optional_other_code
then ...
in the places where you have a duplicated leap-year test expression.
This quality of coding is (at least for me) a criterion to use or ignore
any piece of code. (Or rewrite it.)
The organisation point is very important, however the script code is not
a mess as it is, either.
No? - If you say so then I take your word for granted. ;-) Have fun!
Janis
On 11/11/2022 23:27, Janis Papanagnou wrote:
On 11.11.2022 20:15, castAway wrote:
Alternative(ish) to this script; have you looked at dateutils? http://www.fresse.org/dateutils/
Looks to me like the mentioned date programs or a simple GNU awk date implementation supports that already.
I was recently looking at some code and there's probably similar
cryptic formulas existing. I used an adapted _quantized_ version
(only 8 phases, and a plain integer calculation) for a web page:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
{
var diy = day_in_year (now);
var goldn = (now.getFullYear() % 19) + 1;
var epact = (11 * goldn + 18) % 30;
if ((epact == 25 && goldn > 11) || epact == 24)
epact++;
return Math.floor (((((diy + epact) * 6) + 11) % 177) / 22) % 8;
}
Not too complex (but also without comments, so I cannot tell,
to be honest, what's actually going on). I suppose it could be
simplified for an accurate FP representation. (I think to have
seen such a function also on the Net, maybe even on Wikipedia.)
Alternative(ish) to this script; have you looked at dateutils? http://www.fresse.org/dateutils/
On 11/11/22 13:36, Janis Papanagnou wrote:
I was recently looking at some code and there's probably similar
cryptic formulas existing. I used an adapted _quantized_ version
(only 8 phases, and a plain integer calculation) for a web page:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
{
var diy = day_in_year (now);
var goldn = (now.getFullYear() % 19) + 1;
var epact = (11 * goldn + 18) % 30;
if ((epact == 25 && goldn > 11) || epact == 24)
epact++;
return Math.floor (((((diy + epact) * 6) + 11) % 177) / 22) % 8; >> }
And a final remark... - In this case you have to also consider calendar switches (Julian/Gregorian) in your date calculations. (Not sure your
code supports that.)
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
On 11/11/22 14:06, Janis Papanagnou wrote:
And a final remark... - In this case you have to also consider calendar
switches (Julian/Gregorian) in your date calculations. (Not sure your
code supports that.)
We dont support such difference in calendar because I am not very much familiar with the difference, we support UNIX time and ISO8601 dates
only, for as far as few centuries as static maths may go.
On 11/11/22 13:36, Janis Papanagnou wrote:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
Bash integer arithmetics do floor rounding by deafults,
On 11/13/22 10:15, castAway wrote:
On 11/11/22 13:36, Janis Papanagnou wrote:
I was recently looking at some code and there's probably similar
cryptic formulas existing. I used an adapted _quantized_ version
(only 8 phases, and a plain integer calculation) for a web page:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
{
var diy = day_in_year (now);
var goldn = (now.getFullYear() % 19) + 1;
var epact = (11 * goldn + 18) % 30;
if ((epact == 25 && goldn > 11) || epact == 24)
epact++;
return Math.floor (((((diy + epact) * 6) + 11) % 177) / 22) % 8;
}
Found this function to be from hacklib.c.
The original hacklib function
is a little different, as it uses "TM Year" instead of "Year", no floor rounding and the last mod division is by 7 rather than 8... I tested it,
and Janis' version of the function is more accurate, as far as the few results I checked are concerned. I don't understand the formula thus IDK
why the hacklib function had to be updated. That being said, it ought to
be a reasonable function to use after I do some more testing!
hacklib.c: <https://nethackwiki.com/wiki/Source:NetHack_1.3d/unixunix.c>
castAway
On 11/11/22 13:36, Janis Papanagnou wrote:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
OMG, did you just copy & paste this function ? Well, that seems intuitive, given references
On 11/11/22 13:36, Janis Papanagnou wrote:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
Bash integer arithmetics do floor rounding by deafults, don't trust,
check it, thus $(( 7/4 )) returns 1... Like Ksh,
so maybe the floor rounding
function you mentioned is an overhead... Maybe in Java scripting all
integers are calculated as floating point?
On 11/10/22 21:46, Janis Papanagnou wrote:
Looks to me like the mentioned date programs or a simple GNU awk date
implementation supports that already.
[...]
Now, I know that KSH is very fast and I also read that Ksh code is very convoluted
or very difficult to read and maintain, for example, Mr Siteshwar and Mr Rader
complained a lot of the Ksh code when they were at it trying to develop
it further.
So, it comes to me that Mr Korn sacrificed readability and
maintainability in
exchange for speed, or am I wrong?
Cheers,
JSN
However, the functionality seems to be very basic:
% $AST/date -E '2002-01-01' '2012-01-01'
9Y11M
% $AST/date -E '12:01:01' '19:02:02'
7h01m
% $AST/date -E '2002-01-01 12:01:01' '2012-01-01 19:01:01'
9Y11M
On 12.11.2022 16:40, castAway wrote:
However, the functionality seems to be very basic:
% $AST/date -E '2002-01-01' '2012-01-01'
9Y11M
% $AST/date -E '12:01:01' '19:02:02'
7h01m
% $AST/date -E '2002-01-01 12:01:01' '2012-01-01 19:01:01'
9Y11M
What does it return if you provide ISO dates?
$AST/date -E '2002-01-01T12:01:01' '2012-01-01T19:01:01'
Janis
On 17.11.2022 03:36, Janis Papanagnou wrote:
On 12.11.2022 16:40, castAway wrote:
However, the functionality seems to be very basic:
% $AST/date -E '2002-01-01' '2012-01-01'
9Y11M
% $AST/date -E '12:01:01' '19:02:02'
7h01m
% $AST/date -E '2002-01-01 12:01:01' '2012-01-01 19:01:01'
9Y11M
What does it return if you provide ISO dates?
$AST/date -E '2002-01-01T12:01:01' '2012-01-01T19:01:01'
Nevermind. I found an AST date in some forgotten directory and the
result is the same. Seems we'd need two calls for sub-day accuracy,
and some formatting to create correctly formatted ISO time periods.
Janis
No. The C source function is doing the last step as _binary_ operation
... & 7
which is equivalent here to the _arithmetic_ counterpart
... % 8
that uses the modulo operator (as opposed to bit-wise 'and').
castAway <no@where.com> writes:
On 11/11/22 13:36, Janis Papanagnou wrote:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
Bash integer arithmetics do floor rounding by deafults,
No. Bash's integer division truncates towards zero. The manual says
the results as "as in C", which presumably means modern C. In C90, x/y
with either operand negative was implementation defined. Since C99 the result is truncated towards zero.
On 11/16/22 17:12, Ben Bacarisse wrote:
castAway <no@where.com> writes:Thanks for the clarification! if the function was written in C code and
On 11/11/22 13:36, Janis Papanagnou wrote:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
Bash integer arithmetics do floor rounding by deafults,
No. Bash's integer division truncates towards zero. The manual says
the results as "as in C", which presumably means modern C. In C90, x/y
with either operand negative was implementation defined. Since C99 the
result is truncated towards zero.
Bash is as in C, then they should behave similarly.
Ksh93 `superior'
parameter scoping in functions was a litte hard to deal with [...]
[...] Specially, the new friday_13th() function
may still need some more work.
I reckon it would be cool to have a shell function to convert UNIX times to ISO-8601 (and RFC-5322) formats.
As mentioned upthread there's also the more
portable f()(...) instead of f(){...;} if all you want is
local-scoped variables.
I'm also still unsure about the supported date ranges. I a post
quite some time ago I posted some observations with the different
ranges of time functions in 'date', 'ksh', and 'awk'; all we seems
to be able to rely on was (IIRC) the rather short Unix-Epoch range.
Is there anything more about that function than just checking the
day-of-week (Friday) and date-in-month (13)?
On 11/11/22 13:36, Janis Papanagnou wrote:
function phase_of_the_moon (now) // 0-7, with 0: new, 4: full
Bash integer arithmetics do floor rounding by deafults, don't trust,
check it, thus $(( 7/4 )) returns 1... Like Ksh, so maybe the floor rounding function you mentioned is an overhead... Maybe in Java scripting all integers are calculated as floating point?
I am afraid to have gone a little over the top. The lunar phase function
was implemented, [...]
#return phase of the moon, use UTC time
#usage: phase_of_the_moon year [month] [day]
function phase_of_the_moon #0-7, with 0: new, 4: full
{
typeset day month year diy goldn epact
day="${1#0}" month="${2#0}" year="${3##+(0)}"
day=${day:-1} month=${month:-1} year=${year:-0}
diy=$(get_day_in_year "$day" "$month" "$year")
((goldn = (year % 19) + 1))
((epact = (11 * goldn + 18) % 30))
(((epact == 25 && goldn > 11) || epact == 24 )) && ((epact++))
case $(( ( ( ( ( (diy + epact) * 6) + 11) % 177) / 22) &
7)) in
0) set -- 'New Moon' ;; #.0
1) set -- 'Waxing Crescent' ;;
2) set -- 'First Quarter' ;; #.25
3) set -- 'Waxing Gibbous' ;;
4) set -- 'Full Moon' ;; #.5
5) set -- 'Waning Gibbous' ;;
6) set -- 'Last Quarter' ;; #.75
7) set -- 'Waning Crescent' ;;
esac
I'm also still unsure about the supported date ranges. I a postI am mulling over about shell arithmetics limits.
quite some time ago I posted some observations with the different
ranges of time functions in 'date', 'ksh', and 'awk'; all we seems
to be able to rely on was (IIRC) the rather short Unix-Epoch range.
On 11/19/22 10:40, Janis Papanagnou wrote:
[...]
One thing I forgot that I wanted to point out...
We should be aware that the 8-value quantization will result in phases
of 3 or 4 consecutive days with the same moon phase. I noticed that in
the game of Nethack (where that code stems from) the "Nethack new moon"
phase _starts_ at the day when _real_ new moon actually is. That might
not be what one expects, though. At least my expectation was that it
would be better to either center the real moon phase date around these
3-4 days phase, or give up the quantization and calculate it on a 29.5
days per month basis.
I think the moon phase is a little subjective as brightness of the moon varies from the observed GPS location in the globe.
The moon phase is primarily depending the position of moon and sun as
seen from earth; if (for example) moon and sun are in an orthogonal
angle you have a half moon phase. Because of the magnitude of actual distances and diameters ([avg.] sun ~150'000'000 km, moon ~184'000 km,
earth diameter ~12'700 km) the concrete observation position on earth
is not significant.
S M
E
You know we cannot do anything about improving this formula, right?
distances and diameters ([avg.] sun ~150'000'000 km, moon ~184'000 km,
earth diameter ~12'700 km) [...]
An older version of this moon phase function, or a Java Script version
of it, it used to subtract 1900 from whatever year was input, so for
e.g. year 1970 would be the same as year number 70 (1970-1900).
For this reason, I thought it may be feasible to adjust this formula
by adding or subtracting some years for the internal calculation.
In the NetHack comment for this formula, it notes the following:
#days moon phase advances on first day of year compared
#to preceding year
# = 365.2422 - 12*29.53058 ~= 11
#years in Metonic cycle (time until same phases fall on
#the same days of the month)
# = 18.6 ~= 19
It means the reading frame of the moon phase can be adjusted if
one can find a good number to correct the input year for internal calculation.
In order to verify what adjustment could be made, all data for lunar
phases from 1700 to 2102 was scraped from <https://aa.usno.navy.mil/data/MoonPhases>.
Input year was then adjusted with a value from -200 to +200 years,
meaning that when input year is 1700, moon phases were calculated
as if it were {1500..1900}.
Generated tables were compared with that scraped from US Navy website.
That was possible to determine, within the tested interval of correction from -200 to +200 years, that adding any `8', `-68', `84', `64' or `-144'
to user input produces more accurate results.
For example, it was possible to check that the original function as
was copied from modern NetHack matches exactly the US Navy dataset
3495 out of 19944 primary phases for the period between 1700-2102.
Adjustment Matches Non-matches
+0 3495 16449
+8 8037 11907
-144 8039 11905
-68 8047 11897
+84 8056 11888
The original formula can have up to 5 days or error, I found.
The following example shows the US Navy scrape information for
moon phases from May 1924. Original function returns results
up to 5 days out of sync. Adding 8 years to internal calculation
delivers much better results.
% grep 1924 ~/navy/all.txt | grep May
New Moon 1924 May 3 23:00
First Quarter 1924 May 12 02:14
Full Moon 1924 May 18 21:52
Last Quarter 1924 May 25 14:16
% datediff.sh -m 1924-05 | grep -v -e Wan -e Wax
1924-05-01 New Moon
1924-05-07 First Quarter
1924-05-15 Full Moon
1924-05-22 Last Quarter
1924-05-29 New Moon
~ % ADD_YEARS=8 datediff.sh -m 1924-05 | grep -v -e Wan -e Wax
1924-05-03 New Moon
1924-05-10 First Quarter
1924-05-18 Full Moon
1924-05-25 Last Quarter
As far as I can tell, it may be worth correcting the input year
for internal calculation, however deciding if the correction factor
should be `8' or `-144' is to me a little arbitrary. Even though `-144' corrects a little better according to my tests, it may well be
that a value out of the tested range ( -200 >= tested range <= +200 )
may be even slightly better correction factor. This is just
a preliminary test.
Cheers,
JSN
this is the format returned by "declare -f", which i use as a shell-beautifier. it uses the null-command colon (:) as a comment, the first of which i call the abstract, a "shdoc" runs to the first non-Tag such lines, the "abstract" being the firstsuch, where a Tag is ": {tag}: ". "date" is a common tag, in this case "example" is backed up by an eval_example function.
this has the advantage of dragging around the useful information with in the function itself. i've a ~ 2400 member function collection where i'm always looking for the right place to keep a function, including a "retiredlib". and an "app" is littlemore than a few high-level functions which gather up all the necessary functions, awk scripts, and text files.
this is the format returned by "declare -f", which i use as a shell-beautifier. it uses the null-command colon (:) as a comment, the first of which i call the abstract, a "shdoc" runs to the first non-Tag such lines, the "abstract" being the firstsuch, where a Tag is ": {tag}: ". "date" is a common tag, in this case "example" is backed up by an eval_example function.
this has the advantage of dragging around the useful information with in the function itself. i've a ~ 2400 member function collection where i'm always looking for the right place to keep a function, including a "retiredlib". and an "app" is littlemore than a few high-level functions which gather up all the necessary functions, awk scripts, and text files.
On 12/1/22 12:12, applemcg wrote:
I avoid using the : command to add a comment because it is run by the
this is the format returned by "declare -f", which i use as a
shell-beautifier. it uses the null-command colon (:) as a comment,
[...]
shell and makes the function run slower.
a little late to the party, saw some discussion of function syntax, definition.
from this:
#get day in the week
#usage: get_day_in_week unix_time
function get_day_in_week
{
echo ${DAY_OF_WEEK[( ( ($1+($1<0?1:0))/(24*60*60))%7 +($1<0?6:7))%7]}
}
i'd write:
get_day_in_week ()
{
: get day in the week
: example: get_day_in_week unix_time 2300000
: uses: DAY_OF_WEEK
echo ${DAY_OF_WEEK[( ( ($1+($1<0?1:0))/(24*60*60))%7 +($1<0?6:7))%7]}
}
[...]
On Wednesday, November 30, 2022 at 1:01:27 AM UTC-5, castAway wrote:
[...]
printf "%s\n" ${DAY_OF_WEEK[ $(( unix_time + ... )) ]
[...]
On 01.12.2022 16:12, applemcg wrote:
a little late to the party, saw some discussion of function syntax, definition.
from this:
#get day in the week
#usage: get_day_in_week unix_time
function get_day_in_week
{
echo ${DAY_OF_WEEK[( ( ($1+($1<0?1:0))/(24*60*60))%7 +($1<0?6:7))%7]}
}
i'd write:
get_day_in_week ()Identical to function name, unnecessary bloat, a potential source of inconsistency.
{
: get day in the week
: example: get_day_in_week unix_time 2300000
Seems to document a wrong syntax (seems to be inconsistent to code).
: uses: DAY_OF_WEEK
Already quite obvious.
echo ${DAY_OF_WEEK[( ( ($1+($1<0?1:0))/(24*60*60))%7 +($1<0?6:7))%7]}
}
I'd probably write (using ksh syntax here, but similar for bash)
function get_day_in_week
{
typeset -i unix_time=${1:?}
printf "%s\n" ${DAY_OF_WEEK[ $(( unix_time + ... )) ]
}
and omit the unnecessary comments, add a variable for legibility and
safety (i.e. the test on $1), use arithmetic syntax for clarity, and
standard (and generally safer) printf.
[...]
Mileages vary.
Janis
On Wednesday, November 30, 2022 at 1:01:27 AM UTC-5, castAway wrote:
[...]
On 01.12.2022 16:12, applemcg wrote:
a little late to the party, saw some discussion of function syntax, definition.
from this:
#get day in the week
#usage: get_day_in_week unix_time
function get_day_in_week
{
echo ${DAY_OF_WEEK[( ( ($1+($1<0?1:0))/(24*60*60))%7 +($1<0?6:7))%7]}
}
i'd write:
get_day_in_week ()Identical to function name, unnecessary bloat, a potential source of inconsistency.
{
: get day in the week
: example: get_day_in_week unix_time 2300000
Seems to document a wrong syntax (seems to be inconsistent to code).
: uses: DAY_OF_WEEK
Already quite obvious.
echo ${DAY_OF_WEEK[( ( ($1+($1<0?1:0))/(24*60*60))%7 +($1<0?6:7))%7]}
}
I'd probably write (using ksh syntax here, but similar for bash)
function get_day_in_week
{
typeset -i unix_time=${1:?}
printf "%s\n" ${DAY_OF_WEEK[ $(( unix_time + ... )) ]
}
and omit the unnecessary comments, add a variable for legibility and
safety (i.e. the test on $1), use arithmetic syntax for clarity, and
standard (and generally safer) printf.
[...]
Mileages vary.
Janis
On Wednesday, November 30, 2022 at 1:01:27 AM UTC-5, castAway wrote:
[...]
thanks also for pointing out the performance hit isn't to severe
but let me object to the use of "bloat"
i'm working on an idea, where the shell source text, the stuff you
put in source control, has the "unnecessary bloat", and you've
given be a terrific idea.
i've library, "shdoclib" which mangles shell function text. the
syntax you're looking at is a feature i call "tags"
[...]
Now, to solve your "unnecessary bloat" problem, I'm already keeping a duplicate copy of every function library around, so, with a little
care, the one which i execute will be run thru a yet-to-be-written "just_nocolon" function, a complement of this "shd_justcolon" function:
somefunction ()
{
: "the first null command is the function's abstract"
: here quoted to protect the " ' " character
: after the shdoc, which are the first untagged-lines of the function, come the tags,
: such as the next line
: date: 2016-06-06, the first time we stored this in a library
: ... etc ...
: date: 2022-12-04, the most recent modification
... # followed by the body of the function
...
}
On 12/4/22 20:50, applemcg wrote:
somefunction ()
{
: "the first null command is the function's abstract"
: here quoted to protect the " ' " character
: after the shdoc, which are the first untagged-lines of the
function, come the tags,
: such as the next line
: date: 2016-06-06, the first time we stored this in a library
: ... etc ...
: date: 2022-12-04, the most recent modification
... # followed by the body of the function
...
}
all the : forward code is unnecessary and will be printed on debugging
also, i don't care about comments of the source code if the
reference can be trusted blindly
On 11.11.2022 17:36, Janis Papanagnou wrote:
On 11.11.2022 04:34, castAway wrote:
On 11/10/22 21:46, Janis Papanagnou wrote:
What does the script that AT&T date or GNU date doesn't support in this >>> (time-intervals) respect?
I am not sure that `awk' or `date' can be used to calculate time intervals
other than in days, hours, minutes and seconds. So how can you calculate >> these time differences with awk? You could get to an _approximate_ result >> very easily but it gets more difficult as we need to compensate for a lot >> of things, for example not all months have 4 weeks.
[...]
On re-reading it occurred to me that you were probably not focusing
on the sub-second issue but on support for dates before, say, 1900 or
after 2100, or so. - Yes, Unix tools have different (often restricted) support for dates outside the Unix Epoch. - So, yes, depending on the
used tool you'd have to transform the external form to an internal representation that fits this accuracy.
Janis
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 307 |
Nodes: | 16 (2 / 14) |
Uptime: | 31:10:36 |
Calls: | 6,907 |
Calls today: | 1 |
Files: | 12,376 |
Messages: | 5,427,927 |
Posted today: | 1 |