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

Reply via email to