## - http://linuxchannel.net/ ## ## [changes] ## - 2011.11.06 : add some utils ## - 2011.04.29 : comment patch(UT = TT - delta T) ## - 2011.04.24 : bug fixed, calendar::date('D') ## - 2010.05.20 : bug fixed, calendar::date('W') of ISO-8601 ## - 2010.05.19 : added calendar::date('J'), is a JD ## - 2010.05.18 : support calendar::date('I'), DST(daylight saving time) and all support of date() format. ## - 2009.06.08 : some ## - 2007.07.28 : support win32 PHP4(on Microsoft Windows) and Unix ## - 2005.04.12 : new build ## ## [valid date] ## - unix timestamp base: 1902-01-01 00:00:00 <= date <= 2037-12-31 23:59:59 (guess) ## - JD(Julian Day) base: BC 4713-01-01 12:00 GMT <= Gregorian date <= AD 9999 (guess) ## ## [download & online source view] ## - http://ftp.linuxchannel.net/devel/php_calendar/ ## ## [demo] ## - http://linuxchannel.net/gaggle/calendar.php ## ## [references] ## - http://www.linuxchannel.net/docs/solar-24terms.txt ## - http://www.linuxchannel.net/docs/lunar.txt ## - http://ftp.linuxchannel.net/devel/php_solar/ ## - http://ftp.linuxchannel.net/devel/php_lunar/ ## - http://www.merlyn.demon.co.uk/ // Astronomy and Astronautics ## - http://www.boogle.com/info/cal-overview.html ## - http://star-www.st-and.ac.uk/~fv/webnotes/index.html // Positional Astronomy ## ## [time format] ## - UT = TT - dT ## - DT = in korean 'yeok-hak-si' ## - TDT(Terrestrial Dynamical Time) = DT(Dynamical Time) = TT(Terrestrial Time) ## - local = time(),date() ## - UT = gmtime(),gmdate() or local-time_offset ## - TT = UT + dT // use the astro caculating ## - JD <-> UT <-> GST <-> LST ## <-------------> ## <-------------> ## <--------------------> ## - jd2ut(JD,-time_offset) <-> ut2jd(ut,+time_offset) ## - ut2gst(ut) <-> gst2ut(gst) // ¾Æ·¡ ÇÔ¼ö ## - gst2lst(gst,+lon_offset) <-> lst2gst(lst,-lon_offset) ## ## [degrees format] ## - deg2hms(deg) <-> hms2deg(hms) ## - deg2dms(deg) <-> dms2deg(dms) ## - deg2h(deg) <-> h2deg(h) ## - hms2dms(hms) <-> dms2hms(dms) ## - hms2h(hms) <-> h2hms(h) ## - dms2h(dms) <-> h2dms(h) ## ## [compare 1. PHP4(win32) internal functions VS this method(object)] ## - time() - ## - date() _date() // private, base on unix timestamp, BC 4313 ~ AD 9999(guess) ## - mktime() _mktime() // private, support native value, BC 4313 ~ AD 9999(guess) ## ## [compare 2. PHP4(win32) calendar module VS this method(object)] ## - gregoriantojd() mkjd() // public, support hour, minute, seconds, BC 4313 ~ AD 9999(guess) ## - jdtogregorian() date() // public, same above, but same as `date()' ## - jddayofweek() jddayofweek // public, similar ## - cal_days_in_month() days_in_month() // public, similar ## - unixtojd() _utime2jd() // private, same above ## - jdtounix() _jd2utime() // private, same above ## ## [usage] -- see that last row of this source ## $jd = calendar::mkjd(23,59,59,12,31,1901); ## echo calendar::date('Y-m-d H:i:s T',$jd); ## class calendar { ## private, get Julian day -- same as gregoriantojd() ## ## http://en.wikipedia.org/wiki/Julian_day ## http://ko.wikipedia.org/wiki/%EC%9C%A8%EB%A6%AC%EC%9A%B0%EC%8A%A4%EC%9D%BC ## ftp://ssd.jpl.nasa.gov/pub/eph/export/C-versions/hoffman/ ## http://blog.jidolstar.com/482 ## ## Julian date ## JD 0.0 => BC 4713-01-01 12:00 GMT <= BC 4713-01-01 21:00 KST ## function &_getjd($Y, $M=1, $D=1, $H=21, $I=0, $S=0, $tojulian=FALSE) { $H -= date('Z')/3600; // local zone to GMT if(func_num_args() < 3) // Y is unix_timestamp { list($Y,$M,$D,$H,$I,$S) = explode(' ',calendar::_date('Y n j G i s',$Y-date('Z'))); } if($M < 3) { $M += 12; $Y--; } $S += 64; // is J2000.0 delta 'T', patch san2@2007.07.28 $D += ($H/24.0) + ($I/1440.0) + ($S/86400.0); $A = floor($Y/100.0); $B = $tojulian ? 0 : (2.0 - $A + floor($A/4.0)); // juliantojd() $B = 0 $JD= sprintf('%.13f',floor(365.25*($Y+4716.0))+floor(30.6001*($M+1.0))+$D+$B-1524.5); $D = sprintf('%.13f',$JD-2451545.0); // float, number of days $J = sprintf('%.4f',2000.0+($D/365.25)); // // Jxxxx.xxxx format $T = sprintf('%.13f',$D/36525.0); // // Julian century return array($JD,$J,$D,$T); } ## private, get date(gregorian) from JD -- same as jdtogregorian() ## ## JD to `YYYY MM DD HH II SS', JD is UT ## function &_todate($JD) { // JD >= 2299160.5 gregorian $JD += date('Z')/86400; // JD to local zone(JD) $JD -= 64/86400; // is J2000.0 delta 'T', patch san2@2007.07.28 $Z = $JD + 0.5; // float $W = (int)(($Z-1867216.25) / 36524.25); $X = (int)($W / 4); $A = (int)($Z + 1 + $W - $X); $B = (int)($A + 1524); $C = (int)(($B-122.1) / 365.25); $D = (int)(365.25 * $C); // is not $D = ($B - 122.1) $E = (int)(($B-$D) / 30.6001); $F = (int)(30.6001 * $E); // is not $F = ($B -$D) $_d = $B - $D - $F; $_m = $E - 1; $_y = $C - 4716; $JD -= 0.5; // UT to GMT -12.0H $JD = ($JD - (int)$JD) * 24.0; $_h = (int)$JD; $JD = ($JD - $_h) * 60.0; $_i = (int)$JD; $JD = ($JD - $_i) * 60.0; $_s = round($JD); if($_s > 59) { $_s -= 60; $_i++; } else if($_s < 0) { $_s += 60; $_i--; } if($_i > 59) { $_i -= 60; $_h++; } else if($_i < 0) { $_i += 60; $_h--; } if($_h > 23) { $_h -= 24; $_d++; } else if($_h < 0) { $_h +=24; $_d--; } if($_m > 12) { $_m -= 12; $_y++; } else if($_m < 0) { $_m +=12; $_y--; } return array($_y,$_m,$_d,$_h,$_i,$_s); } ## private, get JD(julian day) from unix timestamp -- same as unixtojd() ## ## D -- get the number of days from base JD ## D = JD(Julian Day) - 2451545.0, base JD(J2000.0) ## ## base position (J2000.0), 2000-01-01 12:00:00 GMT ## as mktime(12,0,0-64,1,1,2000) == 946695536 unix timestamp at KST, -64 is delta 'T' ## as gmmktime(12,0,0-64,1,1,2000) == 946727936 unix timestamp at GMT, -64 is delta 'T' ## ## valid JD: 1902-01-01 00:00:00 ZONE <= JD <= 2037-12-31 23:59:59 ZONE ## function &_utime2jd($utime) { $D = $utime - 946727936; // number of time $D = sprintf('%.13f',$D/86400); // float, number of days $JD= sprintf('%.13f',$D+2451545.0); // float, Julian Day //$J = sprintf('%.4f',2000.0+($D/365.25)); // Jxxxx.xxxx format //$T = sprintf('%.13f',$D/36525.0); // Julian century return $JD; // float } ## private, get unix timestamp from JD -- same as jdtounix() ## ## 1970-01-01 12:00:00 GMT = 2440587.6257407409139 JD = J1970.0 ## valid JD: 1902-01-01 00:00:00 ZONE <= JD <= 2037-12-31 23:59:59 ZONE ## function &_jd2utime($JD) { $JD -= 2440587.6257407409139; // convert to base JD(J1970.0), J2000.0 delta 'T', but it's not need $seconds = round($JD*86400); // convert to time seconds base on 1970-01-01 00:00:00 $seconds += 43200; // to GMT -12H(43200 seconds) $seconds -= date('Z'); // to local time zone return $seconds; } ## private, check datetime that it's null or not null ## function &__check_datetime($argc, &$Y, &$M, &$D, &$H, &$I, &$S) { if($argc >= 6) return TRUE; list($Y,$_M,$_D,$_H,$_I,$_S) = explode(' ',date('Y n j G i s',time())); if($argc < 5) $D = $_D; if($argc < 4) $M = $_M; if($argc < 3) $S = $_S; if($argc < 2) $I = $_I; if($argc < 1) $H = $_H; } ## public, make JD -- match to mktime() ## ## Julian date ## J0.0 = BC 4713-01-01 12:00 GMT = BC 4713-01-01 21:00 KST ~ AD 9999 ## function &mkjd($H=21, $I=0, $S=0, $M=1, $D=1, $Y=NULL) { calendar::__check_datetime(func_num_args(),$Y,$M,$D,$H,$I,$S); list($JD) = calendar::_getjd($Y,$M,$D,$H,(int)$I,(int)$S); return $JD; // folat, JD is UT base } ## private, get unix timestamp from date -- same as mktime() ## ## valid date: 1902-01-01 00:00:00 ZONE <= date <= 2037-12-31 23:59:59 ZONE ## function &_mktime($H=9, $I=0, $S=0, $M=1, $D=1, $Y=NULL) { if($Y>1970 && $Y<2038) return mktime($H,$I,$S,$M,$D,$Y); calendar::__check_datetime(func_num_args(),$Y,$M,$D,$H,$I,$S); $JD = calendar::mkjd($H,$I,$S,$M,$D,$Y); $utime = calendar::_jd2utime($JD); return $utime; } ## private, same as `date()' function, base on unix timestamp(support Microsoft Windows PHP4) ## ## valid date: 1902-01-01 00:00:00 ZONE <= date <= 2037-12-31 23:59:59 ZONE ## function &_date($format, $utime=NULL) { if($utime === NULL) $utime = time(); if($utime>=0 && $utime<2145884400) return date($format,$utime); $JD = calendar::_utime2jd($utime); $str = calendar::date($format,$JD); return $str; } ## public, same as `date()' function, but base on JD by UT(delta T) ## ## valid JD: BC 4713-01-01 12:00 GMT ~ AD 9999 ## function &date($format, $JD=NULL) { static $_weeks = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); static $_months = array('January','February','March','April','May','June','July','August', 'September','Octorber','November','December'); static $_ordinals = array(1=>'st',21=>'st',31=>'st',2=>'nd',22=>'nd',3=>'rd',23=>'rd'); if(func_num_args()<2 || $JD==NULL) $JD = calendar::mkjd(); // current JD(UT) if(!$format || is_array($format)) return calendar::_todate($JD); // array list($Y,$M,$D,$H,$I,$S) = calendar::_todate($JD); ## get DST(daylight saving time), patch san2@2010.05.19 ## if($Y>=1916 && $Y<2038) { list($_DST,$_Z,$_O,$_T) = explode(' ',date('I Z O T',mktime(12,0,0,$M,$D,$Y))); } else { $_DST = 0; list($_Z,$_O,$_T) = explode(' ',date('Z O T')); // $_O example +0900 } ## patch san2@2010.05.19 ## if($Y>1970 && $Y<2038) $_U = mktime($H,$I,$S,$M,$D,$Y); else $_U = calendar::_jd2utime($JD); $_Y = sprintf('%04d',$Y); $_M = sprintf('%02d',$M); $_D = sprintf('%02d',$D); $_H = sprintf('%02d',$H); $_I = sprintf('%02d',$I); $_S = sprintf('%02d',$S); $_w = calendar::jddayofweek($JD + ($_Z/86400)); // JD apply to local TimeZone $_W = calendar::weeknumber($Y,$M,$D); // ISO-8601 $_R = substr($_weeks[$_W],0,3).", $_D ".substr($_months[$M-1],0,3)." $H:$I:$S $_O"; $_P = substr($_O,0,3).':'.substr($_O,-2); $_C = "${_Y}-${_M}-${_D}T${_H}:${_I}:${_S}${_P}"; $_N = ($_W == 0) ? 7 : $_W; $_o = ($M==12 && $_W==1) ? $Y+1 : (($M==1 && $_W>=52) ? $Y-1 : $Y); $r = ''; $nextskip = FALSE; $l = strlen($format); for($i=0; $i<$l; $i++) { $char = $format[$i]; if(!trim($char)) { $r .= $char; continue; } if($nextskip) { $r .= $char; $nextskip = FALSE; continue; } // patch san2@2010.05.19 if($char == '\\') { $nextskip = TRUE; continue; } else $nextskip = FALSE; switch($char) { case 'a': $r .= ($H<12) ? 'am' : 'pm'; break; case 'A': $r .= ($H<12) ? 'AM' : 'PM'; break; case 'B': $r .= calendar::itime($H,$I,$S); break; case 'c': $r .= $_C; break; // ISO 8601 date (added in PHP5) case 'd': $r .= $_D; break; case 'D': $r .= substr($_weeks[$_w],0,3); break; case 'F': $r .= $_months[$M-1]; break; case 'g': $r .= (($H-1) % 12) + 1; break; case 'G': $r .= $H; break; case 'h': $r .= sprintf('%02d',(($H-1)%12)+1); break; case 'H': $r .= $_H; break; case 'i': $r .= $_I; break; case 'I': $r .= $_DST; break; case 'j': $r .= $D; break; case 'J': $r .= $JD; break; case 'l': $r .= $_weeks[$_W]; break; case 'L': $r .= calendar::isleap($Y); break; case 'm': $r .= $_M; break; case 'M': $r .= substr($_months[$M-1],0,3); break; case 'n': $r .= $M; break; case 'N': $r .= $_N; break; // ISO-8601, day of the week, 1(Monday) ~ 7(Sunday) case 'o': $r .= $_o; break; // ISO-8601 year number case 'O': $r .= $_O; break; case 'P': $r .= $_P; break; case 'r': $r .= $_R; break; case 's': $r .= $_S; break; case 'S': $r .= $_ordinals[$D] ? $_ordinals[$D] : 'th'; break; case 't': $r .= calendar::days_in_month($Y,$M); break; case 'T': $r .= $_T; break; case 'u': $r .= date('u'); break; case 'U': $r .= $_U; break; case 'w': $r .= $_w; break; // JD to local zone case 'W': $r .= sprintf('%02d',$_W); break; // ISO-8601 case 'y': $r .= substr($_Y,-2); break; case 'Y': $r .= $Y; break; case 'z': $r .= calendar::dayofyear($Y,$M,$D); break; case 'Z': $r .= $_Z; break; // KST zone +9H, in seconds default : $r .= $char; break; } } return $r; // string } ## public, get leap year ## ## #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) ## ## +-- 4*Y ! // normal ## `-- 4*Y ## |-- 100*Y ! // leap ## `-- 100*Y ## |-- 400*Y ! // normal ## `-- 400*Y // leap ## ## but, 4000*Y is not normal year, is leap year ## http://user.chollian.net/~kimdbin/re/leap_year.html ## function &isleap($year) { if($year%4) return FALSE; else if($year%100) return TRUE; else if($year%400) return FALSE; return TRUE; // else 400*Y } ## public, get week idx ## ## 0(sun), 1(mon), 2(tue), 3(wed), 4(thu), 5(fri), 6(sat) ## function &jddayofweek($JD) { return floor($JD+1.5)%7; // integer } function &dayofyear($Y, $M, $D) { list($JDE) = calendar::_getjd($Y,$M,$D); list($JDS) = calendar::_getjd($Y,1,1); return (int)($JDE - $JDS); } ## ISO-8601, start on Monday ## function &weeknumber_f($Y, $M, $D) { list($JD) = calendar::_getjd($Y,1,1); $widx = calendar::jddayofweek($JD); $days = calendar::dayofyear($Y,$M,$D); //$midx = ($widx<0) ? 6 : $widx; //$days = ($midx<1) ? ($days+$midx) : ($days+$midx-7); $midx = ($widx==0) ? 7 : $widx; // to ISO-8601 $days += ($midx>1) ? ($midx-7-1) : 0; $n = ceil($days/7); if($n >= 52) // last week { list($JD) = calendar::_getjd($Y,12,31); $lidx = calendar::jddayofweek($JD); if($widx>0 && $lidx>0) $n = 1; } else if($n <= 1) // first week { list($JD) = calendar::_getjd($Y-1,1,1); $widx = calendar::jddayofweek($JD); $n = ($widx>1) ? 52 : 53; } return $n; // integer } ## ISO-8601, start on Thursday ## patch san2@2010.05.20 ## function &weeknumber($Y, $M, $D) { list($JD) = calendar::_getjd($Y,1,1); $widx = calendar::jddayofweek($JD) - 1; $days = calendar::dayofyear($Y,$M,$D); $midx = ($widx<0) ? 6 : $widx; $days = ($midx<4) ? ($days+$midx) : ($days+$midx-7); $n = floor($days/7) + 1; if($n == 0) // ok, first week or last of preious year { list($JD) = calendar::_getjd($Y-1,1,1); $widx = calendar::jddayofweek($JD); $n = ($widx>4) ? 52 : 53; } else if($n > 52) // last week or first week of next year { list($JD) = calendar::_getjd($Y,12,31); $widx = calendar::jddayofweek($JD); if($widx>0 && $widx<4) $n = 1; // Monday ~ Wednesday } return $n; // integer } ## public, get swatch internet time, base BMT = GMT + 1 ## same as date('B') ## function &itime($H, $I, $S) { $B = ($H-(date('Z')/3600)+1)*41.666 + $I*0.6944 + $S*0.01157; $B = ($B>0) ? $B : $B+1000.0; return sprintf('%03d',$B); } /*** function &days_in_month($year, $month, $JDS=0) { list($JDS) = calendar::_getjd($year,$month,1); list($JDE) = calendar::_getjd($year,$month+1,1); $term = (int)($JDE - $JDS); return $term; // integer } ***/ ## public ## function &days_in_month($year, $month) { static $months = array(31,0,31,30,31,30,31,31,30,31,30,31); $n = $months[$month-1]; $n = $n ? $n : (calendar::isleap($year) ? 29 : 28); return $n; // integer } ## public ## function &month_info($year, $month) { if($year<1902 || $year>2037) { list($JD) = calendar::_getjd($year,$month,1); $term = calendar::days_in_month($year,$month); $week = calendar::jddayofweek($JD); // week idx $minfo = array($week,$term); } else { $utime = mktime(23,59,59,$month,1,$year); $minfo = explode(' ',date('w t',$utime)); } return $minfo; // array($week,$term) } ## utils ## ## - deg2hms(deg) <-> hms2deg(hms) ## - deg2dms(deg) <-> dms2deg(dms) ## - deg2h(deg) <-> h2deg(h) ## - hms2dms(hms) <-> dms2hms(dms) ## - hms2h(hms) <-> h2hms(h) ## - dms2h(dms) <-> h2dms(h) ## public ## function °2dms($deg, $singed=FALSE) { if($singed) $singed = '+'; if($deg <0) { $singed = '-'; $deg = abs($deg); } $d = floor($deg); $m = floor(fmod($deg*60,60)); $s = sprintf('%.4f',fmod($deg*3600,60)); return array($singed.$d,$m,$s); } /*** function &_calendar($year, $month) { list($week,$term) = calendar::month_info($year,$month); $eidx = 3; $sat = 7 - $week; $chk = $sat + 28; $sats = array($sat,$sat+7,$sat+14,$sat+21); $suns = array($sat-6,$sat+1,$sat+8,$sat+15); $refs = range(1,$term); if($chk <= $term) { $eidx++; $sats[] = $chk; $suns[] = $sat + 22; } ## check last sunday ## if($term-$sats[$eidx] > 0) { $sats[] = $sats[$eidx] + 7; $suns[] = $sats[$eidx] + 1; $eidx++; } ## rewrite array ## for($i=0; $i<=$eidx; $i++) { for($j=$suns[$i]; $j<=$sats[$i]; $j++) $r[$i][] = &$refs[$j-1]; } //ksort($r); echo "$week;$term\n"; print_r($suns); print_r($sats); print_r($r); } ***/ ## public ## function &calendar($year, $month) { list($week,$term) = calendar::month_info($year,$month); $eidx = 3; $refs = range(1,$term); // reference of days $fsat = 7 - $week; // first Saturday ## make index array such as (Sun,Sat) ## for($i=0; $i<=3; $i++) { $isat = $fsat + ($i*7); // index of Saturday $idxs[] = array($isat-6,$isat); } ## check last Saturday and Sunday ## if(($fsat+28) <= $term) $idxs[++$eidx] = array($fsat+22,$fsat+28); if(($term-$idxs[$eidx][1]) > 0) { $idxs[] = array($idxs[$eidx][0]+7,$idxs[$eidx][1]+7); $eidx++; } ## rewrite days ## for($i=0; $i<=$eidx; $i++) { for($j=$idxs[$i][0]; $j<=$idxs[$i][1]; $j++) $r[$i][] = &$refs[$j-1]; } return $r; // array } } // end of class /**** example ********* $_y = 2040; $_m = 12; $r = calendar::calendar($_y,$_m); echo '
';
echo " $_m $_y\n";
echo "Su Mo Tu We Th Fr Sa\n";
$size = sizeof($r);
for($i=0; $i<$size; $i++)
{
printf("%2s",$r[$i][0]);
for($j=1; $j<7; $j++) printf("%3s",$r[$i][$j]);
echo "\n";
}
print_r($r);
**********************/
?>