download this file: class.calendar.php view text/plain: class.calendar.php file encoding: EUC-KR [goback]
<?php
##
## this file name is 'class.calendar.php'
##
## calendar object
##
## [author]
## - Chilbong Kim, <san2(at)linuxchannel.net>
## - 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 '<PRE>';
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);
**********************/
?>