Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 28 additions & 33 deletions src/__tests__/lc/western/western.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ const dates: MockDateCorrelation[] = [
new MockDateCorrelation('12.4.2.11.8', '28/2/1700 CE', '18/2/1700 CE', 2342031, 1757748),
new MockDateCorrelation('12.1.1.1.1', '21/6/1639 CE', '11/6/1639 CE', 2319864, 1735581),
new MockDateCorrelation('11.20.1.1.1', '4/10/1619 CE', '24/9/1619 CE', 2312664, 1728381),
new MockDateCorrelation('11.18.3.9.18', '15/10/1582 CE*', '15/10/1582 CE*', 2299161, 1714878), // Julian to Gregorian Switch
new MockDateCorrelation('11.18.3.9.17', '4/10/1582 CE*', '4/10/1582 CE*', 2299160, 1714877), // Julian to Gregorian Switch
new MockDateCorrelation('11.18.3.9.18', '15/10/1582 CE', '15/10/1582 CE', 2299161, 1714878), // Julian to Gregorian Switch
new MockDateCorrelation('11.18.3.9.17', '4/10/1582 CE', '4/10/1582 CE', 2299160, 1714877), // Julian to Gregorian Switch
new MockDateCorrelation('11.17.10.1.1', '28/6/1569 CE', '18/6/1569 CE', 2294304, 1710021),
new MockDateCorrelation('11.16.1.1.1', '27/11/1540 CE', '17/11/1540 CE', 2283864, 1699581),
new MockDateCorrelation('11.15.1.1.1', '12/3/1521 CE', '2/3/1521 CE', 2276664, 1692381),
Expand Down Expand Up @@ -75,21 +75,14 @@ describe('long-count to gregorian/julian', () => {
describe('gregorian to longcount', () => {
const gregorianFactory = new GregorianFactory();
dates.forEach((dc) => {
// Skip test cases where GregorianFactory offset calculation needs refinement
// See PR #54 for work-in-progress note
const skipCases = ['6.0.0.0.0', '11.18.3.9.18', '11.18.3.9.17', '7.1.1.1.1', '6.1.1.1.1'];
if (skipCases.includes(dc.lc)) {
it.skip(`g(${dc.gregorian}) -> correct date representation (skipped: offset calculation needs refinement)`);
} else {
it(`g(${dc.gregorian}) -> correct date representation`, () => {
const g = gregorianFactory.parse(dc.gregorian);
// Verify that the parsed date matches the expected Gregorian date string
// The toString() method should return the same format as the input (without asterisk if not threshold)
const expectedDate = dc.gregorian.replace('*', '').trim();
const actualDate = `${g}`.trim();
expect(actualDate).to.eq(expectedDate);
});
}
it(`g(${dc.gregorian}) -> correct date representation`, () => {
const g = gregorianFactory.parse(dc.gregorian);
// Verify that the parsed date matches the expected Gregorian date string
// The toString() method should return the same format as the input (without asterisk if not threshold)
const expectedDate = dc.gregorian.replace('*', '').trim();
const actualDate = `${g}`.trim();
expect(actualDate).to.eq(expectedDate);
});
});
});

Expand Down Expand Up @@ -122,17 +115,19 @@ describe('longcount to mayadate', () => {

describe('JSON Dataset Correlation Tests', () => {
const jsonGmtData = getGMTCorrelationData();

describe('Direct source correlations validation', () => {
const directSourceData = getDirectSourceData().slice(0, 5); // Test first 5 for performance
const directSourceData = getDirectSourceData();

directSourceData.forEach((correlation: CorrelationData) => {
it(`should validate ${correlation.maya_long_count} from source data`, () => {
const lc = lcFactory.parse(correlation.maya_long_count).setCorrelationConstant(corr);

// Use the correlation constant from the JSON data, not the hardcoded GMT
const correlationConstant = getCorrelationConstant(correlation.correlation_jdn);
const lc = lcFactory.parse(correlation.maya_long_count).setCorrelationConstant(correlationConstant);

// Validate the Long Count parses correctly
expect(lc).to.not.equal(null);

// This is a basic test - you may need to adjust date format comparison
// based on how your library formats dates vs the JSON ISO format
if (correlation.western_calendar === 'gregorian') {
Expand All @@ -145,21 +140,21 @@ describe('JSON Dataset Correlation Tests', () => {
});
});

describe('Sample correlations from JSON dataset', () => {
// Test a few correlations from the comprehensive dataset
const sampleData = jsonGmtData
.filter(d => d.western_calendar === 'gregorian')
.slice(0, 10); // Test first 10 for performance

sampleData.forEach((correlation: CorrelationData) => {
describe('Gregorian calendar correlations from JSON dataset', () => {
// Filter to only Gregorian calendar dates
const gregorianData = jsonGmtData.filter(d => d.western_calendar === 'gregorian');

gregorianData.forEach((correlation: CorrelationData) => {
it(`should process ${correlation.maya_long_count} -> ${correlation.western_date}`, () => {
const lc = lcFactory.parse(correlation.maya_long_count).setCorrelationConstant(corr);

// Use the correlation constant from the JSON data
const correlationConstant = getCorrelationConstant(correlation.correlation_jdn);
const lc = lcFactory.parse(correlation.maya_long_count).setCorrelationConstant(correlationConstant);

// Basic validation that the Long Count parses and produces a date
expect(`${lc.gregorian}`).to.be.a('string');
expect(lc.julianDay).to.be.a('number');
expect(lc.getPosition()).to.be.a('number');

// Extract year for comparison (adjust format as needed)
const expectedYear = correlation.western_date.split('-')[0];
const gregorianDate = `${lc.gregorian}`;
Expand Down
42 changes: 22 additions & 20 deletions src/factory/gregorian.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class GregorianFactory {
parse(gregorian: string): GregorianCalendarDate {
// Clean the input string - remove all asterisks and era markers
let cleanedGregorian = gregorian.replace(/\*/g, '').trim();

// Determine era (BCE or CE)
let isBCE: boolean = false;
let searchString: string = '';
Expand All @@ -38,18 +38,18 @@ export default class GregorianFactory {
isBCE = false;
searchString = 'CE';
}

// Remove era markers if present
if (searchString) {
cleanedGregorian = cleanedGregorian.replace(` ${searchString}`, '').replace(searchString, '').trim();
}

// Validate basic format: expect three slash-separated numeric components (day/month/year)
const rawParts = cleanedGregorian.split('/');
if (rawParts.length !== 3) {
throw new Error(`Invalid Gregorian date format: "${gregorian}". Expected format: DD/MM/YYYY`);
}

const dateParts: number[] = rawParts.map((part, index) => {
const trimmed = part.trim();
if (trimmed.length === 0) {
Expand All @@ -61,12 +61,12 @@ export default class GregorianFactory {
}
return value;
});

// dateParts[0] = day, dateParts[1] = month, dateParts[2] = year
const day = dateParts[0];
const month = dateParts[1];
const year = dateParts[2];

// Validate date component ranges
if (month < 1 || month > 12) {
throw new Error(`Month out of range in Gregorian date "${gregorian}": ${month}. Expected 1-12`);
Expand All @@ -77,14 +77,16 @@ export default class GregorianFactory {
if (year === 0) {
throw new Error(`Year zero is not valid in Gregorian date "${gregorian}"`);
}

// Convert year to negative for BCE dates
const adjustedYear = isBCE ? -year : year;

// BCE dates use astronomical year numbering: 1 BCE = year 0, 2 BCE = year -1, etc.
// So for BCE year X, the astronomical year is 1 - X
const adjustedYear = isBCE ? (1 - year) : year;

// Convert Gregorian date to julian day using moonbeams
// moonbeams.calendarToJd returns a julian day for the given calendar date
const targetJd = Math.ceil(moonbeams.calendarToJd(adjustedYear, month, day));

// The GregorianCalendarDate stores a base julian day, and when accessing the date property,
// it applies an offset: date = jdToCalendar(storedJd + offset(storedJd))
// We need to find storedJd such that: jdToCalendar(storedJd + offset(storedJd)) = our Gregorian date
Expand All @@ -94,11 +96,11 @@ export default class GregorianFactory {
let storedJd = targetJd;
let iterations = 0;
const maxIterations = 10;

while (iterations < maxIterations) {
// Calculate offset for current storedJd
const offset = storedJd === 2299160 ? 0 :
storedJd <= 1448283 ? -8 :
storedJd <= 1448283 ? -8 :
storedJd <= 1455864 ? -8 :
storedJd <= 1599864 ? -5 :
storedJd <= 1743864 ? -2 :
Expand All @@ -108,37 +110,37 @@ export default class GregorianFactory {
storedJd <= 2175864 ? 7 :
storedJd <= 2240664 ? 9 :
storedJd <= 2299160 ? 10 : 0;

// Check if we've converged
if (storedJd + offset === targetJd) {
break;
}

// Adjust storedJd: we want storedJd + offset = targetJd
// So: storedJd = targetJd - offset
storedJd = targetJd - offset;
iterations++;
}

// Verify the result produces the correct date
const temp = new GregorianCalendarDate(storedJd);
const calculatedDate = temp.date;
const calculatedDay = Math.floor(calculatedDate.day);
const calculatedMonth = calculatedDate.month;
const calculatedYear = calculatedDate.year;
const targetYear = isBCE ? Math.abs(adjustedYear) : adjustedYear;
const targetYear = year; // Use the original year from input
const calcYearForBCE = calculatedYear < 0 ? Math.abs(calculatedYear - 1) : calculatedYear;

// If the date doesn't match, there might be an issue with the offset calculation
// In that case, we'll use the targetJd directly and let the offset be applied
if (calculatedDay !== day ||
calculatedMonth !== month ||
if (calculatedDay !== day ||
calculatedMonth !== month ||
(isBCE ? calcYearForBCE !== targetYear : calculatedYear !== targetYear)) {
// Fallback: store targetJd directly
// The offset will adjust it when converting to calendar date
storedJd = targetJd;
}

return new GregorianCalendarDate(storedJd);
}
}
9 changes: 2 additions & 7 deletions src/lc/western/western.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,10 @@ export default abstract class WesternCalendar {
}

/**
* Represent this date as a string with era markers. If the date is suffixed with
* a '*', this date is on the Julian/Gregorian threshold date.
* Represent this date as a string with era markers.
* @return {string}
*/
toString() {
const date = `${this.day}/${this.month}/${this.year} ${this.era}`;
if (this.isThreshold()) {
return `${date}*`;
}
return date;
return `${this.day}/${this.month}/${this.year} ${this.era}`;
}
}