11
\$\begingroup\$

On my website, users enter their date of birth in the style xx.xx.xx - three two-digit numbers separated by dots. Unfortunately, I forgot to tell the users exactly which format to use. All I know is that one section is used for the month, one for the date, and one for the year. The year is definitely in the 20th century (1900-1999), so the format 31.05.75 means 31 May 1975. Also, I'm assuming everyone uses either the Gregorian or the Julian calendar.

Now, I want to go through my database to clear up the mess. I'd like to start by dealing with the users with the most ambiguous dates, that is, those where the range of possible dates is the biggest.

For example, the date 08.27.53 means 27 August 1953 in either the Gregorian or Julian calendar. The date in the Julian calendar is 13 days later, so the range is just 13 days.

In contrast, the notation 01.05.12 can refer to many possible dates. The earliest is 12 May 1901 (Gregorian), and the latest is 1 May 1912 (Julian). The range is 4020 days.

Rules

  • Input is a string in the format xx.xx.xx, where each field is two digits and zero-padded.
  • Output is the number of days in the range.
  • You can assume that the input will always be a valid date.
  • You may not use any built-in date or calendar functions.
  • Shortest code (in bytes) wins.

Testcases

  • 01.00.31 => 12
  • 29.00.02 => 0 (The only possibility is 29 February 1900 (Julian))
  • 04.30.00 => 13
  • 06.12.15 => 3291
\$\endgroup\$
4
  • \$\begingroup\$ Is the 5, May 1975 supposed to be 31st? Also, do we have to account for leap years? \$\endgroup\$
    – Maltysen
    Commented May 12, 2015 at 23:26
  • \$\begingroup\$ @Maltysen Yes, fixed. Yes. \$\endgroup\$
    – Ypnypn
    Commented May 12, 2015 at 23:35
  • \$\begingroup\$ Why couldn't it be in the 21st century? \$\endgroup\$ Commented May 13, 2015 at 1:09
  • \$\begingroup\$ @ElefantPhace The rules state that the 20th century is assumed; otherwise there would be no maximum date. \$\endgroup\$
    – Ypnypn
    Commented May 13, 2015 at 3:00

2 Answers 2

7
\$\begingroup\$

Pyth, 118 bytes

M++28@j15973358 4G&qG2!%H4FN.pmv>dqhd\0cz\.I&&&hN<hN13eN<eNhgFPNaYK+++*365JhtN/+3J4smghdJthNeNInK60aY-K+12>K60;-eSYhSY

Try it online: Demonstration or Test Suite.

Necessary knowledge of Julian and Gregorian Calendars

Julian and Gregorian Calendar are quite similar. Each calendar divides a year into 12 months, each containing of 28-31 days. The exact days in a month are [31, 28/29 (depends on leap year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]. The only difference between the calendars are their definition of a leap year. In the Julian Calendar any year divisible by 4 is a leap year. The Gregorian Calendar is a bit more specific. Any year divisible by 4 is a leap year, except the year divisible by 100 and not divisible by 400.

So in the 20th century only one year is different. The year 1900, which is a leap year in the Julian Calendar, but not a leap year in the Gregorian Calendar. So the only date, that exists in the one calendar but not in the other calendar is the day 29.02.1900.

Because of the different leap year definition, there's a difference between a date in the Julian Calendar and the Gregorian Calendar. 12 days difference for a date before the 29.02.1900, and 13 days difference for dates after the 29.02.1900.

Simplified Pseudo-Code

Y = []  # empty list
for each permutation N of the input date:
   if N is valid in the Julian Calendar:
      K = number of days since 0.01.1900
      append K to Y
      if K != 60:  # 60 would be the 29.02.1900
         L = K - (12 if K < 60 else 13) 
         append L to Y
print the difference between the largest and smallest value in Y

Detailed Code Explanation

The first part M++28@j15973358 4G&qG2!%H4 defines a function g(G,H), which calculates the number of days in month G of a year H in the Julian Calendar.

M                            def g(G,H): return
      j15973358 4               convert 15973358 into base 4
     @           G              take the Gth element
  +28                           + 28
 +                &qG2!%H4      + (G == 2 and not H % 4)

And the next part is just the for loop, and the ifs. Notice that I interpret N in the format (month, year, day). Just because it saves some bytes.

FN.pmv>dqhd\0cz\.
             cz\.        split input by "."
    mv>dqhd\0            map each d of ^ to: eval(d[d[0]=="0":])
FN.p                     for N in permutations(^):

I&&&hN<hN13eN<eNhgFPN   
I                          if 
    hN                        month != 0
   &                          and
      <hN13                   month < 13
  &                           and
           eN                 day != 0
 &                            and
             <eNhgFPN         day < 1 + g(month,year):

aYK+++*365JhtN/+3J4smghdJthNeN
          JhtN                    J = year
     +*365J   /+3J4               J*365 + (3 + J)/4
    +              smghdJthN      + sum(g(1+d,year) for d in [0, 1, ... month-2])
   +                        eN    + day
  K                               K = ^
aYK                               append K to Y

InK60aY-K+12>K60            
InK60                             if K != 60:
     aY-K+12>K60                    append K - (12 + (K > 60)) to Y

;-eSYhSY
;          end for loop
 -eSYhSY   print end(sorted(Y)) - head(sorted(Y))
\$\endgroup\$
0
\$\begingroup\$

Perl 5, 294 bytes

sub f{map/(\d\d)(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])/             #1             
      &&$3<29+($2==2?!($1%4):2+($2/.88)%2)                        #2  
      &&($j{$_}=++$j+12)                                          #3
      &&$j!#1=60?$g{$_}=++$g:0,'000101'..'991231'if!%g;           #4
      pop=~/(\d\d).(\d\d).(\d\d)/;                                #5
      @n=sort{$a<=>$b}                                            #6
         grep$_,                                                  #7
         map{($j{$_},$g{$_})}                                     #8
         ("$1$2$3","$1$3$2","$2$1$3","$2$3$1","$3$1$2","$3$2$1"); #9
      $n[-1]-$n[0]}                                               #10

Try it online!

298 bytes when spaces, newlines and comments are removed.

Lines 1-4 initializes (if not done) the %g and %j hashes where the values are the Gregorian and Julian day numbers accordingly counting from Jaunary 1st 1900 up to December 31st 1999.

Line 5 puts input date into $1, $2 and $3.

Line 9 lists all six permutations of those three input numbers.

Line 8 converts those six into two numbers each, the Gregorian and the Julian day numbers, but only those who are valid dates.

Line 7 makes sure of that, it filters out non existant day numbers.

Line 6 sorts the list of valid date numbers from the smallest to the largest.

Line 10 returns then difference between the last and first (max and min), which was the wanted range.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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