You are given a date (

`yyyy/mm/dd`

).Assuming, the given date is your date of birth. Write a script to find the mirror dates of the given date.

Dave Cross has built a cool site that does something similar.

Assuming today is

`2021/09/22`

:

`Input: 2021/09/18 Output: 2021/09/14, 2021/09/26`

On the date you were born, someone who was your current age, would have been born on

`2021/09/14`

. Someone born today will be your current age on`2021/09/26`

.

`Input: 1975/10/10 Output: 1929/10/27, 2067/09/05`

On the date you were born, someone who was your current age, would have been born on

`1929/10/27`

. Someone born today will be your current age on`2067/09/05`

.

`Input: 1967/02/14 Output: 1912/07/08, 2076/04/30`

On the date you were born, someone who was your current age, would have been born on

`1912/07/08`

. Someone born today will be your current age on`2076/04/30`

.

The first thing we need to ask when using dates, in particular dates
in the past is *Which calendar?*. The Gregorian Calendar seems
an obvious choice, but only if one is naïeve. The Gregorian calendar
starts on different dates, depending on the country. While the calendar
was introduced in 1582 in
some countries, in other countries, it was
introduced less than 100 years ago.

To make the challenge simpler for us, we will be assuming the Proleptic Gregorian calendar. This basically means we are assuming the Gregorian calendar has always existed. We will also be assuming the year 0 existed.

We could use one of the gazillion date modules from CPAN. However, we will not. Instead, of the two given dates ("today", and the input date), we will calculate the Julian Day Number. Given those, we can easily calculate the Julian Day Numbers of the target dates, which we then convert back to (proleptic) Gregorian dates.

We will use the formulas on Wikipedia to convert dates to and from Julian Day Numbers.

It should be noted that division needs to be done as *integer division*,
with results *rounded towards 0*. That is, `5 / 2 == 2`

, and `-5 / 2 == -2`

.

For the calculations, we fix "today" as `2021/09/22`

(using a fixed date
means our tests are deterministic — and if we fix a date, we may as well
use the date in the given examples.)

We also be reading dates from standard input, one date per line, and for each input date, we output a line with the two target dates.

First, we create a sub which takes a year, month, and date and returns a Julian Day Number:

```
sub g2j ($Y, $M, $D) {
use integer;
(1461 * ($Y + 4800 + ($M - 14) / 12)) / 4 +
(367 * ($M - 2 - 12 * (($M - 14) / 12))) / 12 -
(3 * (($Y + 4900 + ($M - 14) / 12) / 100)) / 4 + $D - 32075
}
```

Note the directive `use integer;`

, which causes Perl to do division
the way C does division between integers.

We also need a sub which takes a Julian Day Number, and returns the date:

```
sub j2g ($J) {
use integer;
my $e = 4 * ($J + 1401 + (((4 * $J + 274277) / 146097) * 3) / 4 - 38) + 3;
my $D = ((5 * (($e % 1461) / 4) + 2) % 153) / 5 + 1;
my $M = (((5 * (($e % 1461) / 4) + 2) / 153 + 2) % 12) + 1;
my $Y = ($e / 1461) - 4716 + (12 + 2 - $M) / 12;
($Y, $M, $D)
}
```

Now we calculate todays Julian Day Number:

```
my @TODAY = (2021, 9, 22);
my $julian_today = g2j @TODAY;
```

Read a date (from `$_`

), and calculate its Julian Day Number:

```
my ($Y, $M, $D) = /[0-9]+/g;
my $julian_then = g2j $Y, $M, $D;
```

The target Julian Day Numbers are now
`2 * $julian_then - $julian_today`

and `2 * $julian_today - $julian_then`

.
Which means we can output the result as:

```
printf "%04d/%02d/%02d, %04d/%02d/%02d\n",
j2g (2 * $julian_then - $julian_today),
j2g (2 * $julian_today - $julian_then);
```

Find the full program on GitHub.

The AWK solution is very similar to the Perl solution, but there are
a few differences. First difference is that AWK doesn't do integer
division. But it does have a `int`

function which rounds towards `0`

.
This makes the `g2j`

function as follows:

```
function g2j (Y, M, D) {
return (int ((1461 * (Y + 4800 + int ((M - 14) / 12))) / 4) + \
int ((367 * (M - 2 - 12 * int ((M - 14) / 12))) / 12) - \
int ((3 * int (((Y + 4900 + int ((M - 14) / 12)) / 100))) / 4) + \
D - 32075)
}
```

Second difference is that AWK cannot return arrays or list from functions.
So, we will return a formatted date from `j2g`

:

```
function j2g (J) {
e = 4 * (J + 1401 + int (int ((4 * J + 274277) / 146097) * 3 / 4) - 38) + 3
D = int (((5 * (int ((e % 1461) / 4)) + 2) % 153) / 5) + 1
M = ((int ((5 * (int ((e % 1461) / 4)) + 2) / 153) + 2) % 12) + 1
Y = int (e / 1461) - 4716 + int ((12 + 2 - M) / 12)
return sprintf ("%04d/%02d/%02d", Y, M, D)
}
```

To set up things, we will set the `FS`

variable to `/`

; that way, AWK
will split the input for us, giving use the input year, month and date
in the variables `$1`

, `$2`

, and `$3`

:

```
BEGIN {
FS = "/"
TODAY_Y = 2021
TODAY_M = 9
TODAY_D = 22
julian_today = g2j(TODAY_Y, TODAY_M, TODAY_D)
}
```

The main loop is now simple:

```
{
julian_then = g2j($1, $2, $3)
print j2g(2 * julian_then - julian_today) ", " \
j2g(2 * julian_today - julian_then)
}
```

Find the full program on GitHub.

Bash does do integer division (it doesn't deal at all with floating point numbers), but to doesn't return values from functions. So we have to use global variables to return values. It also uses numbered variables as the function parameters. This leads to:

```
function g2j () {
local Y=$1
local M=$2
local D=$3
J=$(( ((1461 * (Y + 4800 + (M - 14) / 12)) / 4 +
(367 * (M - 2 - 12 * ((M - 14) / 12))) / 12 -
(3 * ((Y + 4900 + (M - 14) / 12) / 100)) / 4 + D - 32075) ))
}
```

and

```
function j2g () {
local J=$1
local e=$(( 4 * (J + 1401 +
(((4 * J + 274277) / 146097) * 3) / 4 - 38) + 3 ))
D=$(( ((5 * ((e % 1461) / 4) + 2) % 153) / 5 + 1 ))
M=$(( (((5 * ((e % 1461) / 4) + 2) / 153 + 2) % 12) + 1 ))
Y=$(( (e / 1461) - 4716 + (12 + 2 - M) / 12 ))
}
```

Setting up the Julian Day Number for today shows how we use the output values. Note also that we don't use commas to separate arguments.

```
g2j 2021 9 22; julian_today=$J
```

We can use the same trick as in AWK to have the input automatically split;
the variable `IFS`

plays the role of `FS`

in AWK. But there is a catch.
An input like `08`

or `09`

is interpreted as an (illegal) octal number
when used in arithmetic. So we have to strip off any leading 0s.
We do this like this: `${d/#0/}`

, which takes the value in `$d`

, and
replaces the leading `0`

with an empty string.

This results in the following main loop:

```
IFS="/"
while read y m d
do g2j ${y/#0/} ${m/#0/} ${d/#0/}; julian_then=$J
j2g $(( 2 * julian_then - julian_today ))
printf "%04d/%02d/%02d, " $Y $M $D
j2g $(( 2 * julian_today - julian_then ))
printf "%04d/%02d/%02d\n" $Y $M $D
done
```

Find the full program on GitHub.

The `g2j`

function in C is very similar to the one in Perl. Division
in C between integer types is integer division:

```
typedef unsigned short date_type;
long g2j (date_type Y, date_type M, date_type D) {
return ((1461 * (Y + 4800 + (M - 14) / 12)) / 4 +
(367 * (M - 2 - 12 * ((M - 14) / 12))) / 12 -
(3 * ((Y + 4900 + (M - 14) / 12) / 100)) / 4 + D - 32075);
}
```

For the `j2g`

function, we take an additional parameter: an appropriate
sized array in which we put the date component parts:

```
# define idx_Y 0
# define idx_M 1
# define idx_D 2
void j2g (long J, date_type * date) {
long e = 4 * (J + 1401 + (((4 * J + 274277) / 146097) * 3) / 4 - 38) + 3;
date [idx_D] = ((5 * ((e % 1461) / 4) + 2) % 153) / 5 + 1;
date [idx_M] = (((5 * ((e % 1461) / 4) + 2) / 153 + 2) % 12) + 1;
date [idx_Y] = (e / 1461) - 4716 + (12 + 2 - date [idx_M]) / 12;
}
```

In the main program, we have to allocate memory for the date components of the two dates, and we calculate the Julian Day Number of "today":

```
unsigned short TODAY [] = {2021, 9, 22};
int main (void) {
date_type Y, M, D;
date_type * date_early;
date_type * date_late;
if ((date_early = (date_type *) malloc (3 * sizeof (date_type))) == NULL) {
perror ("Malloc failed");
exit (1);
}
if ((date_late = (date_type *) malloc (3 * sizeof (date_type))) == NULL) {
perror ("Malloc failed");
exit (1);
}
long julian_today = g2j (TODAY [idx_Y], TODAY [idx_M], TODAY [idx_D]);
```

We use `scanf`

to parse the input. The `%hu`

format indicates we
are reading an unsigned short. This gives use the following main loop:

```
while (scanf ("%hu/%hu/%hu", &Y, &M, &D) == 3) {
long julian_then = g2j (Y, M, D);
j2g (2 * julian_then - julian_today, date_early);
j2g (2 * julian_today - julian_then, date_late);
printf ("%04d/%02d/%02d, %04d/%02d/%02d\n",
date_early [idx_Y], date_early [idx_M], date_early [idx_D],
date_late [idx_Y], date_late [idx_M], date_late [idx_D]);
}
```

At the end, we free the allocated memory:

```
free (date_early);
free (date_late);
```

Find the full program on GitHub.

Nieuwer version of Lua (nieuwer than the version I run), has `//`

to
do integer division. Lua also doesn't have an `int`

function, so we
roll our own:

```
function int (x)
if x > 0 then
return math . floor (x)
else
return math . ceil (x)
end
end
```

`math . floor`

rounds toward \(- \infty\), and `math . ceil`

rounds towards
\(\infty\). Since we want to round towards `0`

, we check whether the value
we want to round is positive or not, and use the appropriate rounding
function.

This leads to

```
function g2j (Y, M, D)
return (int ((1461 * (Y + 4800 + int ((M - 14) / 12))) / 4) +
int ((367 * (M - 2 - 12 * int ((M - 14) / 12))) / 12) -
int ((3 * int (((Y + 4900 + int ((M - 14) / 12)) / 100))) / 4) +
D - 32075)
end
```

and

```
function j2g (J)
local e = 4 * (J + 1401 +
int (int ((4 * J + 274277) / 146097) * 3 / 4) - 38) + 3
local D = int (((5 * (int ((e % 1461) / 4)) + 2) % 153) / 5) + 1
local M = ((int ((5 * (int ((e % 1461) / 4)) + 2) / 153) + 2) % 12) + 1
local Y = int (e / 1461) - 4716 + int ((12 + 2 - M) / 12)
return Y, M, D
end
```

The rest of the program looks like:

```
local julian_today = g2j (2021, 9, 22)
local output_format = "%04d/%02d/%02d, %04d/%02d/%02d\n"
for line in io . lines () do
local _, _, Y, M, D = line : find ("([0-9]+)/([0-9]+)/([0-9]+)")
local julian_then = g2j (Y, M, D)
local Y1, M1, D1 = j2g (2 * julian_then - julian_today)
local Y2, M2, D2 = j2g (2 * julian_today - julian_then)
io . write (output_format : format (Y1, M1, D1, Y2, M2, D2))
end
```

Find the full program on GitHub.

In Node.js, we don't have integer division either, nor does it have
and `int`

function. So we use the same trick as we used in the Lua
solution (which we don't copy here).

The tricky thing about the Node.js solution is the lack of a usable
`printf`

functionality. So, we have to create our own.

First, a method to pre-pad numbers with `0`

s till the required length:

```
function pad (num, l) {
let out = num
while (out . length < l) {
out = "0" + out
}
return (out)
}
```

Then we can create pretty print function, which takes two dates (as arrays), and formats them:

```
function pp (d1, d2) {
let e1 = d1 . map (x => x . toString ())
let e2 = d2 . map (x => x . toString ())
console . log ("%s/%s/%s, %s/%s/%s",
pad (e1 [0], 4), pad (e1 [1], 2), pad (e1 [2], 2),
pad (e2 [0], 4), pad (e2 [1], 2), pad (e2 [2], 2))
}
```

This leads to the following main program:

```
let julian_today = g2j (2021, 9, 22)
require ('readline')
. createInterface ({input: process . stdin})
. on ('line', line => {
let [Y, M, D] = line . split ('/') . map (x => +x)
let julian_then = g2j (Y, M, D)
pp (j2g (2 * julian_then - julian_today),
j2g (2 * julian_today - julian_then))
})
```

Find the full program on GitHub.

Python has `//`

to do integer division, but this rounds towards
\(-\infty\), which makes it useless for our purposes. Luckily, its
`int`

method rounds towards `0`

, so we can use that, giving us a
similar `g2j`

and `j2g`

as we have seen before.

This gives us the following main program:

```
for line in fileinput . input ():
Y, M, D = map (lambda x: int (x), line . strip () . split ("/"))
julian_then = g2j (Y, M, D)
Y1, M1, D1 = j2g (2 * julian_then - julian_today)
Y2, M2, D2 = j2g (2 * julian_today - julian_then)
print ('{:04d}/{:02d}/{:02d}, {:04d}/{:02d}/{:02d}' .
format (Y1, M1, D1, Y2, M2, D2))
```

Find the full program on GitHub.

Ruby uses integer division when divising integers, but it rounds towards
\(-\infty\). We therefore create a `div`

function which does integer
division with rounding towards `0`

:

```
def div (x, y)
return (x . to_f / y) . to_i
end
```

In this method, we convert one of the arguments to a floating point
number (using `to_f`

), then, after division, we use `to_i`

to force
the result to be an integer. `to_i`

rounds towards `0`

.

This gives us the following `g2j`

:

```
def g2j (y, m, d)
return (div(1461 * (y + 4800 + div(m - 14, 12)), 4) +
div( 367 * (m - 2 - 12 * div(m - 14, 12)), 12) -
div( 3 * div(y + 4900 + div(m - 14, 12), 100), 4) + d - 32075)
end
```

and `j2g`

:

```
def j2g (j)
e = 4 * (j + 1401 + div(div(4 * j + 274277, 146097) * 3, 4) - 38) + 3;
d = div((5 * div(e % 1461, 4) + 2) % 153, 5) + 1;
m = ((div( 5 * div(e % 1461, 4) + 2, 153) + 2) % 12) + 1;
y = div(e, 1461) - 4716 + div(12 + 2 - m, 12);
return y, m, d
end
```

The main program then becomes:

```
julian_today = g2j 2021, 9, 22
ARGF . each_line do
| line |
y, m, d = line . strip . split("/") . map {|x| x . to_i}
julian_then = g2j y, m, d
puts "%04d/%02d/%02d, %04d/%02d/%02d\n" %
[j2g(2 * julian_then - julian_today),
j2g(2 * julian_today - julian_then)] . flatten
end
```

Find the full program on GitHub.