Find the date that is n days from January 1st 1984

05AB1E, 111 106 103 bytes

•3zb•2ôI5°7*+F©`т‰0Kθ4ÖUD2Qi\28X+ë<7%É31α}‹i®ć>šë®1¾ǝDÅsD12‹i>1ǝë<sθ>ª]εNi”……‚應…ä†ï€¿…Ë…ê†Ä…æ…Ì…Í”#sè

Output as a triplet [day, "Month", year].

Try it online. (Sort of, since it times out for any input above \$\geq-650000\$..)

Explanation:

Since 05AB1E doesn't have any date builtins, I've calculated things manually before. I've used the code of going to the next day from this answer of mine, which in turns also uses the leap year calculation of this answer of mine.

Since this challenge also asks to go back in time, I would need to implement something to go to a previous day as well for negative inputs, which would cost a lot of bytes. Instead, since the challenge input is limited to the range \$[-700000,700000]\$, I used that to my advantage and start going upwards in days from date June 21st, 0067 onward, which is 700,000 days before January 1st, 1984.

In pseudo-code, I therefore do the following steps:

1   Date currentDate = [21,06,0067]
2   Loop input + 700000 amount of times:
3*   currentDate += 1;  # Set currentDate to the next date in line
4   Convert month-integer to month-string and output the result

Where step 3 is divided into substeps:

a   Integer isLeapYear = ...;
b   Integer daysInCurrentMonth = currentDate.month == 2 ?
c                                 28 + isLeapYear
d                                :
e                                 31 - (currentDate.month - 1) % 7 % 2;
f   If(currentDate.day < daysInCurrentMonth):
g     nextDate.day += 1;
h   Else:
i     nextDate.day = 1;
j     If(currentDate.month < 12):
k       nextDate.month += 1;
l     Else:
m       nextDate.month = 1;
n       nextDate.year += 1;

Transforming that into code:

1) Date currentDate = [21,06,0067] is done like this:

•3zb•     # Push compressed integer 210667
     2ô   # Split it into parts of size 2: [21,06,67]
...
©         #  Store the current date in variable `®` (without popping)

2) Loop input + 700000 amount of times: is done like this:

I         # Push the input-integer
 5°       # Push 10 to the power 5: 100000
   7*     # Multiply that by 7: 700000
     +    # Add it to the input-integer
      F   # Loop that many times:

3a) Integer isLeapYear = ...; is done like this:

`         # Pop and push the day, month, and year separated to the stack
 т‰       # Take the divmod of 100
   0K     # Remove all 0s
     θ    # Pop and leave its last value
      4Ö  # And check if it's divisible by 4 (1 if it's a leap year; 0 if not)
        U # Pop and store this in variable `X`

3b) currentDate.month == 2 ? and 3c) 28 + isLeapYear:

D         # Duplicate the month that is still on the stack
 2Qi      # If it's equal to 2 (thus February):
    \     #  Discard the duplicated month from the stack
     28X+ #  And add 28 and `X` (the isLeapYear check) together

3d) : and 3e) 31 - (currentDate.month - 1) % 7 % 2;:

ë         # Else:
 <        #  Month - 1
  7%      #  Modulo-7
    É     #  Is odd (short for modulo-2)
     31α  #  Absolute difference with 31
}         # Close the if-else statement

3f) If(currentDate.day < daysInCurrentMonth)::

‹         # Check if the day that is still on the stack is smaller than this value
 i        # If it is:

3g) nextDate.day += 1;:

®         #  Push the entire date from variable `®` again
 ć        #  Extract its head (the days); pop and push [month,year] and day separated
  >       #  Day + 1
   š      #  Prepend it back in front of the list

3h) Else: and 3i) nextDate.day = 1;:

ë         # Else:
 ®        #  Push the entire date from variable `®` again
  1       #  Push a 1
   ¾      #  Push index 0
    ǝ     #  Insert the 1 at index 0 (to replace the day) in the list `®`

3j) If(currentDate.month < 12)::

D         # Duplicate it
 Ås       # Pop and push the middle item (the month)
   D12‹   # Check if the month is below 12:
       i  # And if it is:

3k) nextDate.month += 1;:

>         # Month + 1
 1        # Push index 1
  ǝ       # Insert the month + 1 at index 1 (to replace the month) in the list `®`

3l) Else:, 3m) nextDate.month = 1; and 3n) nextDate.year += 1;:

ë         # Else:
 <        #  Decrease the month by 1 to 11
  s       #  Swap so list `®` is at the top of the stack again
   θ      #  Pop and push its last item (the year)
    >     #  Year + 1
     ª    #  Convert the 11 to list [1,1] and append the year + 1

4) And finally we convert the month-integer to month-string in the resulting date, and output the result:

]         # Close both if-else statements and the infinite loop
 ε        # Map the resulting date to:
  Ni      #  If the (0-based) index is 1 (thus the month)
    ”……‚應…ä†ï€¿…Ë…ê†Ä…æ…Ì…Í”
          #   Push the dictionary string of months ("December January ... November"
     #    #   Split it on spaces to a list of strings
      s   #   Swap so the month is at the top of the stack
       è  #   And index it into the list (0-based and with wraparound,
          #   which is why "december" is the first item)
          # (after which the resulting date is output implicitly)

See this 05AB1E tip of mine (sections How to use the dictionary? and How to compress large integers?) to understand why •3zb• is 210667 and ”……‚應…ä†ï€¿…Ë…ê†Ä…æ…Ì…Í” is "December January February March April May June July August September October November".


Perl 5, 333 bytes

eval'sub f{my(N,Y,B,C)=@_;X=sub{B=~/4|6|9|11/?30:B!=2?31:Y%400?Y%100?Y%4?28:29:28:29};if(!C){B--;C=&X}if(C>&X){B++;C=1}if(!B){B=12;Y--}if(B>12){B=1;Y++}@_==1?f(N,1984,1,1):N<0?f(N+1,Y,B,C-1):N>0?f(N-1,Y,B,C+1):Y.[qw(January February Mars April May June July August September October November December)]->[B-1].C}'=~s,[NXYBC],\$$&,gr;

Try it online!

Ungolfified:

sub f {
  my($n,$y,$m,$d) = @_;
  my $M = sub{$m=~/4|6|9|11/?30:$m!=2?31:$y%400?$y%100?$y%4?28:29:28:29};
  if( $d==0  ){ $m--; $d=&$M }
  if( $d>&$M ){ $m++; $d=1 }
  if( $m==0  ){ $m=12; $y-- }
  if( $m==13 ){ $m=1; $y++ }
  @_==1 ? f($n,1984,1,1)
 :$n<0  ? f($n+1,$y,$m,$d-1)
 :$n>0  ? f($n-1,$y,$m,$d+1)
 :        "$y-".[qw(January February Mars April May June July August September
                    October November December)]->[$m-1]."-$d"
}

Test:

use Test::More tests=>6;
my @tests = (
    [13318,    '2020-June-18'],
    [2,        '1984-January-3'],
    [656979,   '3782-September-30'],     #was 29
    [-30,      '1983-December-2'],
    [-589001,  '371-May-16'],            #was 15
    [0,        '1984-January-1'] );
for(@tests){
    my($n,$expect) = @$_;
    my $got = f($n);
    is($got, $expect, sprintf"   n=%-12d $expect",$n);
}

Output:

ok 1 -    n=13318        2020-June-18
ok 2 -    n=2            1984-January-3
ok 3 -    n=656979       3782-September-30
ok 4 -    n=-30          1983-December-2
ok 5 -    n=-589001      371-May-16
ok 6 -    n=0            1984-January-1

T-SQL, 333 311 bytes

Added some line feeds to avoid scrolling

DECLARE @ int=0WHILE~@i<724671SELECT 
@+=iif(d=iif(m=2,28-sign(1/~(y%4)*y%25+1/~(y%16)),
31+~m%9%2),iif(m=12,320,51-d),1),@i-=1FROM(SELECT
@/50%18m,@%50d,@/900y)x
PRINT concat(@/900,choose(@/50%18,'january','february',
'march','april','may','june','july','august',
'september','october','november','december'),@%50)

Try it online

Tags:

Date

Code Golf