A typescript library for interacting with and modifying both the Maya Long Count (LC) and Calendar Round (CR) dates.
This library supports Node.js versions 20, 22, and 24 (LTS). Development is done on Node 24.
npm install @drewsonne/maya-datesimport { LongCountFactory } from '@drewsonne/maya-dates';
// Long Count from Initial Series on east side of Stele E, Quirigua
const lc = new LongCountFactory().parse('9.17.0.0.0');
console.log(`${lc.buildFullDate()}`);This should print 13 Ajaw 18 Kumk'u 9.17. 0. 0. 0.
Most implementations will consist of creating a fullDate, and passing it to an operation.
Creating a fullDate can be done either by:
- using a factory class (
LongCountFactory,CalendarRoundFactory,FullDateFactory) and calling itsparse(raw_string)function, whereraw_stringis a LC, CR, or CR and LC combination encoded as a string. To specify missing values in a fullDate, using*. For example,
import {
Wildcard,
isWildcard,
LongCountFactory,
CalendarRoundFactory,
FullDateFactory
} from '@drewsonne/maya-dates';
const wildcard = new Wildcard();
const partial_lc = new LongCountFactory().parse('9.17.0.0.*');
const partial_cr = new CalendarRoundFactory().parse('13 Ajaw * Kumk\'u');
const partial_date = new FullDateFactory().parse('13 Ajaw * Kumk\'u 9.17.0.0.*');
console.log(`LC: ${isWildcard(partial_lc.kIn)}`);
console.log(`CR: ${isWildcard(partial_cr.haab.coeff)}`);
console.log(`Full Date: ${isWildcard(partial_date.lc.kIn)}`);- passing explicit values into a model factory or class (
getCalendarRound,LongCount,FullDate).
import {
getCalendarRound,
getHaab,
getTzolkin,
getHaabMonth,
getTzolkinDay,
coefficientParser as _,
FullDate,
LongCount
} from '@drewsonne/maya-dates';
const calendarRound = getCalendarRound(
getTzolkin(_(13), getTzolkinDay('Ajaw')),
getHaab(_(18), getHaabMonth('Kumk\'u'))
);
const lc = new LongCount(0, 0, 0, 17, 9);
const fullDate = new FullDate(calendarRound, lc);
console.log(`${lc}`);
console.log(`${calendarRound}`);
console.log(`${fullDate}`);Once a full date object has been created, you can either add an integer to a fullDate
using shift(number), or filling in missing values in wildcards. The
operations module provides operators to expand a fullDate with a wildcard into all possible
values for dates matching that wildcard pattern.
<<<<<<< HEAD
This version introduces the exports field for better encapsulation and tree-shaking support.
import { LongCountFactory, CalendarRoundFactory, FullDateFactory } from "@drewsonne/maya-dates";// ⚠️ DEPRECATED: Still works for backwards compatibility
import LongCountFactory from "@drewsonne/maya-dates/lib/factory/long-count";
import CalendarRoundFactory from "@drewsonne/maya-dates/lib/factory/calendar-round";Note: Deep imports via /lib/* are supported for backwards compatibility but may be removed in a future major version. Please migrate to named imports from the package root.
- âś… Better tree-shaking (smaller bundle sizes)
- âś… Cleaner, more maintainable code
- âś… Future-proof for ESM support
- âś… Consistent with modern npm package standards =======
Parse different Maya date formats:
import {
LongCountFactory,
CalendarRoundFactory,
FullDateFactory
} from '@drewsonne/maya-dates';
// Parse a Long Count date
const lcFactory = new LongCountFactory();
const longCount = lcFactory.parse('9.17.0.0.0');
// Parse a Calendar Round date
const crFactory = new CalendarRoundFactory();
const calendarRound = crFactory.parse('13 Ajaw 18 Kumk\'u');
// Parse a Full Date (Calendar Round + Long Count)
const fdFactory = new FullDateFactory();
const fullDate = fdFactory.parse('13 Ajaw 18 Kumk\'u 9.17.0.0.0');
console.log(`Long Count: ${longCount}`);
console.log(`Calendar Round: ${calendarRound}`);
console.log(`Full Date: ${fullDate}`);Convert between Gregorian dates and Maya Long Count:
import { LongCount, LongCountFactory } from '@drewsonne/maya-dates';
// Convert from JavaScript Date object
const date = new Date('2012-12-21');
const lc1 = LongCount.fromGregorian(date);
console.log(`Date object = ${lc1}`);
// Output: "Date object = 13. 0. 0. 0. 0"
// Convert from ISO 8601 date string
const lc2 = LongCount.fromGregorian('2012-12-21');
console.log(`ISO 8601 = ${lc2}`);
// Output: "ISO 8601 = 13. 0. 0. 0. 0"
// Alternative: Convert from Julian Day Number directly
const lc3 = LongCount.fromJulianDay(2456283);
console.log(`JDN 2456283 = ${lc3}`);
// Output: "JDN 2456283 = 13. 0. 0. 0. 0"
// Convert Long Count to Gregorian (reverse direction)
const lcFactory = new LongCountFactory();
const longCountDate = lcFactory.parse('13.0.0.0.0');
const gregorian = longCountDate.gregorian;
console.log(`Long Count ${longCountDate} = ${gregorian.day}/${gregorian.month}/${gregorian.year} ${gregorian.era}`);
// Output: "Long Count 13. 0. 0. 0. 0 = 21/12/2012 CE"
// Roundtrip conversion example
const original = lcFactory.parse('9.17.0.0.0');
const asGregorian = original.gregorian;
const backToLC = LongCount.fromJulianDay(asGregorian.julianDay);
console.log(`Original: ${original}, Roundtrip: ${backToLC}`);
// Both will be identical: " 9.17. 0. 0. 0"Available conversion methods:
LongCount.fromGregorian(date | isoString, correlation?)- Convert from Date object or ISO 8601 date string (date only)LongCount.fromJulianDay(jdn, correlation?)- Convert from Julian Day NumberLongCount.fromMayanDayNumber(mdn, correlation?)- Convert from Maya Day NumberlongCount.gregorian- Convert Long Count to Gregorian (getter property)longCount.julianDay- Get Julian Day Number for Long Count (getter property)
Use wildcards to represent partial or unknown dates:
import {
LongCountFactory,
isWildcard,
LongCountWildcard
} from '@drewsonne/maya-dates';
// Create a partial date with a wildcard
const partialLc = new LongCountFactory().parse('9.17.0.0.*');
// Check if a component is a wildcard
if (isWildcard(partialLc.kIn)) {
console.log('The k\'in position is a wildcard');
}
// Expand wildcards to get all possible dates
const wildcard = new LongCountWildcard(partialLc);
const allDates = wildcard.run();
console.log(`Found ${allDates.length} possible dates`);Perform arithmetic operations on Maya dates:
import { LongCountFactory, LongCount, DistanceNumber } from '@drewsonne/maya-dates';
const lc = new LongCountFactory().parse('9.17.0.0.0');
// Add days using plus() with a DistanceNumber
const oneDay = new DistanceNumber(1);
const nextDate = lc.plus(LongCount.fromDistanceNumber(oneDay)).equals();
console.log(`Next day: ${nextDate}`);
// Add multiple days
const manyDays = new DistanceNumber(365);
const futureDate = lc.plus(LongCount.fromDistanceNumber(manyDays)).equals();
console.log(`365 days later: ${futureDate}`);
// Subtract days using minus()
const hundredDays = new DistanceNumber(100);
const pastDate = lc.minus(LongCount.fromDistanceNumber(hundredDays)).equals();
console.log(`100 days earlier: ${pastDate}`);
// Get the full Calendar Round for a Long Count
const fullDate = lc.buildFullDate();
console.log(`Full date: ${fullDate}`);
// Shift Calendar Round dates
const shifted = fullDate.cr.shift(20);
console.log(`Calendar Round shifted by 20 days: ${shifted}`);Support alternative spellings and transliterations of Maya calendar terms:
import {
CalendarRoundFactory,
getI18nManager,
LocaleDefinition
} from '@drewsonne/maya-dates';
// Register alternative spellings
const i18n = getI18nManager();
const altSpellings: LocaleDefinition = {
locale: 'alternative',
name: 'Alternative Spellings',
tzolkinDays: {
'Ajaw': { canonical: 'Ajaw', alternatives: ['Ahau', 'Ajau'] }
},
haabMonths: {
'Kumk\'u': { canonical: 'Kumk\'u', alternatives: ['Kumku', 'Cumku'] }
}
};
i18n.registerLocale(altSpellings);
// Parse dates using alternative spellings
const factory = new CalendarRoundFactory();
const cr1 = factory.parse('4 Ahau 8 Kumku'); // Alternative
const cr2 = factory.parse('4 Ajaw 8 Kumk\'u'); // Canonical
// Both produce the same result
console.log(cr1.toString()); // "4 Ajaw 8 Kumk'u"
console.log(cr2.toString()); // "4 Ajaw 8 Kumk'u"For more details, see I18n Documentation and the i18n example.
If you're upgrading from version 1.x, you'll need to update your import statements.
Before (v1.x):
import LongCountFactory from "@drewsonne/maya-dates/lib/factory/long-count";
import {Wildcard, isWildcard} from "@drewsonne/maya-dates/lib/wildcard";
import {getCalendarRound} from "@drewsonne/maya-dates/lib/cr/calendar-round";After (v2.x):
import {
LongCountFactory,
Wildcard,
isWildcard,
getCalendarRound
} from '@drewsonne/maya-dates';Key Changes:
- All exports are now available from the package root (
@drewsonne/maya-dates) - No need to specify
/lib/paths or know internal directory structure - Factory classes are now named exports instead of default exports
- All class and function exports use named exports for better tree-shaking
Migration Steps:
- Replace all
/lib/...imports with root-level imports - Change default imports to named imports for factory classes
- Group related imports together in a single import statement
- Test your code to ensure all imports resolve correctly
7cb1fe3 (Add modern import examples, Common Patterns section, and runnable examples)
See docs/development/workflow.md for the development workflow and contribution guidelines.
npm run build # Compile TypeScript to JavaScript
npm run build:check # Type check without emitting filesnpm test # Run all tests
npm run test:coverage # Run tests with coverage reportnpm run docs:start # Start Docusaurus dev server
npm run docs:build # Build static documentation site
npm run docs:serve # Serve built documentation locallyFull documentation is built with Docusaurus and includes API reference generated from TypeDoc. You can browse the documentation at https://drewsonne.github.io/maya-dates/.