포스트

우리 앱의 음력은 중국 달력이었다 — 음력 변환 36,890일 전수 대조와 하루가 밀리는 이유

사주로또와 띠 운세 펫의 사주 계산을 한국천문연구원(KASI) 기준 만세력 라이브러리로 바꾸기 전에, 1930~2030년 36,890일을 옛 변환기와 하루씩 대조했습니다. 1,319일(3.6%)이 달랐고, 전부 음력 한 달이 통째로 하루 밀린 구간 44개였습니다. 합삭 시각을 따로 계산해 보니 42개는 합삭이 한국시 0~1시, 베이징 시각으로는 전날 23~24시에 있었습니다. 옛 변환기는 사실상 중국 음력표였던 셈이고, 남은 2개는 2012년과 2017년의 윤달 위치 차이였습니다. 년·월주는 44만 건 중 35건이 절입 몇 분 앞뒤에서만 갈렸습니다

우리 앱의 음력은 중국 달력이었다 — 음력 변환 36,890일 전수 대조와 하루가 밀리는 이유

Read in English My App's Lunar Calendar Was China's — Checking 36,890 Days of Lunar Conversion and Why Whole Months Shift by a Day

사주로또와 띠 운세 펫은 생년월일로 사주 카드를 보여 줍니다. 생일을 음력으로 넣는 사람도 많아서, 음력 생일을 양력으로 바꾼 뒤 그날의 간지(일주)를 구합니다. 여기서 하루만 틀려도 일주가 달라지고, 카드에 나오는 오행이 바뀝니다.

이번 주에 사주 계산을 한국천문연구원(KASI) 기준 음력과 절입표를 담은 오픈소스 라이브러리 manseryeok(MIT)으로 바꿨습니다. 바꾸기 전에 옛 변환기가 얼마나 틀렸는지 궁금해서 100년 치를 하루씩 대조했습니다. 결과는 생각보다 깔끔했고, 원인은 생각보다 근본적이었습니다.

이 글의 운세 관련 내용은 재미로 보는 콘텐츠의 계산 정확도 이야기입니다. 사주 해석의 옳고 그름은 다루지 않습니다.

무엇을 비교했나

옛 변환기는 Dart 패키지 lunar_calendar_converter_new 2.0.0을 옮긴 코드입니다. 처음 Flutter로 만든 앱에서 쓰던 것을 Kotlin, TypeScript로 차례로 옮기면서 세 판이 같은 결과를 내도록 맞춰 왔습니다. 음력 달의 길이와 설날 날짜를 담은 표 두 개로 계산합니다.

비교는 두 가지로 했습니다.

  • 양력→음력: 1930년 1월 1일부터 2030년 12월 31일까지 36,890일, 매일 옛 변환기와 새 라이브러리의 음력 날짜(년·월·일)를 비교
  • 년주·월주: 같은 36,890일 × 태어난 시진 12개 = 442,680건, 옛 사주 계산과 새 라이브러리의 년주·월주를 비교

결과 한눈에

항목값
대조한 날36,890일
음력 날짜가 다른 날1,319일 (3.6%)
그 날들이 이루는 연속 구간44개 (30일 구간 43개, 29일 구간 1개)
설날 날짜가 다른 해8개
추석 날짜가 다른 해1개 (1942년)
년주·월주가 다른 건442,680건 중 35건

1,319일이 흩어져 있지 않았습니다. 음력 한 달이 통째로 하루씩 밀린 구간이 44개 있었고, 그 합이 1,319일이었습니다. 예를 들어 1997년 2월 7일부터 3월 8일까지 30일 동안, 옛 변환기는 “1997년 1월 1일”부터 세고 새 라이브러리는 “1996년 12월 30일”부터 셉니다. 옛 변환기에서는 그 달이 하루 일찍 시작했습니다.

설날이 다른 해는 1944, 1954, 1958, 1966, 1988, 1997, 2027, 2028년입니다. 여덟 번 모두 옛 변환기가 하루 빨랐습니다. 1997년이면 옛 변환기는 2월 7일, 한국 기준은 2월 8일입니다.

왜 하루씩 밀렸나 — 합삭이 자정 바로 뒤에 있었다

음력 한 달은 합삭(달이 해와 같은 방향에 있어 보이지 않는 순간)이 든 날에 시작합니다. 그런데 “그날”을 정하는 시계가 나라마다 다릅니다. 한국은 동경 135도 기준 표준시(UTC+9)를, 중국은 동경 120도 기준(UTC+8)을 씁니다. 한 시간 차이입니다.

그래서 합삭이 한국 시각으로 0시에서 1시 사이에 일어나면 문제가 생깁니다. 한국에서는 이미 다음 날이지만 베이징에서는 아직 전날 밤 11시대라, 중국 달력은 그 달을 하루 일찍 시작합니다.

44개 구간마다 시작일 근처의 합삭 시각을 천문 계산식(Meeus의 『Astronomical Algorithms』 49장 신월 공식)으로 따로 구했습니다.

구간 시작(양력)합삭(한국시)합삭(베이징 시각)옛 변환기한국 기준
1997-02-072월 8일 00:062월 7일 23:061997년 1월 1일1996년 12월 30일
1988-02-172월 18일 00:542월 17일 23:541988년 1월 1일1987년 12월 30일
2027-02-062월 7일 00:562월 6일 23:562027년 1월 1일2026년 12월 30일
1942-09-109월 11일 00:529월 10일 23:521942년 8월 1일1942년 7월 30일

44개 중 42개가 정확히 이 경우였습니다. 합삭이 한국시 0시~1시, 베이징 시각으로 전날 23시~24시에 있었습니다. 옛 변환기는 사실상 중국 기준 음력표였던 겁니다. 처음 썼던 패키지가 중국 달력을 바탕으로 만들어졌다고 보면 모든 숫자가 맞아떨어집니다. 새 라이브러리 문서에도 “KASI 정본 음력, 한·중 차이 교정”이라고 적혀 있습니다.

한 구간(2005년 12월 1일)은 계산한 합삭이 한국시 정확히 00:00으로 나왔습니다. 이 공식의 오차가 몇 분이라 이 건은 판정을 보류했습니다.

남은 2개 — 윤달이 든 달이 달랐다

나머지 두 구간은 합삭 시각 문제가 아니었습니다. 윤달 위치가 달랐습니다.

  • 2012년: 한국은 윤3월(양력 4월 21일부터), 중국은 윤4월입니다. 그래서 4월 21일부터 한 달 동안 옛 변환기는 “4월”, 한국 기준은 “윤3월”이라고 합니다.
  • 2017년: 한국은 윤5월(양력 6월 24일부터), 중국은 윤6월입니다. 6월 24일부터 29일 동안 옛 변환기는 “6월”, 한국 기준은 “윤5월”입니다.

윤달은 절기와 합삭의 순서로 정해지는데, 합삭 날짜가 하루 밀리면 어느 달에 중기(中氣)가 없는지가 바뀌어 윤달 자리가 옮겨 갑니다. 원인은 같은 한 시간 차이입니다.

사용자에게는 무엇이 달라지나

음력으로 생일을 넣은 사람이 이 1,319일 중 하루에 태어났다면, 옛 앱은 양력 날짜를 하루 틀리게 잡았습니다. 그러면 일주가 바뀌고, 일간(나를 나타내는 글자)과 오행 개수가 달라집니다. 1930~2030년 사이 아무 날이나 고르면 3.6% 확률입니다. 1997년 설날에 태어나 음력 1월 1일로 입력한 사람이라면 100% 틀렸습니다.

양력으로 생일을 넣은 사람은 이 문제와 무관합니다. 일주는 양력 날짜만으로 정해지기 때문입니다.

년주·월주 — 절입 몇 분 앞뒤에서만 갈렸다

년주와 월주는 음력이 아니라 절기로 정합니다. 입춘이 지나야 새해의 년주가 되고, 절기(節)마다 월주가 바뀝니다. 옛 코드는 태양 황경을 Meeus의 저정밀식으로 계산해 절입을 판정했습니다. 이 식의 오차는 시간으로 15분 안팎입니다. 새 라이브러리는 KASI와 분 단위로 맞춘 절입표를 씁니다.

시진 12개를 모두 넣어 44만 건을 대조한 결과, 다른 것은 35건이었습니다. 전부 절입 시각 몇 분 앞뒤였습니다.

입력(한국시)절입(새 라이브러리)옛 판정새 판정
1997-06-06 00:30망종 00:33오월(망종 뒤)사월(망종 전)
1994-02-04 10:30입춘 10:31갑술년(입춘 뒤)계유년(입춘 전)
2000-04-04 20:30청명 20:32진월(청명 뒤)묘월(청명 전)
1936-02-05 08:30입춘 08:29을해년(입춘 전)병자년(입춘 뒤)

음력 변환과 달리 이쪽은 근사식의 정밀도 문제라 거의 드러나지 않습니다. 그래도 입춘 당일에 태어난 사람은 년주(띠)가 바뀌는 경우라, 새 앱은 태어난 시진 안에서 절기가 바뀌면 “이 시각 전이면 ○○년, 뒤면 ○○년”이라고 한 줄을 더 보여 줍니다.

무엇을 바꾸고 무엇을 남겼나

  • 사주 카드는 KASI 기준으로 바꿨습니다. 음력 입력은 새 라이브러리로 양력을 구하고, 년주·월주는 절입표로 판정합니다.
  • 윤달 체크를 넣었습니다. 그해 그 달 뒤에 윤달이 있을 때만 체크 칸이 보입니다. 옛 앱은 윤달 정보를 아예 받지 않았습니다.
  • 로또 번호는 바꾸지 않았습니다. 사주로또의 번호는 생일로 만든 시드에서 나오는데, 이 시드는 옛 음력 키를 그대로 씁니다. 시드까지 바꾸면 음력으로 입력한 기존 사용자의 번호가 하루아침에 달라집니다. “같은 생일이면 같은 번호”라는 약속을 지키는 쪽을 골랐습니다.

배운 것

  • “음력 변환”에는 국적이 있습니다. 음력은 천문 현상이지만 날짜를 자르는 시계는 나라의 표준시입니다. 한국 사용자에게 음력을 쓴다면 그 라이브러리가 어느 나라 기준인지 먼저 확인해야 합니다.
  • 경계값이 전부였습니다. 1년 중 대부분은 어떤 변환기를 써도 같습니다. 틀리는 곳은 자정 직후의 합삭, 절입 몇 분 전후처럼 경계에 몰려 있습니다. 표본 몇 개로 테스트하면 다 통과합니다.
  • 전수 대조는 싸고 확실합니다. 100년 치를 하루씩 돌리는 데 특별한 장비가 필요 없었습니다. 불일치가 30일 단위로 묶여 있다는 모양이 보이자 원인 추적은 금방이었습니다.

만든 앱들은 GAMES와 APPS에 있고, 일부는 브라우저에서 바로 해볼 수 있습니다. 소식은 이 블로그와 인스타그램(@fadongkwon.soft) (새 창에서 열림)에서 전해드립니다.

수치는 2026년 11월 8일, 옛 변환 코드(lunar_calendar_converter_new 2.0.0 이식판)와 manseryeok 2.0.0을 같은 기간으로 직접 대조한 결과입니다. 합삭 시각은 Meeus 신월 공식으로 계산한 근삿값(오차 수 분)이고, 한국 기준 음력은 라이브러리가 담은 KASI 자료를 따릅니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.