dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.16k stars 4.71k forks source link

Korean Lunisolar Calendar conversion table incorrectly mixes Julian and Gregorian date information #10130

Closed elyoh closed 4 years ago

elyoh commented 6 years ago

The Korean Lunisolar Calendar uses a table of data for conversion between Korean lunisolar dates and Gregorian dates. The table contains a mixture of Julian and Gregorian date information, but should only contain Gregorian date information.

On such entry is: 1391 */{ 0 , 2 , 5 , 25904 },/* 29 30 30 29 29 30 29 30 29 29 30 30 0 354

This is interpreted by the code as: Y1391-M1-D1 of Korean lunisolar calendar began on 5 Feb 1391 (Gregorian).

However, Y1391-M1-D1 (lunisolar) began on 13 Feb 1391 (Gregorian) or 05 Feb 1391 (Julian). The entry incorrectly uses the day and month from the Julian date and it should use day and month from the Gregorian one.

Further analysis shows that all lines in the table prior to 1582 incorrectly give the first day of the lunisolar year in the Julian calendar rather than the Gregorian calendar. Since the conversion routine takes a Gregorian day, month, and year for conversion into the lunisolar day month and year, any conversions to dates with a solar year before 1583 are incorrect.

The discrepancy likely arises due to the fact that many reference tables switch from using the Julian calendar to the Gregorian calendar in year 1582 (04 Oct 1582 (Julian) was followed by 15 Oct 1582 (Gregorian)). One such table is provided by the Korea Astronomy and Space Science In- stitute (KASI). Here dates prior to Y1582-M09-D18 (lunisolar) are indexed using the Julian calendar and those after Y1582-M09-D19 (lunisolar) are indexed using the Gregorian calendar.

There are also anomalous entries in the .NET table when compared to KASI. The first day of the year is incorrect for 1587, 1648, and 1754. Most notably, the entry for 1587 gives 09 Mar 1587 (Gregorian) as the start date of lunisolar year (this is the first day Y1587-M02-D01 (lunisolar) instead of Y1587-M01-D01 (lunisolar)). The individual month lengths differ from KASI in 1586, 1587, 1659, 1692, 1753, and 1754.

The table used in the .NET class has been corrected here: correct Korean lunisolar .NET table.txt

Reference data: Korea Astronomy and Space Science In- stitute (KASI). VERIFICATION OF THE CALENDAR DAYS OF THE JOSEON DYNASTY KoreanLunisolarCalendar.cs

jkotas commented 6 years ago

cc @tarekgh

tarekgh commented 6 years ago

@elyoh

I have used your link https://astro.kasi.re.kr:444/life/pageView/5 and queried the year 1391 and month 2 (Feb) then I got the following result:

양력 음력 음력간지
1391-02-05 (일) 1391-01-01 신미(辛未)년 경인(庚寅)월 기축(己丑)일

which suggest (if I translated correctly) that Lunar date 1391-M1-D1 is equivalent to Gregorian date 1391 February 5th which match our current data. am I missing anything here?

elyoh commented 6 years ago

@tarekgh

KASI uses Julian dates not Gregorian dates prior to lunar date 1582-M09-D18, so 1391-02-05 must be interpreted as a Julian date not a Gregorian one.

If you check the tables at https://astro.kasi.re.kr:444/life/pageView/5 for year 1582 and month 10, you will see the transition from Julian dates to Gregorian dates.

Julian Gregorian Lunar date
1582-10-02 1582-10-12 1582-09-16
1582-10-03 1582-10-13 1582-09-17
1582-10-04 1582-10-14 1582-09-18
양력날짜가 존재하지 않습니다.(율리우스역과 그레고리안역의 차이때문)
1582-10-05 1582-10-15 1582-09-19
1582-10-06 1582-10-16 1582-09-20
1582-10-07 1582-10-17 1582-09-21

The dates in bold are those used in the table at KASI. The jump of 10 days seen in the dates in bold is because they switch from using Julian dates to Gregorian dates. Thursday 04 Oct 1582 (Julian) was followed by Friday 15 Oct 1582 (Gregorian). The .NET table should only be using the proleptic Gregorian dates (middle column).

The line 양력날짜가 존재하지 않습니다.(율리우스역과 그레고리안역의 차이때문) broadly translates as "There is no solar date (because of the difference between Julian and Gregorian)". A better translation would be that they switched calendars. The authors chose to use Julian dates up to the point of introduction of the Gregorian calendar, Gregorian dates thereafter.

So to clarify your example:

Julian Gregorian Lunar date
1391-02-05 1391-02-13 1391-01-01

The .NET table uses month 2 day 5 (which is correct for the Julian calendar) as the start date of lunar year 1391 but should use month 2 and day 13 (which is the Gregorian calendar equivalent needed by the .NET calculation).

tarekgh commented 6 years ago

Thanks, @elyoh, this is very useful feedback.

How people usually use this calendar for the years prior 1582? I am asking because they may expect to have the Julian dates as what https://astro.kasi.re.kr:444/life/pageView/5 is showing?

Also, does the table hear title "양력" means Gregorian? I am asking because if this correct, then https://astro.kasi.re.kr:444/life/pageView/5 would be wrong to name it as Gregorian for years prior to 1582.

elyoh commented 6 years ago

@tarekgh My understanding is that 양력 means solar calendar. As Julian and Gregorian are both solar calendars, this label is OK for both. Whilst the labelling is somewhat unclear, it is common place to switch calendars in this way. For example the USNO converter takes a day month and year input but treats it as Julian before 04 Oct 1582 and as Gregorian after (clearly explaining this in the notes).

On the other hand, .NET calendars do not have this behavior. They are built to work with DateTime. The only way to elicit a day, month and year from these calendars is by supplying a DateTime object. All the conversion functions (see EastAsianLunisolarCalendar.cs extract a Gregorian day, month and year from the DateTime before looking up the corresponding entry in the conversion table. This is where the issue lies.

Taking this example: var koreanCalendar = new KoreanLunisolarCalendar(); var dt = new DateTime(1391, 02, 13); var lunarDay = koreanCalendar .GetDayOfMonth(dt);

13 Feb 1391 is day number 31 + 13 = day 44. The start of the lunar year as coded in the table as 05 Feb 1391 which is day number = 31 + 5 = day 36. So the code computes that the DateTime(1391, 02, 13) is day 13 - 5 + 1 = 9th day of the 1st lunar month. This is incorrect. It should be day 1.

Taking a second example: var julianCalendar = new JulianCalendar(); var dt = new DateTime(1391, 02, 05, julianCalendar); var lunarDay = koreanCalendar .GetDayOfMonth(dt);

This time, we initialise the DateTime using a Julian year, month, and day by supplying a Julian calendar instance. Again the computed lunar day is 9 and it should be 1.

The only way to get the "correct" result is to do this: var dt = new DateTime(1391, 02, 05); var lunarDay = koreanCalendar .GetDayOfMonth(dt);

This time we get lunar day 1 but only because we faked the input (the expected result should be lunar day 23 of the lunar month 12 of the lunar year 1390).

Before posting this, we cross referenced using Calendrical Calculations to double check the true identity of the dates in the KASI table.

Korean lunisolar 1391-01-01 is Julian day number 2229155.5 This corresponds to Julian 1391-02-05, and Gregorian 1391-02-13.

tarekgh commented 6 years ago

@elyoh are you interested to submit a PR for fixing the calendar data? also, do you have a link to the calendar data in Calendrical Calculations or you were referring to the book?

elyoh commented 6 years ago

I'd be happy to submit a PR and I was referring to the book Calendrical Calculations.

tarekgh commented 6 years ago

I'd be happy to submit a PR

Thanks a lot.

I was referring to the book Calendrical Calculations.

That is what I meant by "you were referring to the book". could you please add the exact section of this book mentioned "Korean lunisolar 1391-01-01 is Julian day number 2229155.5"? sorry I have the old edition of the book which looks don't have this information.

elyoh commented 6 years ago

I see. We calculated 2229155.5 for all three dates. Apologies that wasn't clear. In the third edition, we would have used:

fixed-from-gregorian (function 2.17 with day=13, month=2, year=1391) fixed-from-julian (function 3.3, with day=5, month=2, year=1391) fixed-from-chinese (function 17.17 with cycle=68, year=8, month=1, leap-month=false, day=1) with the korean-location (function 17.32) function 1.2 to convert to Julian day number.

(sorry no page number as I have the Kindle version of the 3rd edition).

Note 17.17 takes a cycle and year rather than a Gregorian year. To convert, 1391 we used 1391+2637=4028 Chinese year hence cycle = ⌈4028/60⌉ = 68 and year = 4028 mod 60 = 8.

tarekgh commented 6 years ago

This is fixed by @elyoh linked PR. Thanks @elyoh