[REBOL] Re: How to...? Convert Date of Birth to Age
From: joel:neely:fedex at: 25-Oct-2002 15:17
Hi, Ladislav,
Nice summary!
Let me add a few remarks and another alternative.
Ladislav Mecir wrote:
> I think, that it may be useful to summarize the results.
>
> The first candidate:
>
> fwd-top: func [
...
> ]
>
> Advantages:
>
> 1) Rebol compatibility
>
> > FWD-TOP returns triplets that are consistent with the
> > way REBOL converts blocks to dates.
>
In retrospect, I could have specified this option by saying:
For two dates A, B such that A <= B, return the "smallest"
triplet of (non-negative) values Y, M, D such that
b = to-date reduce [a/year + y a/month + m a/day + d]
> 2) Forward counting
> 3) Half-monotonicity
>
> Disadvantages:
>
> 1) "Unusual" results
>
This is unavoidable, if the REBOL compatibility criterion is
to be satisfied. For all solutions, "Usuality" and compatibility
with REBOL date arithmetic are mutually exclusive. Pick one.
> 2) Uni-directional counting
>
I was addressing the expanded problem of "difference between
two arbitrary dates" to avoid the semantic complications of
birthdays (see below).
To be fair, we could add:
3) Slow.
(However, I have another version that is equivalent but much
faster; benchmarks to follow later.)
> The second candidate:
>
> new-age: function [birth [date!] date [date!]] [
...
> ]
>
> Advantages:
>
> 1) "Usual" results
> 2) Birth-related counting direction
>
For a person born on 24-Aug-1977, what was that person's age
on 12-Aug-1974? Some would say that the question is meaningless,
in the same sense that
find [1 3 5 7 9 11] 2
evaluates to NONE.
> 3) Weak monotonicity
>
> Disadvantages:
>
> 1) Non-strict monotonicity
>
> The third candidate:
>
> strict-age: function [birth [date!] date [date!]] [
...
> Advantages:
>
> 1) Birth-related counting direction
> 2) Monotonicity / uniqueness
>
> Disadvantages:
>
> 1) "Unusual" results
>
By way of philosophy, let me offer another Grand Universal Principle
of software development:
When there are multiple arguably correct solutions to a
problem, push the decision up the food chain.
We can do this at requirements time by insisting on more rigorous
specification in advance, or we can do this at run time by making
the selection among alternatives available to the caller/user.
Therefore...
A fourth candidate:
ymd-sub: func [
left [date!] right [date!]
/local y m d not-yet? too-far? one i j k
][
either left <= right [
not-yet?: :greater?
too-far?: :lesser?
one: +1
][
not-yet?: :lesser?
too-far?: :greater?
one: -1
]
y: left/year
m: left/month
d: left/day
i: j: k: 0
while [not-yet? right to-date reduce [y m d]] [y: y + one i: i +
one]
while [too-far? right to-date reduce [y m d]] [y: y - one i: i -
one]
while [not-yet? right to-date reduce [y m d]] [m: m + one j: j +
one]
while [too-far? right to-date reduce [y m d]] [m: m - one j: j -
one]
while [not-yet? right to-date reduce [y m d]] [d: d + one k: k +
one]
while [too-far? right to-date reduce [y m d]] [d: d - one k: k -
one]
reduce [i j k]
]
which behaves as follows:
>> ymd-sub 28-jan-2000 3-mar-2000 == [0 1 4]
>> ymd-sub 3-mar-2000 28-jan-2000 == [0 -1 -6]
i.e., if the arguments are increasing, calculate the difference with
time running forward, but if the arguments are decreasing, calculate
the result with time moving backward. The results will be unambiguous
in the sense that forward differences are all non-negative (>= 0),
while backward differences are all non-positive (<= 0).
-jn-
--
----------------------------------------------------------------------
Joel Neely joelDOTneelyATfedexDOTcom 901-263-4446