Markus (and Fabian):

Here is another, much larger patch for the file SMW_DV_Time.php. This patch implements support for the Julian calendar, as well as the Gregorian, and also ensures that the internally-stored Julian Day will in fact conform to astronomical convention. The createJD() now takes the parameter $model, which gives it an instruction as to which set of calculations it ought to apply. JD2Date() selects either the Julian or the Gregorian date, depending on whether the Julian Day is earlier than, or the same as or later than, the Julian Day for October 15, 1582, the day on which Pope Gregory IX proclaimed his new modification to the calendar originally established by Gaius Julius Caesar Dictator.

With this new patch, all dates will print out in Browse Properties, or the Factbox, according to the proper calendar, either Julian or Gregorian. The caption will remain the same as the annotated value.

Dates between October 5-14 inclusive in the year 1582 will calculate to the proper Julian date and print out accordingly; hence they will appear as September 25 through October 4 inclusive.

Old Style support is my next project. Again, that requires adjusting years of dates between January 1 and March 25, and calculating a JD from Julian calendar rules, while back-converting to the Gregorian calendar.

After that will come Hebrew calendar support. That will probably be the most difficult task, and the riskiest, primarily on account of the auxiliary functions that the algorithms require. Once again, I commend you for developing an algorithm for Gregorian calendar conversion and back-conversion that does /not/ require auxiliary function calls. I needed to modify it only slightly to normalize the calculated JD to noon UTC. I didn't even need to specify a Gregorian epoch (that is, 1 January 1 AD). But the Hebrew calendar will not only require auxiliary functions but will also be /iterative/, especially for back-conversion. I will also need to decide what model to use for the XSD value.

Temlakos
--- ../SemanticMediaWiki-lng/SMW_DV_Time.php	2009-08-10 16:42:58.000000000 -0400
+++ ./SMW_DV_Time.php	2009-08-10 23:40:13.000000000 -0400
@@ -6,23 +6,24 @@
 
 /**
  * This datavalue captures values of dates and times. All dates and times refer to
- * the "local time" of the server (or the wiki). A wiki may define what timezone this
- * refers to by common conventions. For export, times are given without timezone
- * information. However, time offsets to that local time are supported (see below).
+ * Coordinated Universal Time (UTC), or the local time of the wiki server.
+ * A wiki may define what timezone this refers to by common conventions.
+ * For export, times are given without timezone information. However, time offsets to
+ * that local time, or to UTC, are supported (see below). The difference is arbitrary.
  *
  * Dates can be given in many formats, using numbers, month names, and abbreviated
  * month names. The preferred interpretation of ambiguous dates ("1 2 2008" or even
  * "1 2 3 BC") is controlled by the language file, as is the local naming of months.
  * English month names are always supported. Currently, the additions "AM", "PM", "BC",
- * and "AD" are supported, but not localised to other languages.
+ * and "AD" are supported, but not (yet) localised to other languages.
  *
- * There is currently no support for different calendar models or conversion between
- * them. All dates are supposed to refer to Gregorian calendar (or its extension to
- * the past, the proleptic Gregorian claendar). Attention: this may change in future
- * versions, and historical dates may be treated as Julian calendar dates in certain
- * ranges. Consider historical dates to be experimental.
+ * All dates are supposed to refer either to the Gregorian calendar (October 15, 1582
+ * or later), or to the Julian calendar (earlier than October 15, 1582 Gregorian). Any
+ * date specified between October 5 and October 14, 1582 inclusive, will be recalculated
+ * according to the Julian calendar. Other calendar-model support is planned.
+ * Historical dates are, however, still experimental.
  *
- * It is able to handle dates across history with full precision for storing, and
+ * This type can handle dates across history with full precision for storing, and
  * substantial precision for sorting and querying. The range of supported past dates
  * should encompass the Beginning of Time according to most of today's theories. The
  * range of supported future dates is limited more strictly, but it does also allow
@@ -41,25 +42,30 @@
  * The implementation notices and stores whether parts of a date/time have been
  * omitted (as in "2008" or "May 2007"). For all exporting and sorting purposes,
  * incomplete dates are completed with defaults (usually using the earliest possible
- * time, i.e. interpreting "2008" as "Jan 1 2008 00:00:00"). But the information
+ * time, i.e. interpreting "2008" as "Jan 1 2008 00:00:00"). The information
  * on what was unspecified is kept internally for improving behaviour e.g. for
  * outputs (defaults are not printed when querying for a value). Functions are
  * provided to access the individual time components (getYear, getMonth, getDay,
  * getTimeString), and those can also be used to find out what was unspecified.
  *
- * Time offests are supported (e.g. "1 1 2008 12:00-2:00"). As explained above, those
- * refer to the local time. Time offsets take leap years into account, e.g. the date
+ * Time offsets are supported (e.g. "1 1 2008 12:00-2:00"). A time offset is the number
+ * of hours:minutes to be added to UTC (GMT) to obtain the local-clock reading. Time zone
+ * monikers (EST, CST, CET, MEZ, etc.) and military time (e.g. 1240Z, equivalent to 12:40
+ * UTC) are also supported.
+ *
+ * Time offsets take leap years into account, e.g. the date
  * "Feb 28 2004 23:00+2:00" is equivalent to "29 February 2004 01:00:00", while
  * "Feb 28 1900 23:00+2:00" is equivalent to "1 March 1900 01:00:00".
  *
- * @todo Add support for different calendar models (mainly requires to settle how this
- * should be specified in various languages).
+ * @todo Add support for additional calendar models (mainly requires conversion algorithms and
+ * internationalization support).
  * @todo Internationalise the treatment of AD, BC, PM, AM. Add more formats (p.m. or BCE).
  * @todo Try to reuse more of MediaWiki's records, e.g. to obtain month names or to
  * format dates. The problem is that MW is based on SIO timestamps that don't extend to
  * very ancient or future dates, and that MW uses PHP functions that are bound to UNIX time.
  *
  * @author Fabian Howahl
+ * @author Terry A. Hurlbut
  * @author Markus Krötzsch
  * @ingroup SMWDataValues
  */
@@ -73,6 +79,7 @@
 	protected $m_year = false; //Gregorian year, remains false if unspecified
 	protected $m_time = false; //time, remains false if unspecified
 	protected $m_jd = ''; //numerical time representation similiar to Julian Day; for ancient times, a more compressed number is used (preserving ordering of time points)
+	protected $m_format = false; // number of parts of the date that were specified
 	protected $m_timeoffset; //contains offset (e.g. timezone) 
 	protected $m_timeannotation; //contains am or pm
 	// The following are constant (array-valued constants are not supported, hence the decalration as variable):
@@ -96,6 +103,9 @@
 		"U" => -8, "UTC" => 0, "V" => -9, "W" => -10, "WDT" => 9, "WEDT" => 1,
 		"WEST" => 1, "WET" => 0, "WST" => 8, "X" => -11, "Y" => -12, "Z" => 0);
 
+// constant epochal values
+	const J1582 = 2299160.5;	// Date of switchover to Gregorian calendar
+
 	protected function parseUserValue($value) {
 		global $smwgContLang;
 
@@ -108,6 +118,7 @@
 		$this->m_timeoffset = 0;
 		$this->m_timepm = false;
 		$this->m_timeannotation = false;
+		$this->m_format = false;
 
 		$value = trim($value); // ignore whitespace
 		$this->m_wikivalue = $value;
@@ -245,13 +256,13 @@
 
 		if ($is_yearbc) {
 			if ($this->m_year > 0) { // see class documentation on BC, "year 0", and ISO conformance ...
-				$this->m_year = -($this->m_year-1);
+				$this->m_year = -($this->m_year);
 			}
 		}
 
 		//handle offset
-		if ($this->m_timeoffset != 0) {
-			$this->createJD();
+		$this->createJD($this->findModel());
+		if ($this->m_jd > -0.5) {
 			$this->m_jd = $this->m_jd - $this->m_timeoffset;
 			$this->JD2Date();
 		}
@@ -296,6 +307,20 @@
 		}
 	}
 
+	// finds the appropriate model to use.
+	protected function findModel() {
+		if ($this->m_year > 1582) {
+			return 'GR'; // All dates after 1582 are Gregorian dates.
+		}
+		if (($this->m_year == 1582) && ($this->m_month > 10)) {
+			return 'GR'; // The Gregorian calendar was inaugurated after October 4, 1582.
+		}
+		if (($this->m_year == 1582) && ($this->m_month == 10) && ($this->m_day > 4)) {
+			return 'GR';
+		} // Set $model to 'JU'
+		return 'JU';
+	}
+
 	protected function parseXSDValue($value, $unit) {
 		list($date,$this->m_time) = explode('T',$value,2);
 		$d = explode('/',$date,3);
@@ -336,7 +361,6 @@
 	}
 
 	public function getNumericValue() {
-		$this->createJD();
 		return $this->m_jd;
 	}
 
@@ -346,7 +370,6 @@
 
 	public function getHash() {
 		if ($this->isValid()) {
-			$this->createJD();
 			return strval($this->m_jd);
 		} else {
 			return implode("\t", $this->m_errors);
@@ -367,7 +390,7 @@
 	}
 
 	/**
-	 * Return the year as a number corresponding to the year in the proleptic
+	 * Return the year as a number corresponding to the year in the Julian or
 	 * Gregorian calendar and using the astronomical year numbering (0 means 1 BC).
 	 */
 	public function getYear() {
@@ -375,7 +398,7 @@
 	}
 
 	/**
-	 * Return the month as a number (between 1 and 12) based on the proleptic
+	 * Return the month as a number (between 1 and 12) based on the Julian or
 	 * Gregorian calendar.
 	 * The parameter $default optionally specifies the value returned
 	 * if the date is valid but has no explicitly specified month. It can
@@ -386,7 +409,7 @@
 	}
 
 	/**
-	 * Return the day as a number based on the proleptic Gregorian calendar.
+	 * Return the day as a number based on the Julian or Gregorian calendar.
 	 * The parameter $default optionally specifies the value returned
 	 * if the date is valid but has no explicitly specified date. It can
 	 * also be set to FALSE to detect this situation.
@@ -443,7 +466,7 @@
 			if ($this->m_year > 0) {
 				$this->m_printvalue = number_format($this->m_year, 0, '.', ''); // note: there should be no digits after the comma anyway
 			} else {
-				$this->m_printvalue = number_format(-($this->m_year-1), 0, '.', '') . ' BC'; // note: there should be no digits after the comma anyway
+				$this->m_printvalue = number_format(-($this->m_year), 0, '.', '') . ' BC'; // note: there should be no digits after the comma anyway
 			}
 			if ($this->m_month) {
 				$this->m_printvalue =  $smwgContLang->getMonthLabel($this->m_month) . " " . $this->m_printvalue;
@@ -475,42 +498,94 @@
 	}
 
 	/**
+	 * This function adds a time fraction to any Julian Day.
+	 */
+	 protected function createTime(){
+		if ($this->m_time != false) { //Calculate fraction only if time is set -- the default time is 0
+			list ($hours, $minutes, $seconds) = explode(':',$this->getTimeString(),3);
+			$time = ($hours/24) + ($minutes / (60*24)) + ($seconds / (3600*24));
+			$this->m_jd += $time;
+		}
+	 }
+
+	/**
 	 * This function computes a numerical value based on the currently set date. If the year is
-	 * grater or equal to -4712 (4713 BC), then (something that is closely inspired by) the Julian Day
+	 * greater or equal to -4712 (4713 BC), then (something that is closely inspired by) the Julian Day
 	 * (JD) is computed. The JD has the form XXXX.YYYY where XXXX is the number of days having elapsed since
-	 * 4713 BC and YYYY is the elapsed time of the day as fraction of 1. See http://en.wikipedia.org/wiki/Julian_day
+	 * noon on 1 January 4713 BC and YYYY is the elapsed time of the day as fraction of 1.
+	 * See http://en.wikipedia.org/wiki/Julian_day
 	 * If the year is before -4713, then the computed number XXXX.YYYY has the following form: XXXX is 
 	 * the number of years BC and YYYY represents the elapsed days of the year as fraction of 1. This
 	 * enables even large negative dates using 32bit floats.
 	 *
-	 * @note The result of this function is used only internally and should not be assumed to be the
-	 * exact JD, even for dates after 4713 BC. The reason is that the time information used in this number is
-	 * based on the local timezone of the wiki (see class documentation), and not necessarily normalized
-	 * to Greenwhich noon. The JD computation, however, is based on proleptic Gregorian calendar, and hence
-	 * is precise for the current input conventions.
+	 * @note The result of this function is used only internally. It should not be assumed to be the
+	 * exact JD, even for dates after 4713 BC, unless a proper time-zone offset or moniker is specified.
 	 */
-	protected function createJD(){
+	protected function createJD($model) {
+		switch($model) {
+			case "GR":
+				$this->gregorian2JD();
+				break;
+			case "JU":
+				$this->julian2JD();
+				break;
+		}
+		$this->createTime();
+	}
+
+	/// Calculate a Julian day according to Gregorian calendar rules
+	protected function gregorian2JD(){
 		$this->m_jd = 0;
-		if ($this->m_year >= -4712) {
-			$a = intval((14-$this->getMonth())/12);
-			$y = $this->m_year + 4800 - $a;
-			$m = $this->getMonth() + 12 * $a - 3;
-
-			if ($this->m_time != false) { //just calculate fraction if time is set -- the default time is 0 anyway
-				list ($hours, $minutes, $seconds) = explode(':',$this->getTimeString(),3);
-				$time = ($hours/24) + ($minutes / (60*24)) + ($seconds / (3600*24));
-				$this->m_jd += $time;
-			}
-			$this->m_jd += $this->getDay() + intval((153*$m+2)/5) + 365*$y + intval($y/4) - intval($y/100) + intval($y/400) - 32045;
+		$a = intval((14-$this->getMonth())/12);
+		$y = $this->m_year + 4800 - $a;
+		$m = $this->getMonth() + 12 * $a - 3;
+		$this->m_jd += $this->getDay() + intval((153*$m+2)/5) + 365*$y + intval($y/4) - intval($y/100) + intval($y/400) - 32045.5;
+		$this->m_format = ($this->m_day != false) ? 3 : (($this->m_month != false) ? 2 : 1);
+	}
+
+	/// Calculate a Julian day according to Julian calendar rules
+	protected function julian2JD(){
+		if ($this->m_year >= -4713) {
+			$this->m_jd = 0;
+			$y1 = ($this->m_year < 1) ? ($this->m_year + 1) : $this->m_year;
+			$m1 = $this->getMonth();
+			$y2 = ($m1 <= 2) ? ($y1 - 1) : $y1;
+			$m2 = ($m1 <= 2) ? ($m1 + 12) : $m1;
+			$this->m_jd += intval((365.25 * ($y2 + 4716))) + intval((30.6001 * ($m2+1))) + $this->getDay() - 1524.5;
 		} else { // starting from the time when JD would be negative, use our own "stretched" representation, currently this just ignores local time
 			$time = 1 - (($this->getMonth() / 12) + ($this->getDay() / 365));
 			$this->m_jd = $this->m_year - $time;
 		}
+		$this->m_format = ($this->m_day != false) ? 3 : (($this->m_month != false) ? 2 : 1);
 	}
 
-	/// Convert Julian Day (see createJD) back to a proleptic Gregorian date.
+	/// Convert the Julian Day fraction to the time string.
+	protected function fracToTime() {
+		$wjd = $this->m_jd + 0.5;
+		$fraction = $wjd - intval($wjd);
+		$time = round($fraction * 3600 * 24);
+		$hours = intval($time / 3600);
+		$time = $time - $hours * 3600;
+		$minutes = intval($time / 60);
+		$seconds = intval($time - $minutes * 60);
+		$this->m_time = $this->normalizeValue($hours).":".$this->normalizeValue($minutes).":".$this->normalizeValue($seconds);
+	}
+
+	/// Convert Julian Day to m_year, m_month, and m_day according to the proper model.
 	protected function JD2Date() {
-		$j = intval($this->m_jd) + 32044;
+		if($this->m_jd < self::J1582) {
+			$this->JD2Julian();
+		} else {
+			$this->JD2Gregorian();
+		}
+		if($this->m_time != false) { // Do not fill this in if it was not filled in to begin with
+			$this->fracToTime();
+		}
+	}
+
+	/// Convert Julian Day (see createJD) back to a Gregorian date.
+	protected function JD2Gregorian() {
+		$j = intval($this->m_jd + 0.5) + 32044;
 		$g = intval($j / 146097);
 		$dg = $j % 146097;
 		$c = intval(((intval($dg / 36524) + 1) * 3) / 4);
@@ -523,17 +598,21 @@
 		$m = intval(($da * 5 + 308) / 153) - 2;
 		$d = $da - intval((($m + 4) * 153) / 5) + 122;
 		$this->m_year = $y - 4800 + intval(($m + 2) / 12);
-		$this->m_month = ($m + 2) % 12 + 1;
-		$this->m_day = $d + 1;
-
-		$fraction = $this->m_jd - intval($this->m_jd);
-		$time = round($fraction * 3600 * 24);
-		$hours = intval($time / 3600);
-		$time = $time - $hours * 3600;
-		$minutes = intval($time / 60);
-		$seconds = intval($time - $minutes * 60);
+		$this->m_month = ($this->m_format >= 2) ? (($m + 2) % 12 + 1) : false;
+		$this->m_day = ($this->m_format == 3) ? ($d + 1) : false;
+	}
 
-		$this->m_time = $this->normalizeValue($hours).":".$this->normalizeValue($minutes).":".$this->normalizeValue($seconds);		
+	/// Convert Julian Day back to a Julian date.
+	protected function JD2Julian() {
+		$b = intval($this->m_jd + 0.5) + 1524;
+		$c = intval(($b - 122.1)/365.25);
+		$d = intval(365.25 * $c);
+		$e = intval(($b - $d)/30.6001);
+		$m = intval(($e < 14) ? ($e - 1) : ($e - 13));
+		$y = intval(($m > 2) ? ($c - 4716) : ($c - 4715));
+		$this->m_year = ($y < 1) ? ($y - 1) : $y;
+		$this->m_month = ($this->m_format >= 2) ? $m : false;
+		$this->m_day = ($this->m_format == 3) ? ($b - $d - intval(30.6001 * $e)) : false;
 	}
 
 }
------------------------------------------------------------------------------
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
Semediawiki-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/semediawiki-devel

Reply via email to