26
\$\begingroup\$

I was recently adding up the ages of myself and my wife and kids and realised that at some point in the not-too-distant future our ages will all add up to exactly 100 years.

Challenge

For an input consisting of a combined (total) age and a list of birth dates, output the date on which the combined ages all add up to the given total.

  1. Input combined age (in years) will be a positive integer
  2. Input list of birth dates will be a list (whatever format is convenient for your language) of dates that must contain numerical representation of Day, Month and Year. Number of days since a specific epoch is not acceptable. You may assume the list of input dates is sorted in chronological order.
  3. Output will be a single date in the same format as input dates
  4. For the purposes of the combined age, 1 year is considered to be exactly 365.25 days
  5. For some inputs, it will be impossible to find a date after all the birth dates when they all add up to the combined age. For instance consider two birth dates that are 20 years apart, but we want a combined of 10 years. In that case, the output would be the 10th birthday of the older birth date. In other words, individual ages are considered to be 0 for all dates before that individual's birth date
  6. The output will be the first date on which the combined ages add up to at least the input age
  7. You may use built-in functions
  8. You must support dates going back as far as 1970/01/01.

Examples

Here I'm giving all dates in YYYY/MM/DD format, but you may whatever format you like.

Input                                               Output

 10  2000/01/01                                     2010/01/01
100  1975/03/05,1978/07/23,2008/11/12,2012/12/20    2018/11/22
100  1975/03/06,1978/07/23,2008/11/12,2012/12/20    2018/11/23
100  1975/03/09,1978/07/23,2008/11/12,2012/12/20    2018/11/23
100  1975/03/10,1978/07/23,2008/11/12,2012/12/20    2018/11/24
  3  1990/08/01,1995/07/02                          1993/08/01
 50  1975/03/05,1978/07/23,2008/11/12,2012/12/20    2001/11/13

Note that in the last two examples, the output date lies within the range of input dates, as per rule 5.

\$\endgroup\$
  • 1
    \$\begingroup\$ @TimmyD. Rule 7 is a may rule, so you don't have to follow it if you don't want to. I suspect date-time functions will be useful for parsing of input dates and formatting of output dates. YMMV. \$\endgroup\$ – Digital Trauma Nov 12 '15 at 19:47
  • 5
    \$\begingroup\$ Rule 7 is a May rule. It can be safely ignored this month (and for the next half year). \$\endgroup\$ – Martin Ender Nov 13 '15 at 15:25
  • \$\begingroup\$ Realistically, I think a family would check at each birthday, counting years as integers and not trying to sum up the days (with 0.25 to complicate matter). \$\endgroup\$ – edc65 Nov 14 '15 at 15:16
  • \$\begingroup\$ @edc65 Who ever let reality get in the way of a good PPCG challenge? ;-) \$\endgroup\$ – Digital Trauma Nov 14 '15 at 20:57
4
\$\begingroup\$

Mathematica 138 107 237 bytes

My first attempts were sloppy and unworthy. This should work generally, provided that the birth dates are given in order of earliest to latest, as in the test cases.

The test cases were well-chosen and difficult to implement correctly. The code turned out way longer than I had hoped.

The general logic is to

  1. Solve for x (in days) such that the distance from x to each date of birth sums to the target passage of time (e.g 100 years converted to days).
  2. If the earliest date + x days > latest date of birth, remove the latest date of birth and repeat step (1), Else move to (3).
  3. Return (earliest date + x days)

t_~f~d_:=If[DateDifference[a=DateObject[DatePlus[d[[1]],Solve[t 365.25==Tr@(x-Join[{0},
QuantityMagnitude@(DateDifference[d[[1]],#,"Day"]&/@Rest@d)]),{x}][[1,1,2]]]/.
{y_,m_,n_,h_}:>{y,m,n+1}],d[[-1]]]>Quantity[0,"Days"],f[t,Most@d],a]

Test Cases

f[10, {{2000, 1, 1}}]
f[100, {{1975, 3, 5}, {1978, 7, 23}, {2008, 11, 12}, {2012, 12, 20}}]
f[100, {{1975, 3, 6}, {1978, 7, 23}, {2008, 11, 12}, {2012, 12, 20}}]
f[100, {{1975, 3, 9}, {1978, 7, 23}, {2008, 11, 12}, {2012, 12, 20}}]
f[100, {{1975, 3, 10}, {1978, 7, 23}, {2008, 11, 12}, {2012, 12,  20}}]
f[3, {{1990, 8, 1}, {1995, 7, 2}}]
f[50, {{1975, 3, 5}, {1978, 7, 23}, {2008, 11, 12}, {2012, 12, 20}}]

pics

\$\endgroup\$
  • \$\begingroup\$ Also, you don't need the space. \$\endgroup\$ – LegionMammal978 Nov 13 '15 at 13:17
1
\$\begingroup\$

PowerShell, 145 125 Bytes

param($a,$b)$c=@();$b|%{$c+=date $_};$t=$a*365.25;$d=$c[0];while($t-gt0){$c|%{if($_-le$d){$t--}};$d=$d.AddDays(1)}"{0:d}"-f$d

Pretty tricksy challenge, but relatively straightforward once understood.

Expanded and Commented:

param($a,$b)          # Take our input
$c=@()                # Make a new array $c
$b|%{$c+=date $_}     # Change our input array $b of strings into dates, store in $c
$t=$a*365.25          # Target number of days we need to account for
$d=$c[0]              # Our starting date, also our cumulative date for output
while($t-gt0){        # While we still have days remaining
  $c|%{               # Iterate through our birthdays
    if($_-le$d){$t--} # If the birthday is less than our cumulutive day, subtract a day
  }
  $d=$d.AddDays(1)    # We've accounted for another day, so increment our cumulative day
}
"{0:d}"-f$d           # Format output

Example:

Input needs to contain an explicitly formatted array of string-date representations. Output is in MM/DD/YYYY format (default for en-us localization in PowerShell).

PS C:\Tools\Scripts\golfing> .\combined-100-year-birthday.ps1 50 @('03/05/1975','07/23/1978','11/12/2008','12/20/2012')
11/13/2001

Edit -- Golfed 20 bytes by changing how we iterate through birthdays, and by using while instead of do/until

\$\endgroup\$
0
\$\begingroup\$

PHP, 220 bytes

I've added some newlines for readability.

function c($y,$s){for($d=$y*365.25;++$i<count($l=array_map(date_create,$s));)$d+=
$l[0]->diff($l[$i])->days;($l[$i-1]>$r=$l[0]->add(new DateInterval(P.ceil($d/$i).D)
))?c($y,array_slice($s,0,-1)):print$r->format('Y/m/d');}

Ideone

Here is the ungolfed version:

function printCombinedBirthday($combinedAgeInYears, $listOfBirthDatesStrings)
{
    $combinedAgeInDays = $combinedAgeInYears * 365.25;
    $listOfBirthDates = array_map('date_create', $listOfBirthDatesStrings);
    $numberOfBirthDates = count($listOfBirthDates);

    for ($i = 0; $i < $numberOfBirthDates; ++$i) {
        $combinedAgeInDays += $listOfBirthDates[0]->diff($listOfBirthDates[$i])->days;
    }

    $combinedAgeInDays = ceil($combinedAgeInDays / $numberOfBirthDates);
    $interval = new DateInterval('P'. $combinedAgeInDays .'D');
    $result = $listOfBirthDates[0]->add($interval);

    if ($listOfBirthDates[$numberOfBirthDates - 1] > $result)
    {
        printCombinedBirthday(
            $combinedAgeInYears,
            array_slice($listOfBirthDatesStrings, 0, -1)
        );
    }
    else {
        echo $result->format('Y/m/d');
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.