Source for file Lunar_API.php
Documentation is available at Lunar_API.php
* Project: Lunar_API :: 양력/음력 변환 코어 클래스<br>
* 이 패키지는 양력/음력간의 변환을 제공하는 API로, 고영창님의 '진짜만세력'
* 0.92(Perl version)와 0.93(Pascal version)버전을 PHP로 포팅한 것이다.
* 이 변환 API의 유효기간은 다음과 같다.
* + -2087-02-09(음력 -2087-01-01) ~ 6078-01-29(음 6077-12-29)
* + -2087-07-05(음력 -2087-05-29) 이전은 계산이 무지 느려짐..
* + -4712-02-08 ~ 9999-12-31
* + -9999-01-01 ~ 9999-12-31
* + 64bit 계산이 가능한 시점까지 가능할 듯..
* @subpackage Lunar Core API
* @author JoungKyun.Kim <http://oops.org>
* @copyright (c) 2024, OOPS.org
* @license 고영창 (http://afnmp3.homeip.net/~kohyc/calendar/index.cgi)
* @link https://github.com/OOPS-ORG-PHP/Lunar
* 이 패키지는 양력/음력간의 변환을 제공하는 API로, 고영창님의 '진짜만세력'
* 0.92(Perl version)와 0.93(Pascal version)버전을 PHP로 포팅한 것이다.
* 이 변환 API의 유효기간은 다음과 같다.
* + -2087-02-09(음력 -2087-01-01) ~ 6078-01-29(음 6077-12-29)
* + -2087-07-05(음력 -2087-05-29) 이전은 계산이 무지 느려짐..
* + -4712-02-08 ~ 9999-12-31
* + -9999-01-01 ~ 9999-12-31
* + 64bit 계산이 가능한 시점까지 가능할 듯..
* @subpackage Lunar Core API
* @author JoungKyun.Kim <http://oops.org>
* @copyright (c) 2024, OOPS.org
* @license 고영창 (http://afnmp3.homeip.net/~kohyc/calendar/index.cgi)
// {{{ +-- protected prpperties
0, 21355, 42843, 64498, 86335, 108366, 130578, 152958,
175471, 198077, 220728, 243370, 265955, 288432, 310767,
332928, 354903, 376685, 398290, 419736, 441060, 462295,
protected $gan = array ('갑', '을', '병', '정', '무', '기', '경', '신', '임', '계');
protected $hgan = array ('甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸');
protected $ji = array ('자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해');
protected $hji = array ('子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥');
protected $ddi = array ('쥐', '소', '호랑이', '토끼', '용', '뱀', '말', '양', '원숭이', '닭', '개', '돼지');
// {{{ +-- public prpperties
'입춘', '우수', '경칩', '춘분', '청명', '곡우',
'입하', '소만', '망종', '하지', '소서', '대서',
'입추', '처서', '백로', '추분', '한로', '상강',
'입동', '소설', '대설', '동지', '소한', '대한',
'立春', '雨水', '驚蟄', '春分', '淸明', '穀雨',
'立夏', '小滿', '芒種', '夏至', '小暑', '大暑',
'立秋', '處暑', '白露', '秋分', '寒露', '霜降',
'立冬', '小雪', '大雪', '冬至', '小寒', '大寒',
'갑자', '을축', '병인', '정묘', '무진', '기사', '경오', '신미', '임신', '계유', '갑술', '을해',
'병자', '정축', '무인', '기묘', '경진', '신사', '임오', '계미', '갑신', '을유', '병술', '정해',
'무자', '기축', '경인', '신묘', '임진', '계사', '갑오', '을미', '병신', '정유', '무술', '기해',
'경자', '신축', '임인', '계묘', '갑진', '을사', '병오', '정미', '무신', '기유', '경술', '신해',
'임자', '계축', '갑인', '을묘', '병진', '정사', '무오', '기미', '경신', '신유', '임술', '계해'
'甲子','乙丑','丙寅','丁卯','戊辰','己巳','庚午','辛未','壬申','癸酉','甲戌','乙亥',
'丙子','丁丑','戊寅','己卯','庚辰','辛巳','壬午','癸未','甲申','乙酉','丙戌','丁亥',
'戊子','己丑','庚寅','辛卯','壬辰','癸巳','甲午','乙未','丙申','丁酉','戊戌','己亥',
'庚子','辛丑','壬寅','癸卯','甲辰','乙巳','丙午','丁未','戊申','己酉','庚戌','辛亥',
'壬子','癸丑','甲寅','乙卯','丙辰','丁巳','戊午','己未','庚申','辛酉','壬戌','癸亥'
protected $week = array ('일','월','화','수','목','금','토');
protected $hweek = array ('日','月','火','水','木','金','土');
'角','亢','氐','房','心','尾','箕',
'斗','牛','女','虛','危','室','壁',
'奎','婁','胃','昴','畢','觜','參',
'井','鬼','柳','星','張','翼','軫'
'각', '항', '저', '방', '심', '미', '기',
'두', '우', '녀', '허', '위', '실', '벽',
'규', '수', '위', '묘', '필', '자', '삼',
'정', '귀', '류', '성', '장', '익', '진'
// {{{ +-- protected (int) div ($a, $b)
protected function div ($a, $b) {
// {{{ +-- protected (int) disptimeday ($year, $month, $day)
* year의 1월 1일부터 해당 일자까지의 날자수
for ( $i= 1; $i< $month; $i++ ) {
if ( $i == 2 || $i == 4 || $i == 6 || $i == 9 || $i == 11 )
if ( ($year % 4) == 0 ) $e++ ;
if ( ($year % 100) == 0 ) $e-- ;
if ( ($year % 400) == 0 ) $e++ ;
if ( ($year % 4000) == 0 ) $e-- ;
// {{{ +-- protected (int) disp2days ($y1, $m1, $d1, $y2, $m2, $d2)
* y1,m1,d1일부터 y2,m2,d2까지의 일수 계산
protected function disp2days ($y1, $m1, $d1, $y2, $m2, $d2) {
$p1 = $p2 = $pn1 = $pp1 = $pp2 = $pr = $dis = $ppp1 = $ppp2 = $k = 0;
for ( $k = $ppp1; $k <= $ppp2; $k++ ) {
if ( $k == - 9000 && $ppp2 > 1990 ) {
} else if ( $k == - 8000 && $ppp2 > 1990 ) {
} else if ( $k == - 7000 && $ppp2 > 1990 ) {
} else if ( $k == - 6000 && $ppp2 > 1990 ) {
} else if ( $k == - 5000 && $ppp2 > 1990 ) {
} else if ( $k == - 4000 && $ppp2 > 1990 ) {
} else if ( $k == - 3000 && $ppp2 > 1990 ) {
} else if ( $k == - 2000 && $ppp2 > 1990 ) {
} else if ( $k == - 1750 && $ppp2 > 1990 ) {
} else if ( $k ==- 1500 && $ppp2 > 1990 ) {
} else if ( $k ==- 1250 && $ppp2 > 1990 ) {
} else if ( $k ==- 1000 && $ppp2 > 1990 ) {
} else if ( $k == - 750 && $ppp2 > 1990 ) {
} else if ( $k == - 500 && $ppp2 > 1990 ) {
} else if ( $k == - 250 && $ppp2 > 1990 ) {
} else if ( $k == 0 && $ppp2 > 1990 ) {
} else if ( $k == 250 && $ppp2 > 1990 ) {
} else if ( $k == 500 && $ppp2 > 1990 ) {
} else if ( $k == 750 && $ppp2 > 1990 ) {
} else if ( $k == 1000 && $ppp2 > 1990 ) {
} else if ( $k == 1250 && $ppp2 > 1990 ) {
} else if ( $k == 1500 && $ppp2 > 1990 ) {
} else if ( $k == 1750 && $ppp2 > 1990 ) {
// {{{ +-- protected (int) getminbytime ($uy, $umm, $ud, $uh, $umin, $y1, $mo1, $d1, $h1, $mm1)
* uy,umm,ud,uh,umin과 y1,mo1,d1,h1,mm1사이의 시간(분)
function getminbytime ($uy, $umm, $ud, $uh, $umin, $y1, $mo1, $d1, $h1, $mm1) {
$dispday = $this->disp2days ($uy, $umm, $ud, $y1, $mo1, $d1);
$t = $dispday * 24 * 60 + ($uh - $h1) * 60 + ($umin - $mm1);
// {{{ +-- protected (array) getdatebymin ($tmin, $uyear, $umonth, $uday, $uhour, $umin)
* uyear,umonth,uday,uhour,umin으로부터 tmin(분)떨이진 시점의
protected function getdatebymin ($tmin, $uyear, $umonth, $uday, $uhour, $umin) {
$y1 = $mo1 = $d1 = $h1 = $mi1 = $t = 0;
$y1 = $uyear - $this->div ($tmin, 525949);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, 1, 1, 0, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, 1, 0, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, $d1, 0, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, $d1, $h1, 0);
$t = $this->getminbytime ( $uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, $d1, $h1, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, 1, 1, 0, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, 1, 0, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, $d1, 0, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, $d1, $h1, 0);
$t = $this->getminbytime ($uyear, $umonth, $uday, $uhour, $umin, $y1, $mo1, $d1, $h1, 0);
return array ($y1, $mo1, $d1, $h1, $mi1);
// {{{ +-- protected (array) sydtoso24yd ($soloryear, $solormonth, $solorday, $solorhour, $solormin)
* 그레고리력의 년월시일분으로 60년의 배수, 세차, 월건(태양력),
* [1] => 29 // 60간지의 연도 배열 index
* [2] => 55 // 60간지의 월 배열 index
* [3] => 11 // 60간지의 일 배열 index
* [4] => 20 // 60간지의 시 배열 index
protected function sydtoso24yd ($soloryear, $solormonth, $solorday, $solorhour, $solormin) {
$soloryear, $solormonth, $solorday, $solorhour, $solormin
// 무인년(1996)입춘시점부터 해당일시까지 경과년수
$so24 = $this->div ($displ2min, 525949);
$so24year = ($so24 % 60) * - 1;
else if ( $so24year > 59 )
$monthmin100 = $displ2min % 525949;
$monthmin100 = 525949 - $monthmin100;
else if ( $monthmin100 >= 525949 )
for ( $i= 0; $i<= 11; $i++ ) {
if ( ($this->month[$j] <= $monthmin100) && ($monthmin100 < $this->month[$j+ 2]))
$so24day = $displ2day % 60;
else if ( $so24day > 59 )
if ( ($solorhour == 0 || $solorhour == 1) && $solormin < 30 )
else if ( ($solorhour == 1 && $solormin >= 30) || $solorhour == 2
|| ($solorhour == 3 && $solormin < 30) )
else if ( ($solorhour == 3 && $solormin >= 30) || $solorhour == 4
|| ($solorhour == 5 && $solormin < 30) )
else if ( ($solorhour == 5 && $solormin >= 30) || $solorhour == 6
|| ($solorhour == 7 && $solormin < 30) )
else if ( ($solorhour == 7 && $solormin >= 30) || $solorhour == 8
|| ($solorhour == 9 && $solormin < 30) )
else if ( ($solorhour == 9 && $solormin >= 30) || $solorhour == 10
|| ($solorhour == 11 && $solormin < 30) )
else if ( ($solorhour == 11 && $solormin >= 30) || $solorhour == 12
|| ($solorhour == 13 && $solormin < 30) )
else if ( ($solorhour == 13 && $solormin >= 30) || $solorhour == 14
|| ($solorhour == 15 && $solormin < 30) )
else if ( ($solorhour == 15 && $solormin >= 30) || $solorhour == 16
|| ($solorhour == 17 && $solormin < 30) )
else if ( ($solorhour == 17 && $solormin >= 30) || $solorhour == 18
|| ($solorhour == 19 && $solormin < 30) )
else if ( ($solorhour == 19 && $solormin >= 30) || $solorhour == 20
|| ($solorhour == 21 && $solormin < 30) )
else if ( ($solorhour == 21 && $solormin >= 30) || $solorhour == 22
|| ($solorhour == 23 && $solormin < 30) )
else if ( $solorhour == 23 && $solormin >= 30 ) {
return array ($so24, $so24year, $so24month, $so24day, $so24hour);
// {{{ +-- protected (array) solortoso24 ($soloryear, $solormonth, $solorday, $solorhour, $solormin)
* 그레고리력의 년월일시분이 들어있는 절기의 이름번호,
protected function solortoso24 ($soloryear, $solormonth, $solorday, $solorhour, $solormin) {
list ($so24, $so24year, $so24month, $so24day, $so24hour) =
$this->sydtoso24yd ($soloryear, $solormonth, $solorday, $solorhour, $solormin);
$soloryear, $solormonth, $solorday, $solorhour, $solormin
# perl 과 php 의 음수의 나머지 결과값이 다르다! --;
#$monthmin100 = $displ2min % 525949;
#$monthmin100 = 525949 - $monthmin100;
$monthmin100 = ($displ2min % 525949) * - 1;
else if ( $monthmin100 >= 525949 )
$i = $so24month % 12 - 2;
else if ( $i == - 1 ) $i = 11;
$tmin = $displ2min + ($monthmin100 - $this->month[$j]);
list ($y1, $mo1, $d1, $h1, $mi1) =
$tmin = $displ2min + ($monthmin100 - $this->month[$j+ 1]);
list ($y1, $mo1, $d1, $h1, $mi1) =
$tmin = $displ2min + ($monthmin100 - $this->month[$j+ 2]);
list ($y1, $mo1, $d1, $h1, $mi1) =
$inginame, $ingiyear, $ingimonth, $ingiday, $ingihour, $ingimin,
$midname, $midyear, $midmonth, $midday, $midhour, $midmin,
$outginame, $outgiyear, $outgimonth, $outgiday, $outgihour, $outgimin
// {{{ +-- protected (int) degreelow ($d)
$i = $this->div ((int) $d, 360);
while ( $di >= 360 || $di < 0 ) {
// {{{ +-- protected (int) moonsundegree ($day)
* 태양황력과 달황경의 차이 (1996 기준)
$sl = (float) ($day * 0.98564736 + 278.956807); // 평균 황경
$smin = 282.869498 + 0.00004708 * $day; // 근일점 황경
$sminangle = 3.14159265358979 * ($sl - $smin) / 180; // 근점이각
$sd = 1.919 * sin ($sminangle) + 0.02 * sin (2 * $sminangle); // 황경차
$sreal = $this->degreelow ($sl + $sd); // 진황경
$ml = 27.836584 + 13.17639648 * $day; // 평균 황경
$mmin = 280.425774 + 0.11140356 * $day; // 근지점 황경
$mminangle = 3.14159265358979 * ($ml - $mmin) / 180; // 근점이각
$msangle = 202.489407 - 0.05295377 * $day; // 교점황경
$msdangle = 3.14159265358979 * ($ml - $msangle) / 180;
$md = 5.06889 * sin ($mminangle)
+ 0.146111 * sin (2 * $mminangle)
+ 0.01 * sin (3 * $mminangle)
- 0.238056 * sin ($sminangle)
- 0.087778 * sin ($mminangle + $sminangle)
+ 0.048889 * sin ($mminangle - $sminangle)
- 0.129722 * sin (2 * $msdangle)
- 0.011111 * sin (2 * $msdangle - $mminangle)
- 0.012778 * sin (2 * $msdangle + $mminangle); // 황경차
$mreal = $this->degreelow ($ml + $md); // 진황경
// {{{ +-- protected (array) getlunarfirst ($syear, $smonth, $sday)
* 그레고리력 년월일이 들어있는 태음월의 시작합삭일지, 망일시,
* [0] => 2013 // 시작 합삭 년도
* [10] => 2013 // 끝 합삭 년도
$dm = $this->disp2days ($syear, $smonth, $sday, 1995, 12, 31);
list ($year, $month, $day, $hour, $min)= $this->getdatebymin ($i, 1995, 12, 31, 0, 0);
list ($year2, $month2, $day2, $hour2, $min2) = $this->getdatebymin ($i, 1995, 12, 31, 0, 0);
if ( $smonth == $month2 && $sday == $day2 ) {
# 위의 라인 부분이 pascal(0.93) 버전에서는 아래와 같이 처리 되어 있음.
# 함수 반환 값의 차이가 없는 것으로 보아 성능 개선 코드일까?
# $de = $this->moonsundegree ($d);
list ($year2, $month2, $day2, $hour2, $min2) = $this->getdatebymin ($i, 1995, 12, 31, 0, 0);
$d = $this->disp2days ($year, $month, $day, 1995, 12, 31);
while ( $de < 179.999 ) {
list ($year1, $month1, $day1, $hour1, $min1) = $this->getdatebymin ($i, 1995, 12, 31, 0, 0);
$year, $month, $day, $hour, $min,
$year1, $month1, $day1, $hour1, $min1,
$year2, $month2, $day2, $hour2, $min2
// {{{ +-- protected (array) solartolunar ($solyear,$solmon,$solday)
* [3] => // 음력 윤달 여부 (boolean)
* [4] => 1 // 평달(false)/큰달(true) 여부 (boolean)
protected function solartolunar ($solyear, $solmon, $solday) {
list ($smoyear, $smomonth, $smoday, $smohour, $smomin,
$y0, $mo0, $d0, $h0, $mi0, $y1, $mo1, $d1, $h1, $mi1)
$lday = $this->disp2days ($solyear, $solmon, $solday, $smoyear, $smomonth, $smoday) + 1;
$i = abs ($this->disp2days ($smoyear, $smomonth, $smoday, $y1, $mo1, $d1));
list ($inginame, $ingiyear, $ingimonth, $ingiday, $ingihour, $ingimin,
$midname1, $midyear1, $midmonth1, $midday1, $midhour1, $midmin1,
$outginame, $outgiyear, $outgimonth, $outgiday, $outgihour, $outgimin)
= $this->solortoso24 ($smoyear, $smomonth, $smoday, $smohour, $smomin);
$midname2 = $midname1 + 2;
$s0 = $this->month[$midname2] - $this->month[$midname1];
list ($midyear2, $midmonth2, $midday2, $midhour2, $midmin2)
= $this->getdatebymin ($s0, $midyear1, $midmonth1, $midday1, $midhour1, $midmin1);
if ( ($midmonth1 == $smomonth && $midday1 >= $smoday) || ($midmonth1 == $mo1 && $midday1 < $d1) ) {
$lmonth = ($midname1 - 1) / 2 + 1;
if ( ($midmonth2 == $mo1 && $midday2< $d1) || ($midmonth2 == $smomonth && $midday2 >= $smoday) ) {
$lmonth = ($midname2 - 1) / 2 + 1;
if ( $smomonth < $midmonth2 && $midmonth2 < $mo1 ) {
$lmonth = ($midname2 - 1) / 2 + 1;
$lmonth = ($midname1 - 1) / 2 + 1;
if ( $lmonth == 12 && $smomonth == 1 )
if ( ($lmonth == 11 && $leap == 1) || $lmonth == 12 || $lmonth < 6 ) {
list ($midyear1, $midmonth1, $midday1, $midhour1, $midmin1)
= $this->getdatebymin (2880, $smoyear, $smomonth, $smoday, $smohour, $smomin);
list ($outgiyear, $outgimonth, $outgiday, $lnp, $lnp2)
if ( $outgiday == $outgimonth ) {
if ( $lmonth != $outgimonth ) {
if ( $lmonth == $outgimonth ) {
return array ($lyear, $lmonth, $lday, $leap ? true : false, $largemonth ? true : false);
// {{{ +-- protected (array) lunartosolar ($lyear, $lmonth, $lday, $leap)
protected function lunartosolar ($lyear, $lmonth, $lday, $leap = false) {
list ($inginame, $ingiyear, $ingimonth, $ingiday, $ingihour, $ingimin,
$midname, $midyear, $midmonth, $midday, $midhour, $midmin,
$outginame, $outgiyear, $outgimonth, $outgiday, $outgihour, $outgimin)
$midname = $lmonth * 2 - 1 ;
$tmin = $this->month[$midname] * - 1;
list ($midyear, $midmonth, $midday, $midhour, $midmin)
= $this->getdatebymin ($tmin, $ingiyear, $ingimonth, $ingiday, $ingihour, $ingimin);
list ( $outgiyear, $outgimonth, $outgiday, $hour, $min,
$yearm, $monthm1, $daym, $hourm, $minm,
$year1, $month1, $day1, $hour1, $min1 )
list ($lyear2, $lmonth2, $lday2, $lnp, $lnp2)
if ( $lyear2 == $lyear && $lmonth == $lmonth2 ) {
$tmin = - 1440 * $lday + 10;
list ($syear, $smonth, $sday, $hour, $min)
= $this->getdatebymin ($tmin, $outgiyear, $outgimonth, $outgiday, 0, 0);
list ($lyear2, $lmonth2, $lday2, $lnp, $lnp2)
if ( $lyear2== $lyear && $lmonth== $lmonth2 ) {
$tmin = - 1440 * $lday + 10;
list ($syear, $smonth, $sday, $hour, $min)
= $this->getdatebymin ($tmin, $year1, $month1, $day1, 0, 0);
list ($lyear2, $lmonth2, $lday2, $lnp, $lnp2)
if ( $lyear2 == $lyear && $lmonth == $lmonth2 ) {
$tmin = - 1440 * $lday + 10;
list ($syear, $smonth, $sday, $hour, $min)
= $this->getdatebymin ($tmin, $year1, $month1, $day1, 0, 0);
return array ($syear, $smonth, $sday);
// {{{ +-- protected (int) getweekday ($syear, $smonth, $sday)
* 그레고리력 날자를 요일의 배열 번호로 변환
protected function getweekday ($syear, $smonth, $sday) {
while ( $d > 6 || $d < 0 ) {
// {{{ +-- protected (int) get28sday ($syear, $smonth, $sday)
protected function get28sday ($syear, $smonth, $sday) {
$i = $this->div ($d, 28);
while ( $d > 27 || $d < 0 ) {
# parscal(0.93) 버전에서는 이 2라인이 제거 되어 있음.
# 실행의 값은 차이가 없는데, 성능 개선인가?
* vim: set filetype=php noet sw=4 ts=4 fdm=marker:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
|