MDNodaHelpers/src/NodaSplitTime.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");
}
}
}
}