[REBOL] Re: How to...? Convert Date of Birth to Age
From: joel::neely::fedex::com at: 22-Oct-2002 18:46
Hi, Ladislav,
I'm long overdue to reply to this, but have had occasion to revisit
the "difference of dates" problem. For various obscure reasons,
substitue FWD-TOP for DATEDELTA in the snippet below.
Ladislav Mecir wrote:
> Hi,
>
> >> age 29/2/2004 28/2/2005
> == [0 11 28]
> >> datedelta 29/2/2004 28/2/2005
> == [0 11 30]
>
> >> age 29/2/2004 1/3/2005
> == [1 0 1]
> >> datedelta 29/2/2004 1/3/2005
> == [1 0 0]
>
> Which results are correct?
>
The issue (which I didn't think through with sufficient precision
in the earlier discussion) is that we tend to expect all of these
statements to be equivalent:
last - first = difference
last - difference = first
first + difference = last
when, in fact, they are not equivalent for date "arithmetic". To
illustrate, consider the number of months/days between the dates
27-Jan-2004 and 03-Mar-2004. Working forward, we observe that:
27-Jan-2002 -> 27-Feb-2002 = 1 months 0 days
27-Feb-2002 -> 03-Mar-2002 = 0 months 4 days
--------------------------------------------
27-Jan-2002 -> 03-Mar-2002 = 1 months 4 days
but working backward, we find that:
03-Mar-2002 <- 03-Feb-2002 = 1 months 0 days
03-Feb-2002 <- 27-Jan-2002 = 0 months 7 days
--------------------------------------------
03-Mar-2002 <- 27-Jan-2002 = 1 months 7 days
and this difference resembles the difference between:
>> fwd-top 27-jan-2002 03-mar-2002 == [0 1 4]
>> age 27-jan-2002 03-mar-2002 == [0 1 7]
So, as you asked, which is "correct"?
I've concluded that there are two reasons for favoring the
behavior of FWD-TOP over AGE as follows:
Minor reason: We think of time as moving forward, rather than
backward, so if we must choose, let's use the
direction that matches our experience.
Major reason: Monotonicity. For two dates A and B, where A < B,
we would expect the difference between A and B
(in whatever representation) to increase as B increases. This
behavior is exhibited by FWD-TOP, but not by AGE, as shown in the
following example:
>> for target 29-mar-2004 03-apr-2004 1 [
[ print [target tab mold fwd-top 08-feb-2004 target]
[ ]
29-Mar-2004 [0 1 21]
30-Mar-2004 [0 1 22]
31-Mar-2004 [0 1 23]
1-Apr-2004 [0 1 24]
2-Apr-2004 [0 1 25]
3-Apr-2004 [0 1 26]
>> for target 29-mar-2004 03-apr-2004 1 [
[ print [target tab mold age 08-feb-2004 target]
[ ]
29-Mar-2004 [0 1 21]
30-Mar-2004 [0 1 22]
31-Mar-2004 [0 1 23]
1-Apr-2004 [0 1 22]
2-Apr-2004 [0 1 23]
3-Apr-2004 [0 1 24]
I have trouble with the idea that a baby born on 08-feb-2004 will
be the same age (in months/days) on 1-apr-2004 as (s)he was two
days earlier.
This is (to me, at least) an interesting demonstration of the
fact that the "real world" of everyday activity, and the "ideal
world" of computing, do not always correspond as nicely as we
programmers would like to think they do! ;-)
-jn-
PS: To save anyone the trouble of digging through dusty email
archives, the relevant function definitions appear below:
fwd-top: func [
lo [date!] hi [date!]
/local y m d i j k
][
if hi < lo [k: lo lo: hi hi: k]
y: lo/year
m: lo/month
d: lo/day
i: j: k: 0
while [hi > to-date reduce [y m d]] [y: y + 1 i: i + 1]
while [hi < to-date reduce [y m d]] [y: y - 1 i: i - 1]
while [hi > to-date reduce [y m d]] [m: m + 1 j: j + 1]
while [hi < to-date reduce [y m d]] [m: m - 1 j: j - 1]
while [hi > to-date reduce [y m d]] [d: d + 1 k: k + 1]
while [hi < to-date reduce [y m d]] [d: d - 1 k: k - 1]
reduce [i j k]
]
age: function [birth [date!] date [date!]] [years months days new] [
if date < birth [
return head insert age date birth '-
]
days: date/day - birth/day
if negative? days [
months: birth/month + 1
years: birth/year
if months > 12 [
months: 1
years: years + 1
]
new: to date! reduce [1 months years]
days: new - birth + date/day - 1
birth: new
]
months: date/month - birth/month
years: date/year - birth/year
if negative? months [
months: months + 12
years: years - 1
]
reduce [years months days]
]
--
----------------------------------------------------------------------
Joel Neely joelDOTneelyATfedexDOTcom 901-263-4446