*/ 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 */ 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"); } } } }