The Art of Time-Keeping, Part 5: Maya Calendar
I can’t believe it’s been a few years since I built the French Republican Calendar web application and explained how it works in a blog post, which is really part of the series on the art of time-keeping. However, I was not exactly satisfied with making a web application for that one calendar when there are so many more interesting calendars in the world.
Thus, I decided to explore a different calendar system with a completely different philosophy—the Maya1 calendar. Whereas the original version of the French Republican Calendar is completely based on astronomy and required calculation of the autumnal equinox, the Maya calendar system is entirely based on math and fixed-length cycles. I thought this would make a lovely contrast in calendar design philosophies.
However, before we dive in deeper, it is important to mention that there isn’t just one “Maya calendar,” but rather three—the Tzolkʼin2, Haabʼ, and the Long Count. These cycles are also not exclusive to the Maya, but are used by many other Mesoamerican cultures, such as the Aztecs, under slightly different names.
All three calendars are used in conjunction to tell the time, as the Tzolkʼin and Haabʼ are both unnumbered cycles. Only the long count can be used to identify an exact calendar date. Separately, there is also the Lords of the Night, which forms a 9-day week cycle.
The finished implementation can be found here.
The Tzolkʼin
We start with the Tzolkʼin, which is a 260-day cycle. It’s used for ritual and divination purposes and also in traditional date-based names, but since this isn’t an anthropology blog, I will not go into the details here.
The Tzolkʼin cycle is constructed from two parallel cycles:
- a day number from 1 to 13, called the trecena cycle; and
- a cycle of 20 day names: Imix, Ikʼ, Akʼbʼal, Kʼan, Chikchan, Kimi, Manikʼ, Lamat, Muluk, Ok, Chuwen, Ebʼ, Bʼen, Ix, Men, Kibʼ, Kabʼan, Etzʼnabʼ, Kawak, and Ajaw.
The cycle goes as follows: 1 Imix, 2 Ikʼ, 3 Akʼbʼal, …, 13 Bʼen, 1 Ix, 2 Men, …, 7 Ajaw, 8 Imix, 9 Ikʼ, …, 12 Kawak, 13 Ajaw. Since the least common multiple of 13 and 20 is 260, the result is a 260-day cycle.
Note that this does not correspond to any astronomical cycle. It is speculated that this is related to the length of a human pregnancy, or that the numbers 13 and 20 are sacred in some way.
Also note that there is no definite start or end to this cycle. It is just as valid to say the cycle starts on 1 Imix as 4 Ajaw.
The Haabʼ
The second Maya calendar cycle is the Haabʼ, which is a 365-day cycle, divided into 18 months of 20 days each. The 5 extra days are placed at the end of the year and called the Wayebʼ, which can be treated as a shorter pseudo-month.
The regular months are named Pop, Woʼ, Sip, Sotzʼ, Sek, Xul, Yaxkʼin, Mol, Chʼen, Yax, Sakʼ, Keh, Mak, Kʼankʼin, Muwan, Pax, Kʼayabʼ, and Kumkʼu. The days of the months start from 0 and go up to 19. So a year would start at 0 Pop, followed by 1 Pop, etc. The month of Pop ends at 19 Pop, which is followed by 0 Woʼ. The pattern continues until 19 Kumkʼu, after which we have 0 Wayebʼ. The last day of the year is 4 Wayebʼ.
Given that a Haabʼ cycle is exactly 365 days, this appears to be the equivalent to a year in our calendar. However, due to the lack of leap days, Haabʼ cycles drift by one day relative to the solar year every four years. It doesn’t appear to have bothered the Maya that much, probably because they live in the tropics, where the seasons aren’t as pronounced.
The Long Count
The final Maya calendar is the Long Count, which uniquely identifies each date
as the number of days since the creation of the current cycle of the world
according to Maya mythology. It is commonly expressed as numbers in a modified
base-20 system. Each “digit” counts up from 0 to 19, except for the second last
“digit,” which is base 18 and goes up to 17 only. Typically, five such “digits”
are used in dates, although theoretically more could be used. When written, the
“digits” are separated by . characters.
For example, the date 13.0.0.0.0 is exactly days, which is around 5125 years, since the creation of the current world according to the Maya.
There are many attempts to correlate the Long Count to the Gregorian calendar that we use daily, but a consensus has emerged around the Goodman–Martinez–Thompson (GMT3) correlation, which places the date of creation at August 11, 3114 BCE in the proleptic Gregorian calendar, which is equivalent to September 6, 3114 BCE in the proleptic Julian calendar.
There are also names for each “digit” or unit of the Long Count. From the least significant to the most significant, they are the kʼin, winal, tun, kʼatun, bʼakʼtun, piktun, kalabtun, kʼinchiltun, alautun, and the hablatun. Given that a 20 hablatuns is around 25.2 billion years, which is longer than the current age of the universe, it doesn’t really make sense to have names for bigger units, though it is mathematically possible to just add more digits.
Note that 13 bʼakʼtuns after the date of creation, i.e. the date 13.0.0.0.0, under the GMT correlation is December 21st, 2012, which has inspired some nonsense. Now that you know how it works, you can see that the Long Count just keeps counting and the bʼakʼtun doesn’t even overflow and reset to 0 on that date.
The Lords of the Night
The Lords of the Night are nine deities in Maya mythology who rule over every ninth night, forming a cycle similar to our concept of weeks. Unfortunately, the Maya names for these nine deities are lost to history, so they are designated numerically from G1 to G9, with G1 ruling over the date of creation.
The Calendar Round
A Calendar Round is a date that gives both the Tzolkʼin and Haabʼ, resulting in a cycle that repeats after 52 Haabʼ years or days, which is . This is the most common way of giving Maya dates.
Building a calendar
Now that we know how the Maya calendar works, we can try building a calendar application around it. While we could simply make a date converter between Maya dates and Gregorian dates, such things have been done many times before. Instead, I wanted to create something similar to the French Republican Calendar webapp, which is month-based, with 5-6 extra days at the end of the year. The Haabʼ cycle resembles that paradigm the most, making it the prime candidate to build a calendar application around.
However, there’s just one problem: Haabʼ cycles are not numbered, and therefore it is meaningless to speak of year numbers, and you can’t display a specific month if you can’t uniquely identify the year.
To resolve this, I decided to create a year numbering system for Haabʼ based on the Maya date of creation. Note that in Maya mythology, the world was created on 4 Ajaw 8 Kumkʼu, which is also the epoch of the Long Count. I decided to call the year containing the date of creation “year 0.” The year number then increases every time 0 Pop, the start of the year, rolls around. This means that year 0 started on 0 Pop before Maya creation, and near the end of the year, on 8 Kumkʼu, the world was created per Maya mythology.
Note that this is not an official system but is something that I invented for this application.
Architecture
Since the main cycle used in the application is the Haabʼ, and we want to show the other two cycles as well as the Gregorian date, a lot of conversion is required. To ease such conversions, it makes sense to have a linear count of days since an epoch internally. For this reason, it might make sense to use the Long Count converted to an integer for this purpose, if it weren’t for the concept of Julian Day Numbers (JDNs), which were introduced in part 1. Since the French Republican Calendar already implements all the conversions with JDNs, it makes sense to keep using JDNs for the Maya calendar.
Note that the Maya date of creation according to the GMT correlation is JDN , which is the offset we must add to any integer Long Count date to get the JDN.
Conversions
Now, let’s look at some of the conversions that we have to do in the calendar application.
Converting JDN to Tzolkʼin
Since the Tzolkʼin is really a 13-day cycle of numbers and a 20-day cycle of day names, a bit of modular arithmetic, plus an adjustment offset, is sufficient.
Specifically, given the JDN , the day number and the day name (where 0 is Imix) can be calculated as follows:
In a C-like language that has the modulo operation return the sign of the
dividend, the day number can be expressed as (jdn % 13 + 18) % 13 + 1 and the
day name index can be expressed as (jdn % 20 + 36) % 20.
Or in Python:
def jdn_tzolkin(jdn: int) -> (int, int):
return ((jdn + 5) % 13 + 1, (jdn + 16) % 20)
Converting JDN to Haabʼ
Since the Haabʼ is a 365-day cycle, a bit of modular arithmetic, plus an adjustment offset , can get us the offset into the cycle. We can then compute for the month number, with 0 for Pop and 18 for Wayebʼ, and the day number from the JDN as follows:
The Haabʼ year number can be calculated as:
Note that is the JDN for 0 Pop in “year 0.”
Converting Haabʼ to JDN
To reverse the operation, we can simply do:
Converting Long Count
As mentioned before, the Long Count as an integer is just JDN minus , so adding and subtracting the offset is enough.
For display purposes, the integer needs to be converted to modified base-20. The following TypeScript function is what the application actually uses to convert JDN to Long Count for display:
export function jdnLongCount(jdn: number): LongCount | null {
let z = jdn - 584283;
if (z < 0)
return null;
const parts = [z % 20, Math.floor(z / 20) % 18];
z = Math.floor(z / 360);
while (z > 0) {
parts.push(z % 20);
z = Math.floor(z / 20);
}
while (parts.length < 5) {
parts.push(0);
}
return parts.reverse();
}
And the reverse operation:
function longCountJDN(longCount: LongCount): number {
const state = [...longCount];
const last = (state.pop() ?? 0) + (state.pop() ?? 0) * 20;
let sum = 0;
state.forEach((value) => sum = sum * 20 + value);
return sum * 360 + last + 584283;
}
Note that I’ve chosen to not try to represent dates before the Maya creation.
JDN to Gregorian
The algorithms for these are given in part 1, so I will not repeat them here.
Navigation
With the conversion functions, it should be possible to build most of the functions of the calendar. However, since the Tzolkʼin is a very important cycle for the Maya, it would make sense to have a way to find the previous and next occurrence of a certain Tzolkʼin date.
Note that the Tzolkʼin cycles are not numbered, and they don’t even have a
well-defined start of the cycle, so it doesn’t really make sense to add a cycle
number for the Tzolkʼin. Instead, we would like to define two
functions—prev_tzolkin and next_tzolkin—to compute the previous and next
occurrence of a given Tzolkʼin date before or after a certain JDN, respectively.
However, before we can do that, we first need to know the position of a given Tzolkʼin date in the 260-day cycle.
Tzolkʼin cycle offset
To determine the position for the Tzolkʼin date with the day number and the day name (where 0 is Imix), we can express this as a system of linear congruences. Before we do that, let’s define to make it easier to work with. Then, we construct the system:
Since , the divisors are pairwise coprime. This means that we can invoke the Chinese remainder theorem, which states that there is a unique solution for modulo .
We can now solve for this unique solution:
- Express as for some .
- Substitute into to obtain .
- Rearrange to obtain .
- Note that . Therefore, we can multiply both sides by 17 to obtain . We note that , so we can reduce this to .
- Express this as for some .
- Substitute this back into to obtain , which we can expand to .
- Convert this back into the congruence .
Therefore, Tzolkʼin cycle offset .
In Python code:
def tzolkin_offset(num: int, index: int) -> int:
return (40 * (num - 1) + 221 * index) % 260
prev_tzolkin implementation
We basically compute the offset of the given JDN in the Tzolkʼin cycle, determine how many days after the desired position that day is, and subtract that many days:
def prev_tzolkin(jdn: int, num: int, index: int) -> int:
current = tzolkin_offset(*jdn_tzolkin(jdn))
desired = tzolkin_offset(num, index)
return jdn - (current - desired) % 260
next_tzolkin implementation
We basically compute the offset of the given JDN in the Tzolkʼin cycle, determine how many days before the desired position that day is, and add that many days:
def next_tzolkin(jdn: int, num: int, index: int) -> int:
current = tzolkin_offset(*jdn_tzolkin(jdn))
desired = tzolkin_offset(num, index)
return jdn + (desired - current) % 260
Conclusion
Hopefully, this post helped you understand how the three Maya calendars work together, along with the Lords of the Night. I hope this helps you understand how it all works as you play around with my implementation of the calendar. Of course, with the information here, you should also be able to construct your own implementation, in theory.
Given how long this took, I doubt the next installment in this series will come soon, but it probably will be about the Chinese calendar, which has the complete opposite design philosophy compared to the Maya. Whereas the Maya used fixed cycles that slowly desynchronize from the solar year, the Chinese calendar is a reflection of the burning desire to perfectly track the motions of the sun and the moon, even at the cost of making it difficult to calculate dates in advance.
Notes
-
Apparently, in English, “Maya” simultaneously considered the preferred singular, plural, and adjective form. The only exception is in linguistics, where it’s conventional to speak of “Mayan languages.” ↩
-
The ʼ symbol in the word Tzolkʼin and Haabʼ is the “modifier letter apostrophe,” which is in Mayan orthography to modify the prevoius consonant. It’s mostly used to indicate ejective consonants, except for bʼ, which indicates a voiced bilabial implosive. After vowels, ʼ indicates the glottal stop. ↩
-
Note that the GMT correlation is based on the names of three people and has nothing to do with the so-called Greenwich Mean Time, which is an ambiguous term often used as an alias for UTC. ↩