What's the Mayan date?

Just over seven years ago, everybody suddenly stopped talking about the Maya people. It is time to rectify that!

For clarification, I am talking about the Mesoamerican Long Count Calendar. Your program will have as input a date in the Gregorian Calendar, and as output the corresponding date from the aforementioned Mesoamerican calendar.

That calendar counts days since August 11, 3114 BCE. It divides that number of days into periods of different lengths. There's the single day, the Winal (20 days), the Tun (18 Winal, or 360 days), the K'atun (20 Tun, 7200 days), and the B'ak'tun (20 K'atun, 144 000 days).

So you have your number of days since the epoch, then you find out how many of each period fit in there. Let's take William the Conqueror's coronation date of December 25, 1066. We're not bothering with the Julian Calendar here - we're using the Proleptic Gregorian Calendar for dates in the distant past (including the Maya epoch): basically the system of leap days every four years, except years divisible by 100 but not by 400, which I trust you're all familiar with.

Between Christmas Day in 1066 and the Maya epoch date of August 11, 3114 BCE, there's 1526484 days. In that number, you can fit 10 periods of 144000 days, so that's our B'ak'tun. The remainder, 86484, can fit 12 periods of 7200 days, so that's the K'atun. The remainder is 84. That's less than 360, so our Tun is 0. You can fit 4 Winal though, leaving a remainder of 4 days.

Now concatenate all those numbers with full stops in between, in decreasing order of period length. The date of 25 December 1066 becomes 10.12.0.4.4.

Specification

Your program will take as input a date in the Gregorian Calendar, between January 1, 1 AD (7.17.18.13.3), and 31 December, 3999 AD (18.0.15.17.6). The date can be in any format you want (my test cases will be using ISO 8601) as long as it is in the Gregorian calendar.

That means, valid input formats include (but are not limited to) "22 MAR 2233", "7/13/2305", "20200107T000000Z", and "2020-01-07T00:00:00+00:00", even an array like "[7], [1], [2020]", depending on what your language parses the most easily or what approach you want to use. It is not allowed however to include any information beyond year, month, day of the month, time (has to be 00:00:00) and time zone (has to be GMT). Once I start allowing the day of the year, the Unix timestamp, a full-fledged DateTime data object (which contains a host of other values) or some other date tracking scheme, this issue would no longer be about converting a Gregorian date into a Mayan date, but only about representing a Mayan date. So basically, just stick with a primitive or a list of primitives for input.

It will output a string containing the corresponding Mayan date, as discussed above. It has to be a single string of numbers separated by periods; an int array is not allowed. Using a built-in Maya converter in your language (though I would be surprised if it had one) is not permitted either.

The question is tagged , so the shortest program in byte count wins!

Test cases

Gregorian; Mayan
1-1-1; 7.17.18.13.3
1-1-2; 7.17.18.13.4
1066-12-25; 10.12.0.4.4
2000-2-29; 12.19.7.0.1
2012-12-21; 13.0.0.0.0
2020-1-5; 13.0.7.2.11
2100-3-1; 13.4.8.8.6
2154-4-11; 13.7.3.6.10
3999-12-31; 18.0.15.17.6

• Some languages provide a Date or DateTime type which support both Unix-timestamp and getting year / month / day. Will it be valid input format? Or must I use a string or list of 3 integers? – tsh Jan 7 '20 at 9:50
• 2100-3-1 test case is incorrect. Output should be 13.4.8.8.6 according to Wolfram. – Grimmy Jan 7 '20 at 13:26
• xkcd was right! – flawr Jan 7 '20 at 14:45
• There really shouldn't be any 0th stuff in there. We don't have 0 AD or the 0th month or the 0th day, but the challenge might be more fun this way (more suited to programming). – John Hamilton Jan 8 '20 at 7:11
• @John the Gregorian calendar doesn't, but the Mayan one certainly does. Remember how that date in 2012 was 13.0.0.0.0? – KeizerHarm Jan 8 '20 at 7:33

05AB1E, 54 52 bytes

365*Š3‹¹α4т‚DPª÷®β•ë˜¿•ºS₂+²£•HØ•OŽQív₂y-‰R})R'.ý


Try it online!

First step: compute the day number.

365                # literal 365
*               # multiply
#  => 365*year is left on the stack for now
Š                  # get the other two inputs
#  => day is left on the stack
3‹                 # is month less than 3? (returns 0 or 1)
¹α               # absolute difference with year
4              # literal 4
т             # 100
‚            # pair the two: [4, 100]
DP          # product of (a copy of) the pair: 400
ª         # append it to to the pair: [4, 100, 400]
÷        # int divide the year by each of those numbers
®β      # convert from base -1: y/4 - y/100 + y/400
#  => number of leap days is left on the stack
•ë˜¿•              # compressed integer 15254545
º             # mirrored: 1525454554545251
S            # split to a list of digits
₂+          # add 26 to each: [27, 31, 28, 31, 30, ...]
²£        # month first values of that list
# dump all on the stack
#  => days in previous months is left on the stack
•HØ•              # compressed integer 1136750
#  => gregorian-mayan offset is left on the stack
O                  # sum the stack, giving the # of days since 0.0.0.0.0


Second step: convert that number to the appropriate notation.

ŽQí                # compressed integer 6866
v      }        # for each digit:
₂y-            #  26 - digit (20, 18, 20, 20)
‰R         #  divmod, reverse, then dump
#  (pushes the mod then the div)
)       # wrap the stack in an array
R      # reverse
'.ý   # join by "."

• Well done! I'm mildly curious why you need to define 365, but not any of the other values involved :O – KeizerHarm Jan 7 '20 at 13:34
• @KeizerHarm I do define all the values involved, but 05AB1E has compressed integer literals. However, integers larger than 356 need 3 bytes in their compressed form, so a plain 365 is just as short. – Grimmy Jan 7 '20 at 13:37
• @KeizerHarm I see loads of integers: 365; 3; 4; т(100); ®(-1); •ΘÏF•(5254545); ₂(26); •Hó•(1136777); ŽQí(6866); ₂(26). ;) See this 05AB1E tip of mine if you want to understand how the compressed integers work. And the 26 and 100 are single-byte builtins. Nice answer Grimmy. Out of curiosity, how is the Base -1 used here after you've integer-divided by [4,100,400]? – Kevin Cruijssen Jan 7 '20 at 13:48
• @KevinCruijssen [a,b,c] converted from base n is a * n² + b * n + c. For n = -1, that gives a - b + c. y/4 - y/100 + y/400 is the number of leap days. – Grimmy Jan 7 '20 at 13:53
• @KeizerHarm The part that counts days in previous months is based partly on this answer. The rest is all new. – Grimmy Jan 8 '20 at 9:09

Python 3, 149 $$\\cdots\$$ 145 140 bytes

from datetime import*
def f(d):
n,s=(date(*d)-date(1,1,1)).days+1137143,""
for i in[144000,7200,360,20,1]:s+=f".{n//i}";n%=i
return s[1:]


Try it online!

Takes Gregorian date as [year, month, day] list and returns Mayan date as f"{B'ak'tun}.{K'atun}.{Tun}.{Winal}.{Day}"(loosely as a python f-string).

• This has an int array as output. I did specifically say that the output had to be a string of the ints concatenated with periods. – KeizerHarm Jan 7 '20 at 12:38
• @KeizerHarm Ah, sorry missed that. Will fix. – Noodle9 Jan 7 '20 at 12:40
• I'll have to look up what an f-string is, but this works for me :) – KeizerHarm Jan 7 '20 at 12:48
• @KeizerHarm f-strings combine "string".format() with in-string evaluation of expressions. And it turns out they're are useful for golfing too - just saved 3 bytes with them! :-) – Noodle9 Jan 7 '20 at 14:28
• I forgot to include the ping for @KeizerHarm's comment, sorry – Jo King Jan 8 '20 at 9:21

JavaScript (ES6), 96 bytes

Assumes that the system is set up to UTC time (as the TIO servers are)

Takes input as 3 distinct parameters (year, month, day).

(y,m,d)=>[144e3,7200,360,20,1].map(k=>n/(n%=k,k)|0,n=new Date(y+4e3,m-1,d+395335)/864e5).join.


Try it online!

How?

We can't safely pass a year $$\y<100\$$ to the Date constructor, as it gets interpreted as $$\1900+y\$$. To work around this issue, we add $$\4000\$$ to the year. We make sure to use a multiple of $$\400\$$ so that we have the same properties regarding leap years.

The remaining number of days to reach Epoch from August 11, 3114 BCE + 4000 years is $$\395335\$$.

JavaScript (Node.js), 74 bytes

d=>(i=5,g=(a,m=20-8%--i)=>i?g(a/m|0)+'.'+a%m:a)(new Date(d)/864e5+1856305)


Try it online!

Input as yyyy-MM-dd format string.

Thanks Arnauld, save 4 bytes.

C# (Visual C# Interactive Compiler), 186 163 bytes

string f(int[]t){int x,n=(int)(new DateTime(t[0],t[1],t[2])-new DateTime(1,1,1)).TotalDays+1137143;return string.Join(".",new[]{144000,7200,360,20,1}.Select(e=>{x=n/e;n%=e;return x;}));}


Try it online!

Edit: Use int[] as input instead of DateTime to meet the specification better. Thanks @keizerharm for the hint

• You're using a DateTime object as input. I think I was possibly being too vague with the specification, but this goes against the idea of "the input may only contain day, month, year information". A DateTime contains a lot more than that. It, for example, allows direct subtraction of one such object from another. – KeizerHarm Jan 7 '20 at 13:17
• Make the input three ints and move the initialisation of the DateTime to be inside the method based on that input, and it'll be alright. – KeizerHarm Jan 7 '20 at 13:19
• Yep, that works! – KeizerHarm Jan 7 '20 at 13:36
• 168 bytes – tsh Jan 8 '20 at 8:44

C (GCC) 239 200

-39 bytes ceilingcat

#define S d/m;d%=m;m
l(y){y=y%(y%25?4:16)<1;}b,k,t;D(y,m,d){for(d+=1137110+L" ?[z˜·ÕôēıŐŮ"[m-1]+(m>2?l(y):0);--y;d+=365+l(y));m=144e3;b=S=7200;k=S=360;t=S=20;y=S=1;printf("%d.%d.%d.%d.%d",b,k,t,y,d);}


Try it online!

• @ceilingcat Nice trick with the string literal for the array. What does the z prefix specify? I can't find it in any documentation? – rtpax Jan 8 '20 at 20:09
• If you mean L"..." prefix, it denotes a string of wide chars that happen to be the same length as ints on TIO. – ceilingcat Jan 8 '20 at 20:37
• Ah, I got confused looking at it and thought the z~ was a z" and I didn't notice the L"` earlier – rtpax Jan 8 '20 at 21:19