@drewsonne/maya-dates
Version:
Typescript package to manipulate dates in the Maya Calendar
372 lines (287 loc) • 11.7 kB
Markdown
# @drewsonne/maya-dates

A typescript library for interacting with and modifying both the Maya Long Count (LC)
and Calendar Round (CR) dates.
## Table of Contents
- [Node.js Support](#nodejs-support)
- [Quickstart](#quickstart)
- [Instructions](#instructions)
- [Date Creation](#date-creation)
- [Operations](#operations)
- [Common Patterns](#common-patterns)
- [Parsing Maya Dates](#parsing-maya-dates)
- [Gregorian to Long Count Conversion](#gregorian-to-long-count-conversion)
- [Working with Wildcards](#working-with-wildcards)
- [Date Arithmetic](#date-arithmetic)
- [Alternative Spellings (i18n)](#alternative-spellings-i18n)
- [Migration Guide](#migration-guide)
- [Development](#development)
- [Documentation](#documentation)
## Node.js Support
This library supports Node.js versions 20, 22, and 24 (LTS). Development is done on Node 24.
## Quickstart
```sh
npm install @drewsonne/maya-dates
```
```typescript
import { 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`.
## Instructions
Most implementations will consist of creating a fullDate, and passing it to an operation.
### Date Creation
Creating a fullDate can be done either by:
- using a factory class (`LongCountFactory`, `CalendarRoundFactory`, `FullDateFactory`)
and calling its `parse(raw_string)` function, where `raw_string` is a LC, CR,
or CR and LC combination encoded as a string. To specify missing values in a
fullDate, using `*`. For example,
```typescript
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`).
```typescript
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}`);
```
### Operations
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
## Migration Guide
### Modern Import Syntax
**This version introduces the `exports` field for better encapsulation and tree-shaking support.**
#### Recommended: Named imports from package root
```typescript
import { LongCountFactory, CalendarRoundFactory, FullDateFactory } from "@drewsonne/maya-dates";
```
#### Backwards Compatible (deprecated): Deep imports
```typescript
// ⚠️ 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.
### Benefits of Modern Imports
- ✅ Better tree-shaking (smaller bundle sizes)
- ✅ Cleaner, more maintainable code
- ✅ Future-proof for ESM support
- ✅ Consistent with modern npm package standards
=======
## Common Patterns
### Parsing Maya Dates
Parse different Maya date formats:
```typescript
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}`);
```
### Gregorian to Long Count Conversion
Convert between Gregorian dates and Maya Long Count:
```typescript
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 Number
- `LongCount.fromMayanDayNumber(mdn, correlation?)` - Convert from Maya Day Number
- `longCount.gregorian` - Convert Long Count to Gregorian (getter property)
- `longCount.julianDay` - Get Julian Day Number for Long Count (getter property)
### Working with Wildcards
Use wildcards to represent partial or unknown dates:
```typescript
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`);
```
### Date Arithmetic
Perform arithmetic operations on Maya dates:
```typescript
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}`);
```
### Alternative Spellings (i18n)
Support alternative spellings and transliterations of Maya calendar terms:
```typescript
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](docs/I18N.md) and the [i18n example](examples/i18n.ts).
## Migration Guide
### Upgrading from v1.x
If you're upgrading from version 1.x, you'll need to update your import statements.
**Before (v1.x):**
```typescript
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):**
```typescript
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:**
1. Replace all `/lib/...` imports with root-level imports
2. Change default imports to named imports for factory classes
3. Group related imports together in a single import statement
4. Test your code to ensure all imports resolve correctly
>>>>>>> 7cb1fe3 (Add modern import examples, Common Patterns section, and runnable examples)
## Development
See [docs/development/workflow.md](docs/development/workflow.md) for the development workflow and contribution guidelines.
### Building
```sh
npm run build # Compile TypeScript to JavaScript
npm run build:check # Type check without emitting files
```
### Testing
```sh
npm test # Run all tests
npm run test:coverage # Run tests with coverage report
```
### Documentation
```sh
npm run docs:start # Start Docusaurus dev server
npm run docs:build # Build static documentation site
npm run docs:serve # Serve built documentation locally
```
## Documentation
Full documentation is built with **Docusaurus** and includes API reference generated from TypeDoc.
You can browse the documentation at
[https://drewsonne.github.io/maya-dates/](https://drewsonne.github.io/maya-dates/).