양력 – 음력 변환 클래스
<?php class LunarCalendar { var $lunarMonthType = array(); // var $accumulateLunarDate = array(); var $SolarToLunar = array(); var $LunarToSolar = array(); var $error = ""; var $solar_start = "1881-01-30"; var $lunar_start = '18810101'; function __construct() { // 음력 달력의 달형태를 저장한다. // 각 해는 13월로 표현되고, 1 작은달, 2 큰달, 3 작은 윤달, 4 큰 윤달 이다. 0 은 윤달이 없는 해에 자리를 채우는 것이다. // 1881년 1월 30일은 음력 1881년 1월 1일 임으로 이를 기준으로 계산한다. $monthTypeMark = "1212122322121" . "1212121221220" . "1121121222120" . "2112132122122" . "2112112121220" . "2121211212120" . "2212321121212" . "2122121121210" . "2122121212120" . "1232122121212" . "1212121221220" . "1121123221222" . "1121121212220" . "1212112121220" . "2121231212121" . "2221211212120" . "1221212121210" . "2123221212121" . "2121212212120" . "1211212232212" . "1211212122210" . "2121121212220" . "1212132112212" . "2212112112210" . "2212211212120" . "1221412121212" . "1212122121210" . "2112212122120" . "1231212122212" . "1211212122210" . "2121123122122" . "2121121122120" . "2212112112120" . "2212231212112" . "2122121212120" . "1212122121210" . "2132122122121" . "2112121222120" . "1211212322122" . "1211211221220" . "2121121121220" . "2122132112122" . "1221212121120" . "2121221212110" . "2122321221212" . "1121212212210" . "2112121221220" . "1231211221222" . "1211211212220" . "1221123121221" . "2221121121210" . "2221212112120" . "1221241212112" . "1212212212120" . "1121212212210" . "2114121212221" . "2112112122210" . "2211211412212" . "2211211212120" . "2212121121210" . "2212214112121" . "2122122121120" . "1212122122120" . "1121412122122" . "1121121222120" . "2112112122120" . "2231211212122" . "2121211212120" . "2212121321212" . "2122121121210" . "2122121212120" . "1212142121212" . "1211221221220" . "1121121221220" . "2114112121222" . "1212112121220" . "2121211232122" . "1221211212120" . "1221212121210" . "2121223212121" . "2121212212120" . "1211212212210" . "2121321212221" . "2121121212220" . "1212112112210" . "2223211211221" . "2212211212120" . "1221212321212" . "1212122121210" . "2112212122120" . "1211232122212" . "1211212122210" . "2121121122210" . "2212312112212" . "2212112112120" . "2212121232112" . "2122121212110" . "2212122121210" . "2112124122121" . "2112121221220" . "1211211221220" . "2121321122122" . "2121121121220" . "2122112112322" . "1221212112120" . "1221221212110" . "2122123221212" . "1121212212210" . "2112121221220" . "1211231212222" . "1211211212220" . "1221121121220" . "1223212112121" . "2221212112120" . "1221221232112" . "1212212122120" . "1121212212210" . "2112132212221" . "2112112122210" . "2211211212210" . "2221321121212" . "2212121121210" . "2212212112120" . "1232212122112" . "1212122122120" . "1121212322122" . "1121121222120" . "2112112122120" . "2211231212122" . "2121211212120" . "2122121121210" . "2124212112121" . "2122121212120" . "1212121223212" . "1211212221220" . "1121121221220" . "2112132121222" . "1212112121220" . "2121211212120" . "2122321121212" . "1221212121210" . "2121221212120" . "1232121221212" . "1211212212210" . "2121123212221" . "2121121212220" . "1212112112220" . "1221231211221" . "2212211211220" . "1212212121210" . "2123212212121" . "2112122122120" . "1211212322212" . "1211212122210" . "2121121122120" . "2212114112122" . "2212112112120" . "2212121211210" . "2212232121211" . "2122122121210" . "2112122122120" . "1231212122212" . "1211211221220" . "2121121321222" . "2121121121220" . "2122112112120" . "2122141211212" . "1221221212110" . "2121221221210" . "2114121221221"; // $monthTypeMark = "1212122322121" . "1212121221220"; // 디버깅용 데이터. // $monthTypeMark 에 대응하는 날의수 $dateCount = array( 0, 29, 30, 29, 30 ); //문자열 입력을 배열로 컷팅. $perYear = str_split($monthTypeMark, 13); foreach ($perYear as $yearData) { $arr = str_split($yearData); $lunarMonthType[] = $arr; } //인덱스 구축. $solarDate = new DateTime($this->solar_start); $lastSol = $solarDate->format('Ymd'); $lastLuna = $this->lunar_start; $lunarYear = (int) substr($this->lunar_start, 0, 4); foreach ($lunarMonthType as $yearArr) { $accArr = array(); $lunarMonth = 0; foreach ($yearArr as $monthType) { if ($monthType == '0') continue; $dcnt = $dateCount[$monthType]; $isLeapMonth = false; if ($monthType == '3' || $monthType == '4') $isLeapMonth = true; else $lunarMonth++; $lunarYMD = sprintf('%d%02d%02d%s', $lunarYear, $lunarMonth, 1, $isLeapMonth ? 'L' : ' '); if (isset($this->SolarToLunar[$solarDate->format('Ym')]) == false) { $this->SolarToLunar[$solarDate->format('Ym')][$lastSol] = $lastLuna; } $this->SolarToLunar[$solarDate->format('Ym')][$solarDate->format('Ymd')] = $lunarYMD; $this->LunarToSolar[$lunarYMD] = $solarDate->format('Ymd'); $lastSol = $solarDate->format('Ymd'); $lastLuna = $lunarYMD; $solarDate->add(new DateInterval('P' . $dcnt . 'D')); } $lunarYear++; } } /** * 디버깅용 인덱스 출력함수 * */ function print_index() { // foreach ($this->SolarToLunar as $k=> $l) // { // if(count($l) >1) // { // print_r("$k => "); // print_r($l); // } // } print_r($this->SolarToLunar); } /** * getLunarDate의 반환값을 포맷팅 하기 위한 함수 * 아래 포맷을 지원함 * *Y-m-d : 2010-02-03 형태 * *YmdL : 20100203L 형태, L이 붙으면 윤달 그렇지 않으면 윤달 아님. * @param unknown_type $lunarDate * @param unknown_type $fmt */ static function formatLunar($lunar, $fmt = 'Y-m-d') { $lunarYear = $lunar['year']; $lunarMonth = $lunar['month']; $lunarDate = $lunar['date']; $isLeapMonth = $lunar['is_leap_month']; return LunarCalendar::formatLunar2($lunarYear, $lunarMonth, $lunarDate, $isLeapMonth, $fmt); } /** * 포맷팅 지원함수. * @param unknown_type $lunarYear * @param unknown_type $lunarMonth * @param unknown_type $lunarDate * @param unknown_type $isLeapMonth * @param unknown_type $fmt */ static function formatLunar2($lunarYear, $lunarMonth, $lunarDate, $isLeapMonth, $fmt) { switch ($fmt) { case 'Y-m-d': $lunarYMD = sprintf('%04d-%02d-%02d', $lunarYear, $lunarMonth, $lunarDate); break; case 'YmdL': $lunarYMD = sprintf('%04d%02d%02d%s', $lunarYear, $lunarMonth, $lunarDate, $isLeapMonth ? 'L' : ' '); break; } return $lunarYMD; } /** * getLunarDate의 쓰기 편한 형태 * 2010-03-18 형태로 아규먼트를 넣을수 있음. * * @param unknown_type $Y_m_d */ function getLunarDateYmd($Y_m_d) { // print_r('$Y_m_d' .$Y_m_d); $format = "Y-m-d"; $tm = date_parse_from_format($format, $Y_m_d); $year = $tm["year"]; $month = $tm["month"]; $date = $tm["day"]; // print_r($tm); return $this->getLunarDate($year, $month, $date); } /** * * 음력으로 돌려줌. * 반환은 아래 형태 Array ( [year] => 2050 [month] => 03 [date] => 9 [is_leap_month] => 0 // 윤달 여부, 1이면 윤달. ) * 계산 범위 초과시 null * @param unknown_type $year * @param unknown_type $month * @param unknown_type $date */ function getLunarDate($year, $month, $date) { $this->error = ""; list($nearSol, $nearLuna) = $this->_getNearData($year, $month, $date); if (empty($nearSol)) return null; //키와 입력과의 날짜 차이만금, lunarPinDate에 더한다. $targetJD = cal_to_jd(CAL_GREGORIAN, $month, $date, $year); $keyJD = cal_to_jd(CAL_GREGORIAN, substr($nearSol, 4, 2), substr($nearSol, 6, 2), substr($nearSol, 0, 4)); $diff = $targetJD - $keyJD; $lunarYear = substr($nearLuna, 0, 4); $lunarMonth = substr($nearLuna, 4, 2); $lunarDate = substr($nearLuna, 6, 2); $lunarLeapMonth = substr($nearLuna, 8, 1); $lunarDate += $diff; return array( 'year' => $lunarYear, 'month' => $lunarMonth, 'date' => $lunarDate, 'is_leap_month' => $lunarLeapMonth == 'L' ? 1 : 0, ); } function _getNearData($year, $month, $date) { $ym = sprintf('%d%02d', $year, $month); $ymd = sprintf('%d%02d%02d', $year, $month, $date); if (false == isset($this->SolarToLunar[$ym])) { $this->error = '계산할수 있는 범위가 아닙니다.'; return null; } $pair = $this->SolarToLunar[$ym]; $lastLuna = ''; $lastSol = ""; // print_r($pair); foreach ($pair as $sol => $luna) { // print_r('$ymd < $keys[$i] ' . "$ymd < $keys[$i]\n"); // print_r("$ymd $sol $luna"); if ($ymd < $sol) { return array( $lastSol, $lastLuna ); } else if ($ymd == $sol) { return array( $sol, $luna ); } $lastSol = $sol; $lastLuna = $luna; } return array( $lastSol, $lastLuna ); } /** * 음력에 대응하는 양력 날짜 구하기. * @param unknown_type $lunarYear * @param unknown_type $lunarMonth * @param unknown_type $lunarDate * @param unknown_type $isLeapMonth */ function getSolarDate($lunarYear, $lunarMonth, $lunarDate, $isLeapMonth = false) { $this->error = ""; $nearKey = sprintf('%d%02d%02d%s', $lunarYear, $lunarMonth, 1, $isLeapMonth ? 'L' : ' '); if (false == isset($this->LunarToSolar[$nearKey])) { $this->error = '계산할수 있는 범위가 아닙니다.'; return null; } $solarPinDate = $this->LunarToSolar[$nearKey]; //키와 입력과의 날짜 차이만금, $solarPinDate 더한다. $keyDate = substr($nearKey, 6, 2); $keyIsLeapMonth = ('L' == substr($nearKey, 8, 1) ? true : false); if ($keyIsLeapMonth != $isLeapMonth) { $this->error = ($isLeapMonth ? "윤달" : "평달") . "$lunarYear-$lunarMonth-$lunarDate" . '는 없음.'; return null; } $diff = $lunarDate - $keyDate; $date = DateTime::createFromFormat('Ymd', $solarPinDate); // print_r($date); $date->add(new DateInterval('P' . $diff . 'D')); // print_r($date); return $date->format('Y-m-d'); } } /* 테스트 용 소스 */ $Y = 2012; $D = 04; $LunarCalendar = new LunarCalendar(); echo "Sol -> Moon Test<br />"; for($i=1;$i<=30;$i++) { $rst = $LunarCalendar->getLunarDate($Y, $D, $i); $rst = $LunarCalendar->formatLunar($rst); echo "{$Y}-{$D}-{$i} -> ".$rst."<br />"; } echo "<br />"; echo "<br />"; echo "Moon -> Sol Test<br />"; for($i=1;$i<=31;$i++) { $rst = $LunarCalendar->getSolarDate($Y, $D, $i, false); echo "{$Y}-{$D}-{$i} -> ".$rst."<br />"; } ?>