Logo

TEA Calendar

Pragmatically convert Gregorian calendar date of the period 1583-2100 to Chinese Calendar date

Demo

Table Of Contents

About the Project

This JavaScript program converts any given Gregorian calendar date of the period 1583-2100 to its corresponding Chinese calendar date. It is intended to be an alternative to the conversion via the JavaScript standard built-in object [Intl.DateTimeFormat](Intl.DateTimeFormat - JavaScript | MDN), not always reliable for this particular purpose in the real world. The code below (see also this codepen) illustrates this inconsistency, which encompasses more than four thousand dates of the past five centuries.

//conversion wiht the JavaScript's Standard built-in objects
const date = new Date('2018-11-08T12:00:00.000');
const options = {year: 'numeric', month: 'numeric', day: 'numeric' };
const dateFormat = new Intl.DateTimeFormat('fr-CA-u-ca-chinese', options);
let output = dateFormat.format(date);
//output in Chrome as well as in Firefox: "2/10/35".
//In reality, the corresponding date is "1/10/35".

Note About the correct conversion of the date above, cf., for example, the conversion table 2011-2020 published by the Taipei Astronomical Museum or the Gregorian-Lunar Calendar Conversion Table 2018 of Hong Kong Observatory.

Due to the nature and the history of the traditional Chinese calendar (and the traditional East-Asian calendars in general), it is impossible to resolve this problem by adjusting the astro-mathematical formulas used in the major browser engines (more precisely, the subclass ChineseCalendar  developped by IBM and integrated in International Components for Unicode, then in Chromium, Gecko and Webkit). One of possible solutions is to convert the problematic dates by referring to a dedicated dataset. Such a patch requires filtering each input, resulting certainly in a slow-down in the calculation process.

It is more pragmatic to use only the data collected from the existing calendars (in the same way as the class ChineseLunisolarCalendar of .NET) instead of (re)doing the work of calculation of their editors. By adopting this “down-to-earth” approach, the program presented here is built upon a model of data abstraction which, by exploiting certain characteristics of the traditional lunisolar calendar of East Asia, allows not only to substantially reduce the size of the data to be conveyed but also to do the conversion by quite few simple arithmetic operations.

An NPM version with identical core codes is also published in GitHub.

Features

  • ESM.

  • With a linguistic module supporting outputs with pinyin, in Chinese and in Korean.

  • Size of minified js file: 4.42 kB.

  • Speed of conversion (operations per second, according to tests carried out in JSBench.me):

    Day (s) TEA calendar JS's Intl.DateTimeFormat
    One day 1.1M ops 52K ops
    30 consecutive days 37K ops 1.7K ops
    365 consecutive days 3.2K ops 143 ops

      Note Although demonstrating a narrower gap, the outcome of another test, which utilizes the Performance API and runs on localhost, essentially corroborates the findings shown above.

Usage

Basic conversion

1. import the core module:

  • local import:
import {Teac} from '/PATH/TO/teac.min.js'
  • remote import from deno.land:
 import {Teac} from 'https://deno.land/x/tea_calendar/teac.min.js'

2. input a Gregorian Calendar date string in "YYYY-MM-DD" format to get an array of four elements:

  • number of the year in the sexagenary cycle,

  • lunar month number,

  • day number,

  • Boolean value for leap month (true: leap month)

const date = new Teac('2025-07-28').num();
// Expected output: [42, 6, 4, true] 

Note In order to avoid the impact of time zone setting of the device (or the network), this program uses the UTC and the 12:00:00 GMT+00:00 for each input date. For example, the string '2023-04-04' is converted to "2023-04-04T12:00:00.000Z" before the construction of the corresponding JavaScript Date object.

i18n

import {Teac, Lang} from './Teac.js'

2a. output the sexagenary years in string format:

  • in Chinese:
const d = new Teac('2025-07-28').yearIn('zh');
// Expected output: ['乙巳', 6, 4, true] 
  • in Korean:
const d = new Teac('2025-07-28').yearIn('ko');
// Expected output: ['을사', 6, 4, true] 
  • in Pinyin:
const d = new Teac('2025-07-28').yearIn('en');
// Expected output: ['yi-si', 6, 4, true] 

Note Any ISO 639-1 codes other than ko and zh could replace the en here. In other words, de, fr, nl are valid and all produce the same output.

2b. or, output entirely in text format:

  • in traditional Chinese:
const d = new Teac('2025-07-28').sino(0)
// Expected output: ['乙巳年', '閏六月', '初四'] 
  • in simplified Chinese:
const d = new Teac('2025-07-28').sino(1)
// Expected output: ['乙巳年', "闰六月', '初四']
  • without literal characters:
const d = new Teac('2025-07-28').sino(0, false);
// Expected output: ['乙巳', "閏六', '初四']

## Limitations

Because of their closeness to the midnight, the 9th new moon of 2057, the 8th new moon of 2089 and the 7th one of 2097 may have a discrepancy of one day. Any prospective conversion concerning these dates is subject to adjustment and this program is of no exception.

License

Distributed under the MIT License. See LICENSE for more information.

Author

Acknowledgements