8
\$\begingroup\$

This challenge is a relatively simple concept that I'm surprised has not been done yet. You will be given only an integer as an input. Your task is to find the date that is this number of days from January 1st, 1984. This challenge is uniquely for the Gregorian calendar (which is standard almost everywhere).

One restriction

The twist on this challenge is that you should not use a built-in function that deals with the date and time - because then this challenge would be trivial for a few languages. Your program must do this calculation entirely on its own - keep in mind leap years exist and have special rules:

  • every year that is a multiple of four is a leap year
  • the exception to the above rule is any year that is a multiple of 100 but not a multiple of 400

Output format

You must output the year, month, and day of the month in any order you want so long as the number for the year and day of the month are not conjoined. You can use any character (not numeric or alphabetical), or none, as a separator. The name of the month must be given as a word instead of a number. Examples for this format are shown in the test cases.

Input

You will be given an integer as an input. You can assume this integer will be smaller in magnitude than 700000 - this means negative integers are also accepted.

Test cases

input | accepted output (which is relatively flexible)

13318 | June 18 2020

2 | JanUaRy03(1984)

656979 | 3782september29

-30 | DECEMBER 02 1983

-589001 | 00371may15 (leading zeroes may be used)

0 | 1984 1 january

Scoring

This is . Shortest code wins!

\$\endgroup\$
7
\$\begingroup\$

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".

\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
3
\$\begingroup\$

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

\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 330 300 bytes

a=>{for(n=a<(d=m=0),y=1984,a*=n?-1:1;a-->0;){s="3"+((y%400)?((y%100)?((y%4)?(0):1):0):1)+"3232332323";v=27+(+s[m]);if(n&&!d--){m=m?(m-1):11;m==11&&y--;d=v}if(!n&&d++==v){d=0;m=m^11?(m+1):0;!m&&y++;}};return y+['January','February','March','April','May','June','July','August','September','October','November','December'][m]+(d+1)}

Try it online!

String improvement (and removed parentheses):

a=>{for(n=a<(d=m=0),y=1984,a*=n?-1:1;a-->0;){v=27+(+("3"+(y%400?(y%100?(y%4?0:1):0):1)+3232332323)[m]);if(n&&!d--){m=m?m-1:11;m==11&&y--;d=v}if(!n&&d++==v){d=0;m=m^11?m+1:0;!m&&y++;}}return y+"January February March April May June July August September October November December".split(" ")[m]+(d+1)}

Try it online!

Tests:

console.log(f(13318))
console.log(f(2))
console.log(f(656979))
console.log(f(-30))
console.log(f(-589001))
console.log(f(0))

Results:

2020June18
1984January3
3782September30
1983December2
371May16
1984January1

Ungolfified:

a=>{
    for(n=a<(d=m=0),y=1984,a*=n?-1:1;a-->0;){
        v=27+(+("3"+(y%400?(y%100?(y%4?0:1):0):1)+3232332323)[m]);
        if(n&&!d--){
            m=m?m-1:11;
            m==11&&y--;
            d=v
        }
        if(!n&&d++==v){
            d=0;
            m=m^11?m+1:0;
            !m&&y++;
        }
    }
return y+"January February March April May June July August September October November December".split(" ")[m]+(d+1)
}
\$\endgroup\$
  • \$\begingroup\$ Is there something I can do with the hard-coded ['January','February','March','April','May','June','July','August','September','October','November','December'] ? \$\endgroup\$ – SomoKRoceS Jun 24 '20 at 22:27

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.