Skip to content

Commit

Permalink
feat: add support for comprehensive date conversion
Browse files Browse the repository at this point in the history
This commit starts using the mapping of Japanese era dates
to Gregorian calendar dates, enabling comprehensive date
conversion.

It includes accurate calculation of the time span of years and
months for periods predating the adoption of the Gregorian
calendar in Japan.
Additionally, day-to-day conversion of Japanese era dates
to Gregorian calendar dates was added.
  • Loading branch information
enellis authored and birtles committed Dec 9, 2024
1 parent 6c0164d commit dd4ea94
Show file tree
Hide file tree
Showing 16 changed files with 1,090 additions and 377 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ app.

## [Unreleased]

- Added precise conversion of Japanese era dates, including those preceding
the adoption of the Gregorian calendar in Japan, with accurate calculations
for the timespan of specific years and months
(e.g. 元治元年, 慶応4年閏4月, 明治5年12月2日)
([#2093](https://github.com/birchill/10ten-ja-reader/pull/2093)).
- Fixed sorting of word results
(see [#1966](https://github.com/birchill/10ten-ja-reader/issues/1966#issuecomment-2426690974)).
- (Chrome) Fixed language code for Chinese localization
Expand Down
4 changes: 4 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@
"message": "Dictionary data is updating, incomplete results shown.",
"description": "Status bar message shown while the dictionary is being updated and only a snapshot can be shown."
},
"content_era_info_invalid": {
"message": "Invalid",
"description": "Message shown when we fail to calculate the corresponding Gregorian date of a pre-Gregorian era"
},
"content_kanji_base_radical": {
"message": "from $char$ (​$readings$​)",
"description": "The string used to describe the base radical",
Expand Down
4 changes: 4 additions & 0 deletions _locales/ja/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@
"message": "辞書データを更新中のため、一部のデータのみ表示されています。",
"description": "Status bar message shown while the dictionary is being updated and only a snapshot can be shown."
},
"content_era_info_invalid": {
"message": "該当なし",
"description": "Message shown when we fail to calculate the corresponding Gregorian date of a pre-Gregorian era"
},
"content_kanji_base_radical": {
"message": "部 $char$ (​$readings$​)",
"description": "The string used to describe the base radical",
Expand Down
4 changes: 4 additions & 0 deletions _locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@
"message": "由于词典数据正在更新,当前显示的数据不完整。",
"description": "Status bar message shown while the dictionary is being updated and only a snapshot can be shown."
},
"content_era_info_invalid": {
"message": "无结果",
"description": "Message shown when we fail to calculate the corresponding Gregorian date of a pre-Gregorian era"
},
"content_kanji_base_radical": {
"message": "从​$char$​(​$readings$​)",
"description": "The string used to describe the base radical",
Expand Down
4 changes: 4 additions & 0 deletions rspack.config.cosmos.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ const config = {
react: 'preact/compat',
'react-dom/test-utils': 'preact/test-utils',
'react-dom': 'preact/compat',
'webextension-polyfill': path.resolve(
__dirname,
'tests/browser-polyfill.ts'
),
},
extensions: ['.ts', '.tsx', '.js'],
},
Expand Down
6 changes: 6 additions & 0 deletions src/background/background-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export const BackgroundRequestSchema = discriminator('type', {
}),
searchWords: SearchRequestSchema,
searchOther: SearchOtherRequestSchema,
calculateEraDateTimeSpan: s.type({
era: s.string(),
year: s.number(),
month: s.optional(s.number()),
day: s.optional(s.number()),
}),
toggleDefinition: s.type({}),
translate: s.type({
input: s.string(),
Expand Down
5 changes: 5 additions & 0 deletions src/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
SearchRequest,
} from './background-request';
import { setDefaultToolbarIcon, updateBrowserAction } from './browser-action';
import { calculateEraDateTimeSpan } from './calculate-date';
import { registerMenuListeners, updateContextMenus } from './context-menus';
import { FxFetcher } from './fx-fetcher';
import { isCurrentTabEnabled } from './is-current-tab-enabled';
Expand Down Expand Up @@ -590,6 +591,10 @@ browser.runtime.onMessage.addListener(
}
})();

case 'calculateEraDateTimeSpan':
Bugsnag.leaveBreadcrumb('Calculating era date time span', request);
return Promise.resolve(calculateEraDateTimeSpan(request));

case 'translate':
Bugsnag.leaveBreadcrumb('Translating string', {
...request,
Expand Down
137 changes: 137 additions & 0 deletions src/background/calculate-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { EraInfoDate, EraInfoTimeSpan } from '../content/dates';
import { DateArray, eraInfo } from '../utils/era-info';

export function calculateEraDateTimeSpan({
era,
year,
month,
day,
}: {
era: string;
year: number;
month?: number;
day?: number;
}): EraInfoTimeSpan | undefined {
let dateStart: EraInfoDate | undefined = undefined;
let dateEnd: EraInfoDate | undefined = undefined;

if (!day) {
const res = calculateTimeSpanOfEraYearOrMonth(era, year, month);

if (!res) {
return undefined;
}

dateStart = dateToEraInfoDate(res.dateStart);
dateEnd = dateToEraInfoDate(res.dateEnd);
} else if (month) {
dateStart = dateToEraInfoDate(toGregorianDate(era, year, month, day));
}

if (!dateStart) {
return undefined;
}

return { dateStart, dateEnd };
}

function dateToEraInfoDate(date: Date): EraInfoDate {
return {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
};
}

function dateArrayToDate(dateArray: DateArray, dayOffset: number = 0) {
return new Date(dateArray[0], dateArray[1] - 1, dateArray[2] + dayOffset);
}

function toGregorianDate(
era: string,
year: number,
month: number,
day: number
): Date {
const dateArray = eraInfo[era].years[year][month];
const date = dateArrayToDate(dateArray, day - 1);
return date;
}

function calculateTimeSpanOfEraYearOrMonth(
era: string,
year: number,
month?: number
): { dateStart: Date; dateEnd: Date } | undefined {
const eraData = eraInfo[era];

const startOfEraDate = dateArrayToDate(eraData.start);
const endOfEraDate = dateArrayToDate(eraData.end);

if (!startOfEraDate || !endOfEraDate) {
return undefined;
}

if (!(year in eraData.years)) {
return undefined;
}

let startOfTimeSpan = startOfEraDate;

if (month) {
if (!(month in eraData.years[year])) {
return undefined;
}

const startOfMonthArray = eraData.years[year][month];

startOfTimeSpan = dateArrayToDate(startOfMonthArray);
} else if (1 in eraData.years[year]) {
const startOfYearArray = eraData.years[year][1];

startOfTimeSpan = dateArrayToDate(startOfYearArray);
}

const laterStartDate =
startOfEraDate > startOfTimeSpan ? startOfEraDate : startOfTimeSpan;

let endOfTimeSpan = endOfEraDate;

if (month) {
const nextMonthIsLeapMonth = month > 0 && (-month) in eraData.years[year];
const isLastMonthInYear = !nextMonthIsLeapMonth && Math.abs(month) === 12;

let nextYear = year;
let nextMonth = month;

if (nextMonthIsLeapMonth) {
nextMonth = -nextMonth;
} else if (isLastMonthInYear) {
nextYear++;
nextMonth = 1;
} else {
nextMonth = Math.abs(nextMonth) + 1;
}

if (nextYear in eraData.years && nextMonth in eraData.years[nextYear]) {
const startOfNextMonthArray = eraData.years[nextYear][nextMonth];

endOfTimeSpan = dateArrayToDate(startOfNextMonthArray, -1);
}
} else {
const notLastYearOfEra = year + 1 in eraData.years;
if (notLastYearOfEra) {
const startOfNextYearArray = eraData.years[year + 1][1];

endOfTimeSpan = dateArrayToDate(startOfNextYearArray, -1);
}
}

const earlierEndDate =
endOfEraDate < endOfTimeSpan ? endOfEraDate : endOfTimeSpan;

return {
dateStart: laterStartDate,
dateEnd: earlierEndDate,
};
}
Loading

0 comments on commit dd4ea94

Please sign in to comment.