384 lines
12 KiB
PHP
384 lines
12 KiB
PHP
<?PHP
|
|
/**
|
|
* Describes a time after splitting / transfer object.
|
|
*
|
|
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
|
|
*/
|
|
declare(strict_types = 1);
|
|
|
|
/**
|
|
* Describes a time after splitting / transfer object.
|
|
*/
|
|
final class NodaSplitTime {
|
|
|
|
public const DEFAULT_DATE = '0001-01-01';
|
|
|
|
public readonly string $start_year;
|
|
public readonly string $end_year;
|
|
|
|
public readonly string $counting_time_month;
|
|
public readonly string $counting_time_day;
|
|
|
|
public readonly NodaCountingTimeIndicator $counting_time_indicator;
|
|
public readonly NodaTimeBeforeAfterIndicator $before_after_indicator;
|
|
|
|
public string $start_date;
|
|
public string $end_date;
|
|
|
|
/**
|
|
* Returns a single, exact date.
|
|
*
|
|
* @param string $year Year.
|
|
* @param string $month Month.
|
|
* @param string $day Day.
|
|
*
|
|
* @return NodaSplitTime
|
|
*/
|
|
public static function genExactDate(string $year, string $month, string $day, NodaTimeBeforeAfterIndicator $before_after_indicator = NodaTimeBeforeAfterIndicator::none):NodaSplitTime {
|
|
|
|
$start_year = $end_year = $year;
|
|
$start_date = $end_date = $year . '-' . $month . '-' . $day;
|
|
|
|
if ($before_after_indicator === NodaTimeBeforeAfterIndicator::before
|
|
|| $before_after_indicator === NodaTimeBeforeAfterIndicator::until
|
|
) {
|
|
$start_year = $start_date = '?';
|
|
}
|
|
if ($before_after_indicator === NodaTimeBeforeAfterIndicator::after
|
|
|| $before_after_indicator === NodaTimeBeforeAfterIndicator::since
|
|
) {
|
|
$end_year = $end_date = '?';
|
|
}
|
|
|
|
return new NodaSplitTime($start_year, $end_year, $month, $day, start_date: $start_date, end_date: $end_date);
|
|
|
|
}
|
|
|
|
/**
|
|
* Validates the entered start year.
|
|
*
|
|
* @param string $input Input to validate.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function _validateStartYear(string $input):void {
|
|
|
|
if (strlen($input) > 6) {
|
|
throw new MDgenericInvalidInputsException("Time statement longer than 6 characters is impossible");
|
|
}
|
|
|
|
if (($this->before_after_indicator === NodaTimeBeforeAfterIndicator::before
|
|
|| $this->before_after_indicator === NodaTimeBeforeAfterIndicator::until)
|
|
&& $input !== '?'
|
|
) {
|
|
throw new MDgenericInvalidInputsException("Times with no certain start need to have a question mark (?) entered as a start date");
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Validates the entered end year.
|
|
*
|
|
* @param string $input Input to validate.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function _validateEndYear(string $input):void {
|
|
|
|
if (strlen($input) > 6) {
|
|
throw new MDgenericInvalidInputsException("Time statement longer than 6 characters is impossible");
|
|
}
|
|
|
|
if (($this->before_after_indicator === NodaTimeBeforeAfterIndicator::after
|
|
|| $this->before_after_indicator === NodaTimeBeforeAfterIndicator::since)
|
|
&& $input !== '?'
|
|
) {
|
|
throw new MDgenericInvalidInputsException("Times with no certain end need to have a question mark (?) entered as a end date");
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Pads to four digits. E.g. 2 > 02.
|
|
*
|
|
* @param string $input Input string.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function pad_to_two(string $input):string {
|
|
|
|
return \substr("00" . $input, -2);
|
|
|
|
}
|
|
|
|
/**
|
|
* Validates the entered month string.
|
|
*
|
|
* @param string $input Input to validate.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function _validateCountingTimeMonth(string $input):string {
|
|
|
|
if (strlen($input) > 2) throw new MDgenericInvalidInputsException("Input too long");
|
|
|
|
if ($input === '00') {
|
|
return $input;
|
|
}
|
|
|
|
if (($parsedInt = filter_var(ltrim($input, "0"), FILTER_VALIDATE_INT)) === false) {
|
|
throw new MDgenericInvalidInputsException("Input value is not numeric");
|
|
}
|
|
if ($parsedInt > 12) {
|
|
throw new MDgenericInvalidInputsException("Attempted to set a month number beyond 12");
|
|
}
|
|
|
|
return self::pad_to_two($input);
|
|
|
|
}
|
|
|
|
/**
|
|
* Validates the entered day string.
|
|
*
|
|
* @param string $input Input to validate.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function _validateCountingTimeDay(string $input):string {
|
|
|
|
if (strlen($input) > 2) throw new MDgenericInvalidInputsException("Input too long");
|
|
|
|
if ($input === '00') {
|
|
return $input;
|
|
}
|
|
|
|
if (($parsedInt = filter_var(ltrim($input, "0"), FILTER_VALIDATE_INT)) === false) {
|
|
throw new MDgenericInvalidInputsException("Input value is not numeric");
|
|
}
|
|
if ($parsedInt > 31) {
|
|
throw new MDgenericInvalidInputsException("Attempted to set a day number beyond 31");
|
|
}
|
|
|
|
return self::pad_to_two($input);
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns a date time for the start of the time.
|
|
*
|
|
* @return DateTime
|
|
*/
|
|
public function startToDateTime():DateTime {
|
|
|
|
if ($this->counting_time_indicator === NodaCountingTimeIndicator::bce) {
|
|
return new DateTime('-' . $this->start_date);
|
|
}
|
|
else {
|
|
return new DateTime($this->start_date);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns a date time for the end of the time.
|
|
*
|
|
* @return DateTime
|
|
*/
|
|
public function endToDateTime():DateTime {
|
|
|
|
if ($this->counting_time_indicator === NodaCountingTimeIndicator::bce) {
|
|
return new DateTime('-' . $this->end_date);
|
|
}
|
|
else {
|
|
return new DateTime($this->end_date);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Generates a time name based on the current entry.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function toTimeName():string {
|
|
|
|
$prefix = $this->before_after_indicator->toString();
|
|
if (!empty($prefix)) $prefix .= ' ';
|
|
|
|
// Determine start and end for display
|
|
if ($this->start_year === '?') {
|
|
$start = (int)$this->end_year;
|
|
}
|
|
else $start = (int)$this->start_year;
|
|
|
|
if ($this->end_year === '?') {
|
|
$end = (int)$this->start_year;
|
|
}
|
|
else $end = (int)$this->end_year;
|
|
|
|
if ($this->before_after_indicator === NodaTimeBeforeAfterIndicator::before && $this->counting_time_month === '00') {
|
|
$start++;
|
|
$end++;
|
|
}
|
|
else if ($this->before_after_indicator === NodaTimeBeforeAfterIndicator::after && $this->counting_time_month === '00') {
|
|
$start--;
|
|
$end--;
|
|
}
|
|
|
|
// Determine suffix
|
|
if ($start < 0 && $end < 0) {
|
|
$suffix = " v. Chr.";
|
|
}
|
|
else if ($end < 1000) {
|
|
$suffix = " n. Chr.";
|
|
}
|
|
else $suffix = "";
|
|
|
|
$start = \abs($start);
|
|
$end = \abs($end);
|
|
|
|
if ($start !== $end) {
|
|
return "{$prefix}{$start}-{$end}{$suffix}";
|
|
}
|
|
|
|
// A single day of a given month of a given (single) year
|
|
else if (\intval($this->counting_time_month) !== 0 and \intval($this->counting_time_day) !== 0) {
|
|
return "{$prefix}{$this->counting_time_day}.{$this->counting_time_month}.{$start}{$suffix}";
|
|
}
|
|
|
|
// A single year
|
|
else if ($start === $end && trim((string)$this->counting_time_month, " 0") === "" && trim((string)$this->counting_time_day, " 0") === "") {
|
|
return "{$prefix}{$start}{$suffix}";
|
|
}
|
|
|
|
// Single month of a given year
|
|
else if ($start === $end && trim((string)$this->counting_time_month, " 0") !== "" && trim((string)$this->counting_time_day, " 0") === "") {
|
|
|
|
$fmt = new IntlDateFormatter(
|
|
'de-DE',
|
|
IntlDateFormatter::FULL,
|
|
IntlDateFormatter::FULL,
|
|
null,
|
|
IntlDateFormatter::GREGORIAN,
|
|
'MMMM Y'
|
|
);
|
|
|
|
try {
|
|
return $prefix . $fmt->format(MD_STD::strtotime("{$start}-{$this->counting_time_month}-15 01:01:01")) . $suffix;
|
|
}
|
|
catch (MDInvalidInputDate $e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns an array in the old time splitter format (array with sex values).
|
|
*
|
|
* @return array<string>
|
|
*/
|
|
public function toOldFormat():array {
|
|
|
|
return [
|
|
$this->start_year,
|
|
$this->end_year,
|
|
$this->counting_time_month,
|
|
$this->counting_time_day,
|
|
$this->counting_time_indicator->toString(),
|
|
$this->before_after_indicator->toString(),
|
|
];
|
|
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct(string $start_year, string $end_year,
|
|
string $counting_time_month = "00", string $counting_time_day = "00",
|
|
NodaCountingTimeIndicator $counting_time_indicator = NodaCountingTimeIndicator::ce,
|
|
NodaTimeBeforeAfterIndicator $before_after_indicator = NodaTimeBeforeAfterIndicator::none,
|
|
false|string $start_date = false,
|
|
false|string $end_date = false,
|
|
) {
|
|
|
|
if (substr($start_year, 0, 1) === '-') {
|
|
if (strlen($start_year) > 5) $start_year = '-' . str_pad(trim($start_year, '-'), 4, '-', STR_PAD_LEFT);
|
|
}
|
|
if ($start_date !== false && str_starts_with($start_date, '-')) {
|
|
$parts = explode('-', trim($start_date, '-'));
|
|
$parts[0] = str_pad($parts[0], 4, '0', STR_PAD_LEFT);
|
|
$start_date = '-' . implode('-', $parts);
|
|
}
|
|
if (substr($end_year, 0, 1) === '-') {
|
|
if (strlen($end_year) > 5) $end_year = '-' . str_pad(trim($end_year, '-'), 4, '-', STR_PAD_LEFT);
|
|
}
|
|
if ($end_date !== false && str_starts_with($end_date, '-')) {
|
|
$parts = explode('-', trim($end_date, '-'));
|
|
$parts[0] = str_pad($parts[0], 4, '0', STR_PAD_LEFT);
|
|
$end_date = '-' . implode('-', $parts);
|
|
}
|
|
|
|
$this->counting_time_indicator = $counting_time_indicator;
|
|
$this->before_after_indicator = $before_after_indicator;
|
|
|
|
$this->_validateStartYear($start_year);
|
|
$this->start_year = $start_year;
|
|
|
|
$this->_validateEndYear($end_year);
|
|
$this->end_year = $end_year;
|
|
|
|
$this->counting_time_month = $this->_validateCountingTimeMonth($counting_time_month);
|
|
$this->counting_time_day = $this->_validateCountingTimeDay($counting_time_day);
|
|
|
|
// Calculate start date and end date
|
|
if (($this->before_after_indicator === NodaTimeBeforeAfterIndicator::before
|
|
|| $this->before_after_indicator === NodaTimeBeforeAfterIndicator::until)
|
|
|| $start_date === '?'
|
|
) {
|
|
$this->start_date = '-9999-12-31';
|
|
}
|
|
if (($this->before_after_indicator === NodaTimeBeforeAfterIndicator::after
|
|
|| $this->before_after_indicator === NodaTimeBeforeAfterIndicator::since)
|
|
|| $end_date === '?'
|
|
) {
|
|
$this->end_date = '9999-12-31';
|
|
}
|
|
|
|
if (!isset($this->start_date) && false !== $start_date) {
|
|
$this->start_date = date("Y-m-d", MD_STD::strtotime($start_date));
|
|
}
|
|
if (!isset($this->end_date) && false !== $end_date) {
|
|
$this->end_date = date("Y-m-d", MD_STD::strtotime($end_date));
|
|
}
|
|
|
|
if (!isset($this->start_date)) {
|
|
if ($this->counting_time_month === "00") {
|
|
$this->start_date = $this->start_year . '-01-01';
|
|
}
|
|
else if ($this->counting_time_day === "00") {
|
|
$this->start_date = $this->start_year . '-' . $this->counting_time_month . '-01';
|
|
}
|
|
else {
|
|
throw new MDgenericInvalidInputsException("Cannot identify start date automatically");
|
|
}
|
|
}
|
|
if (!isset($this->end_date)) {
|
|
if ($this->counting_time_month === "00") {
|
|
$this->end_date = $this->end_year . '-12-31';
|
|
}
|
|
else if ($this->counting_time_day === "00") {
|
|
$this->end_date = $this->end_year . '-' . $this->counting_time_month . '-31';
|
|
}
|
|
else {
|
|
throw new MDgenericInvalidInputsException("Cannot identify end date automatically");
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|