Markus, Fabian, and everyone:This very large patch installs Hebrew calendar support. Specifically, it installs support for the Hillel II calendar inaugurated in the fourth century AD (or CE or, I believe, vuZ).
I used my own adaptation of the algorithms developed by John Walker for his Calendar Converter site at http://www.fourmilab.ch/. However, I made this effort to improve upon his algorithms. Originally, his hebrew-to-JD conversion function was recursive, in that it called a function, that called another function, that called the hebrew-to-JD function all over again. Not being comfortable with that, I isolated enough code to predict a Julian Day for Rosh Hashanah in any given Hillel year, and called /that/ function in order to determine how long any given year was. By eliminating the recursion, I hope that I improved performance.
I haven't noticed any performance degredation, and I have tested this code on a practice page having eighteen annotated dates, one of which is in the Hillel model. The tooltip dialog balloons presently produce three printouts--Gregorian, Julian, and Hillel, in that order--for every annotated date.
The next model that I plan to implement will be the Biblical calendar, that uses direct astronomical calculations to retro-calculate the appearances of the new moon and the vernal equinox at the latitude of Jerusalem. Hopefully, it won't take long to calculate.
Temlakos
--- ../SemanticMediaWiki-lng/SMW_DV_Time.php 2009-08-12 13:47:15.000000000 -0400 +++ ./SMW_DV_Time.php 2009-08-12 18:16:15.000000000 -0400 @@ -28,6 +28,11 @@ * according to Julian rules, but the printout is either Gregorian or * Julian according to the epoch. * + * Hebrew (Hillel II) calendar support is provided. The algorithms are adapted from John + * Walker's 1999 algorithms, which he released into the public domain. Source: + * http://www.fourmilab.ch/documents/calendar/ Note that the algorithms have been slightly + * rewritten to remove a certain amount of recursion in the original scheme. + * * Other calendar-model support is planned. Historical dates are, however, * still experimental. * @@ -83,6 +88,7 @@ protected $m_xsdvalue = false; // cache for XSD value protected $m_gregvalue = false; // cache for (proleptic) Gregorian value protected $m_julvalue = false; // cache for (proleptic) Julian value + protected $m_hvalue = false; // cache for HIllel II value protected $m_pref = false; // holds a symbol for the calendar model protected $m_day = false; //Gregorian day, remains false if unspecified protected $m_month = false; //Gregorian month, remains false if unspecified @@ -95,9 +101,15 @@ protected $m_dayj = false; // Julian day, remains false if unspecified protected $m_monthj = false; // Julian month, remains false if unspecified protected $m_yearj = false; // Julian year, remains false if unspecified + protected $m_dayh = false; // Hillel II day, remains false if unspecified + protected $m_monthh = false; // Hillel II month, remains false if unspecified + protected $m_yearh = false; // Hillel II year, remains false if unspecified // The following are constant (array-valued constants are not supported, hence the declaration as private static variable): private static $m_months = array("January", "February", "March", "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December"); private static $m_monthsshort = array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); + private static $m_monthsh = array(1 => "Nisan", "Iyar", "Sivan", "Tammuz", "Av", "Elul", + "Tishrei", "Cheshvan", "Kislev", "Teveth", "Shevat", "Adar", "Veadar"); + private static $m_monthsshorth = array(1 => "Nis", "Iya", "Siv", "Tam", "Av", "Elu", "Tis", "Che", "Kis", "Tev", "She", "Ada", "Vea"); private static $m_formats = array( SMW_Y => array('year'), SMW_YM => array('year','month'), SMW_MY => array('month','year'), SMW_YDM => array('year','day','month'), SMW_YMD => array('year','month','day'), SMW_DMY => array('day','month','year'), SMW_MDY => array('month','day','year')); private static $m_daysofmonths = array ( 1 => 31, 2 => 29, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31 ); // Time zone monikers and their associated offsets in hours and fractions of hours @@ -118,6 +130,7 @@ // constant epochal values const J1582 = 2299160.5; // Date of switchover to Gregorian calendar + const HEBREW_EPOCH = 347995.5; // The Julian day start of the 19-cycle Hebrew calendar protected function parseUserValue($value) { global $smwgContLang; @@ -135,6 +148,9 @@ $this->m_dayj = false; $this->m_monthj = false; $this->m_yearj = false; + $this->m_dayh = false; + $this->m_monthh = false; + $this->m_yearh = false; $value = trim($value); // ignore whitespace $this->m_wikivalue = $value; @@ -149,7 +165,7 @@ //browse string for special abbreviations referring to year like AD, BC, and OS $is_yearbc = false; - if(preg_match("/(AD|BC|Gr|Jl|OS)/u", $filteredvalue, $match)){ + if(preg_match("/(AD|BC|Gr|Jl|OS|He)/u", $filteredvalue, $match)){ $this->m_pref = $match[0]; if ($this->m_pref == 'BC') { $is_yearbc = true; @@ -251,6 +267,8 @@ $globalvar = 'm_'.$globalvar; // (for searching this file) this is one of: m_year, m_month, m_day if($prelimModel == 'Jl') { $globalvar = $globalvar . 'j'; + } elseif($prelimModel == 'He') { + $globalvar = $globalvar . 'h'; } if (!$this->$globalvar) $this->$globalvar = intval($array[$i]); $i++; @@ -273,7 +291,15 @@ wfLoadExtensionMessages('SemanticMediaWiki'); $this->addError(wfMsgForContent('smw_nodatetime',$value)); return true; - } elseif ( ($this->m_year == false) && ($this->m_yearj < -4713) && ($this->m_timeoffset != 0) ) { //no support for time offsets if year < -4713 + } elseif ( ($this->m_dayh > 0) && ($this->m_dayh > SMWTimeValue::hebrew_month_days($this->m_yearh,$this->m_monthh)) ) { //date does not exist in Hillel calendar + wfLoadExtensionMessages('SemanticMediaWiki'); + $this->addError(wfMsgForContent('smw_nodatetime',$value)); + return true; + } elseif ( ($this->m_monthh > 0) && ($this->m_monthh > SMWTimeValue::hebrew_year_months($this->m_yearh)) ) { //month Veadar wrongly specified in Hillel calendar + wfLoadExtensionMessages('SemanticMediaWiki'); + $this->addError(wfMsgForContent('smw_nodatetime',$value)); + return true; + } elseif ( ($this->m_yearj != false) && ($this->m_yearj < -4713) && ($this->m_timeoffset != 0) ) { //no support for time offsets if year < -4713 wfLoadExtensionMessages('SemanticMediaWiki'); $this->addError(wfMsgForContent('smw_nodatetime',$value)); return true; @@ -327,29 +353,53 @@ return false; } + protected static function findMonthH($label) { + global $smwgContLang; + $retVal = $smwgContLang->findMonthH($label); + if ( $retVal !== false ) { + return $retVal; + } + $retVal = array_search($label, self::$m_monthsh); + if ( $retVal !== false ) { + return $retVal + 1; + } + $retVal = array_search($label, self::$m_monthsshorth); + if ( $retVal !== false ) { + return $retVal + 1; + } + return false; + } + protected function findPrelimModel() { if(($this->m_pref == 'BC') || ($this->m_pref == 'Jl') || ($this->m_pref == 'OS')) { return 'Jl'; // Assume Julian model if specified in any way, shape or form. } + if($this->m_pref == 'He') { // He model specified directly + return $this->m_pref; + } return 'Gr'; } protected function checkDigit($digit){ + $prelimModel = $this->findPrelimModel(); if(!is_numeric($digit)){ //check for alphanumeric day or month value - $prelimModel = $this->findPrelimModel(); if(preg_match("/[0-3]?[0-9](st|nd|rd|th)/u", $digit)) { //look for day value terminated by st/nd/th $dayVal = intval(substr($digit,0,strlen($digit)-2)); //remove st/nd/th if($prelimModel == 'Jl') { $this->m_dayj = $dayVal; + } elseif($prelimModel == 'He') { + $this->m_dayh = $dayVal; } else { $this->m_day = $dayVal; } return SMW_DAY; } - $monthnumber = SMWTimeValue::findMonth($digit); + $monthnumber = ($prelimModel == 'He') ? SMWTimeValue::findMonthH($digit) : SMWTimeValue::findMonth($digit); if ( $monthnumber !== false ) { if($prelimModel == 'Jl') { $this->m_monthj = $monthnumber; + } elseif($prelimModel == 'He') { + $this->m_monthh = $monthnumber; } else { $this->m_month = $monthnumber; } @@ -358,7 +408,11 @@ return 0; } elseif (intval($digit) >= 1 && intval($digit) <= 12) { //number can be a month, a day or a year (111) return SMW_DAY_MONTH_YEAR; - } elseif ((intval($digit) >= 1 && intval($digit) <= 31)) { //number can be a day or a year (011) + } elseif (intval($digit) >= 1 && intval($digit) <= 13 && $prelimModel == 'He'){ // A thirteenth month is allowed in the Hillel II model + return SMW_DAY_MONTH_YEAR; + } elseif (intval($digit) >= 1 && intval($digit) <= 30) { //number can be a day or a year up to 30 if Hillel II (011) + return SMW_DAY_YEAR; + } elseif (intval($digit) >= 1 && intval($digit) <= 31 && $prelimModel != 'He') { // number can be day or year if Julian or Gregorian return SMW_DAY_YEAR; } elseif (is_numeric($digit)) { //number can just be a year (011) return SMW_YEAR; @@ -376,7 +430,7 @@ $this->m_pref = ''; // Erase the OS marker; it will not be needed after this. return 'Jl'; // Old Style dates are converted per Julian rules. } - if (($this->m_pref == 'Gr') || ($this->m_pref == 'Jl')) {//Specified calendar models + if (($this->m_pref == 'Gr') || ($this->m_pref == 'Jl') || ($this->m_pref == 'He')) {//Specified calendar models return $this->m_pref; } // Model unspecified: must be determined by examination of the date parts. @@ -421,10 +475,12 @@ if ($this->isValid() && ($linked !== NULL) && ($linked !== false)) { $this->makeGregorianValue(); $this->makeJulianValue(); + $this->makeHillelValue(); SMWOutputs::requireHeadItem(SMW_HEADER_TOOLTIP); return '<span class="smwttinline">' . $this->m_caption . '<span class="smwttcontent">' . $this->m_gregvalue . ' <br />' . - $this->m_julvalue . '</span></span>'; + $this->m_julvalue . ' <br />' . + $this->m_hvalue . '</span></span>'; } else { return $this->m_caption; } @@ -491,6 +547,8 @@ switch ($model) { case 'Jl': return $this->m_yearj; + case 'He': + return $this->m_yearh; default: return $this->m_year; } @@ -507,6 +565,8 @@ switch ($model) { case 'Jl': return ($this->m_monthj != false)?$this->m_monthj:$default; + case 'He': + return ($this->m_monthh != false)?$this->m_monthh:$default; default: return ($this->m_month != false)?$this->m_month:$default; } @@ -522,6 +582,8 @@ switch ($model) { case 'Jl': return ($this->m_dayj != false)?$this->m_dayj:$default; + case 'He': + return ($this->m_dayh != false)?$this->m_dayh:$default; default: return ($this->m_day != false)?$this->m_day:$default; } @@ -571,6 +633,7 @@ protected function makePrintoutValue() { $this->makeGregorianValue(); $this->makeJulianValue(); + $this->makeHillelValue(); } protected function makeGregorianValue() { @@ -615,6 +678,23 @@ } } + protected function makeHillelValue() { + global $smwgContLang; + if ($this->m_hvalue === false) { + //MediaWiki date function is not applicable any more (no support for BC Dates) + $this->m_hvalue = number_format($this->m_yearh, 0, '.', '') . ' He'; // note: there should be no digits after the comma anyway + if ($this->m_monthh) { + $this->m_hvalue = $smwgContLang->getMonthLabelH($this->m_monthh) . " " . $this->m_hvalue; + } + if ($this->m_dayh) { + $this->m_hvalue = $this->m_dayh . " " . $this->m_hvalue; + } + if ($this->m_time) { + $this->m_hvalue .= " " . $this->m_time; + } + } + } + protected static function normalizeValue($value){ if(strlen($value) == 1) { $value = "0".$value; @@ -664,6 +744,8 @@ case "Jl": $this->julian2JD(); break; + case "He": + $this->hebrew2JD(); } $this->createTime(); } @@ -710,6 +792,7 @@ protected function JD2Date() { $this->JD2Julian(); $this->JD2Gregorian(); + $this->JD2Hebrew(); if($this->m_time != false) { // Do not fill this in if it was not filled in to begin with $this->fracToTime(); } @@ -746,5 +829,128 @@ $this->m_monthj = ($this->m_format >= 2) ? $m : false; $this->m_dayj = ($this->m_format == 3) ? ($b - $d - intval(30.6001 * $e)) : false; } +// Rules for determining whether a year is a leap or embolismic year, or not. + protected static function hebrew_leap($year) { + return ((($year * 7) + 1) % 19) < 7; + } + +// Number of months in a year, depending on whether it is an embolismic year. + protected static function hebrew_year_months($year) { + return SMWTimeValue::hebrew_leap($year) ? 13 : 12; + } + +// Gives the start of the year and accounts for never starting a year on the first, +// fourth, or sixth day of the week. The start is relative to the HEBREW_EPOCH. + protected static function hebrew_delay_1($year) { + $months = floor(((235 * $year) - 234) / 19); + $parts = 12084 + (13753 * $months); // A part of time is 1/18th of a minute. + $day = ($months * 29) + floor($parts / 25920); + + if (((3 * ($day + 1)) % 7) < 3) { + $day++; + } + return $day; + } + +// Delays the start of the year by up to two days depending on the lengths of the +// next and previous years, as determined by the hebrew_delay_1 function. + protected static function hebrew_delay_2($year) { + $last = SMWTimeValue::hebrew_delay_1($year - 1); + $present = SMWTimeValue::hebrew_delay_1($year); + $next = SMWTimeValue::hebrew_delay_1($year + 1); + + return (($next - $present) == 356) ? 2 : + ((($present - $last) == 382) ? 1 : 0); + } + + protected static function rosh_hashanah_h($year) { +// Gives the Julian Day of Rosh Hashanah in any given Hillel year + return self::HEBREW_EPOCH + SMWTimeValue::hebrew_delay_1($year) + + SMWTimeValue::hebrew_delay_2($year) + 2; + } + +// Gives the length of any given year by subtracting one Rosh Hashanah from another + protected static function hebrew_year_days($year) { + return SMWTimeValue::rosh_hashanah_h($year + 1) - SMWTimeValue::rosh_hashanah_h($year); + } + +// Gives the length of any given month in any given year. + protected static function hebrew_month_days($year, $month) { +// The months of Iyar, Tammuz, Elul, Teveth, and Veadar (if observed) +// are always 29 days long, according to law. + if ($month == 2 || $month == 4 || $month == 6 || + $month == 10 || $month == 13) { + return 29; + } +// Adar is 29 days long on any year other than a leap year. + if ($month == 12 && !SMWTimeValue::hebrew_leap($year)) { + return 29; + } +// If it's Cheshvan, days depend on length of year + if ($month == 8 && ((SMWTimeValue::hebrew_year_days($year) % 10) != 5)) { + return 29; + } +// Similarly, Kislev varies with the length of year + if ($month == 9 && ((SMWTimeValue::hebrew_year_days($year) % 10) == 3)) { + return 29; + } +// 30 days have Nisan, Sivan, Av, Elul, and Tishrei--and Cheshvan and Kislev if they +// violate the above conditions, and Adar if it is actually the intercalary twelfth +// month. + return 30; + } + + protected static function hebrew_jd($year, $month, $day) { + + $months = SMWTimeValue::hebrew_year_months($year); + $jd = SMWTimeValue::rosh_hashanah_h($year) + $day - 1; + + if ($month < 7) { + for ($mon = 7; $mon <= $months; $mon++) { + $jd += SMWTimeValue::hebrew_month_days($year, $mon); + } + for ($mon = 1; $mon < $month; $mon++) { + $jd += SMWTimeValue::hebrew_month_days($year, $mon); + } + } else { + for ($mon = 7; $mon < $month; $mon++) { + $jd += SMWTimeValue::hebrew_month_days($year, $mon); + } + } + return $jd; + } + + protected function hebrew2JD() { + $this->m_jd = SMWTimeValue::hebrew_jd($this->getYear('He'),$this->getMonth('He',7),$this->getDay('He')); + $this->m_format = ($this->m_dayh != false) ? 3 : (($this->m_monthh != false) ? 2 : 1); + } + + protected function JD2Hebrew() { + $jd = floor($this->m_jd - 0.5) + 0.5; // Convert to the nearest midnight. +// Now count the years since or before (negative) the Hebrew epoch,... + $count = floor((($jd - self::HEBREW_EPOCH) * 98496.0) / 35975351.0); +// and subtract 1. This is the first-order estimate of the year. + $this->m_yearh = $count - 1; +// Advance the year until the next instance of Tishrei would be in the future. + for ($i = $count; $jd >= SMWTimeValue::rosh_hashanah_h($i); $i++) { + $this->m_yearh++; + } + if($this->m_format == 1) { // Year-only precision + $this->m_monthh = false; + $this->m_dayh = false; + return; + } +// Set the month at either Tishrei or Nisan, whichever is closer: + $first = ($jd < SMWTimeValue::hebrew_jd($this->m_yearh, 1, 1)) ? 7 : 1; + $this->m_monthh = $first; +// Advance the month until the next month-end would be at or ahead of the present day. + for ($i = $first; + $jd > SMWTimeValue::hebrew_jd($this->m_yearh, $i, SMWTimeValue::hebrew_month_days($this->m_yearh, $i)); + $i++) { + $this->m_monthh++; + } +// Now set the day to count from the start of the current month. + $this->m_dayh = ($this->m_format < 3) ? false : (($jd - SMWTimeValue::hebrew_jd($this->m_yearh, $this->m_monthh, 1)) + 1); + } }
------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july
_______________________________________________ Semediawiki-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/semediawiki-devel
