*/ declare(strict_types = 1); /** * Class for splitting times. */ final class NodaTimeSplitter { const MONTH_NAMES_GERMAN = [ "01" => ['Januar', 'Jan.'], "02" => ['Februar', 'Feb'], "03" => ['März', 'Mrz.'], "04" => ['April', 'Apr.'], "05" => ['Mai'], "06" => ['Juni', 'Jun.'], "07" => ['Juli', 'Jul.'], "08" => ['August', 'Aug.'], "09" => ['September', 'Sep.', 'Sept.'], "10" => ['Oktober', 'Okt.'], "11" => ['November', 'Nov.'], "12" => ['Dezember', 'Dez.'], ]; const MONTH_NAMES_HUNGARIAN = [ "01" => ['január', 'jan'], "02" => ['február', 'feb'], "03" => ['március', 'mar.'], "04" => ['április', 'apr.'], "05" => ['május', 'maj.'], "06" => ['június', 'jun.'], "07" => ['július', 'jul.'], "08" => ['augusztus', 'aug.'], "09" => ['szeptember', 'szp.'], "10" => ['október', 'okt.'], "11" => ['november', 'nov.'], "12" => ['december', 'dec.'], ]; const STRINGS_TO_CLEAN = [ "között" => "", "nach Christus" => "", "n. Christus" => "", "nach Chr." => "", "n. Chr." => "", "n.Chr." => "", // To clean "v.Chr." => "v. Chr.", "v. Chr" => "v. Chr.", ]; const STOP_STRINGS_GERMAN = [ "-", ",", ";", ":", "/", "(", ")", "[", "]", ", ", " und ", "nach ", "um ", "ca.", "ab ", "seit ", "bis ", "vor ", "anfang ", "ende ", ]; const STOP_STRINGS_HUNGARIAN = [ "-", ",", ";", ":", "/", "(", ")", "[", "]", "ca.", ", ", "-ig", "és", "eleje", "között", "töl", "januárig", "februárig", "márciusig", "vége", "végén", "áprilisig", "májusig", "júniusig", "júliusig", "augusztusig", "szeptemberig", "októberig", "novemberig", "decemberig", ]; /** * Cleans input strings by trimming obsolete stuff. * * @param string $input Input date name. * * @return string */ private static function clean_input(string $input):string { while (strpos($input, " -") !== false) $input = str_replace(" -", "-", $input); while (strpos($input, "- ") !== false) $input = str_replace("- ", "-", $input); $input = strtr($input, self::STRINGS_TO_CLEAN); while (strpos($input, "..") !== false) $input = str_replace("..", ".", $input); return trim($input, ", [](){}"); } /** * Checks if a string is really numeric, not numeric + space, dot. * * @param string $input Input string. * * @return boolean */ private static function is_numeric(string $input):bool { if (is_numeric($input) and strpos($input, " ") === false and strpos($input, ".") === false ) { return true; } return false; } /** * Validates a time substr. * * @param string $datum Date. * @param integer $start Start of substr. * @param integer $end End of substr. * * @return string */ private static function validateDateSubstr(string $datum, int $start, int $end = 10000):string { if ($start !== 0 && !in_array(substr($datum, $start - 1, 1), ["-", " ", "."], true) ) { return ""; } $output = substr($datum, $start, $end); if (self::is_numeric($output)) return $output; return ""; } /** * Generates new time name based on moda. * * @param array $moda Date strings. * * @return string */ public static function timePartsToTimeName(array $moda):string { if ($moda[0] === "?") { $prefix = "Bis "; $moda[0] = $moda[1]; } else if ($moda[1] === "?") { $prefix = "Seit "; $moda[1] = $moda[0]; } else $prefix = ""; $moda[0] = intval($moda[0]); $moda[1] = intval($moda[1]); if ($moda[0] < 0 && $moda[1] < 0) { $suffix = " v. Chr."; } else if ($moda[1] < 1000) { $suffix = " n. Chr."; } else $suffix = ""; $moda[0] = abs($moda[0]); $moda[1] = abs($moda[1]); if ($moda[0] !== $moda[1]) { return "{$prefix}{$moda[0]}-{$moda[1]}{$suffix}"; } else if (intval($moda[2]) !== 0 and intval($moda[3]) !== 0) { return "{$prefix}{$moda[3]}.{$moda[2]}.{$moda[0]}{$suffix}"; } else if ($moda[0] === $moda[1] && trim((string)$moda[2], " 0") === "" && trim((string)$moda[3], " 0") === "") { return "{$prefix}{$moda[0]}{$suffix}"; } else if ($moda[0] === $moda[1] && trim((string)$moda[2], " 0") !== "" && trim((string)$moda[3], " 0") === "") { setlocale(LC_TIME, NodaTimeAutotranslater::LANGS_TO_LOCALES['de']); return $prefix . strftime(getMonthFormatByLang("de"), MD_STD::strtotime("{$moda[0]}-{$moda[2]}-01 01:01:01")) . $suffix; } return ""; } /** * Generates counting year - the middle between start and end year. * * @param array $moda Date strings. * * @return integer */ public static function timePartsToCountingYear(array $moda):int { if ($moda[0] === "?") { return abs(intval($moda[1])); } if ($moda[1] === "?") { return abs(intval($moda[0])); } return abs((int)ceil(intval($moda[1]) - ((intval($moda[1]) - intval($moda[0])) / 2))); } /** * Generates HTML for linking disassembly of times for a single day. * * @param integer $znum Time ID. * @param array $moda Date strings. * @param MDTlLoader $tlLoader Translation loader. * * @return string */ public static function generateDisassemblyForDay(int $znum, array $moda, MDTlLoader $tlLoader):string { $zaehlzeit_jahr = self::timePartsToCountingYear($moda); // Wenn Datum in Form von tt.mm.jjjj, dann biete zerlegen an $output = '
'; $output .= ''; $output .= ''; $output .= ''; $output .= ''; $output .= '
' . $tlLoader->tl("tempi", "tempi", "time_tool") . '+'; if (!empty($newTimeName)) { $output .= $newTimeName; } else { if (!empty(trim($moda[3], " 0")) and !empty(trim($moda[2], " 0"))) $output .= $moda[3] . '.' . $moda[2] . '.' . $moda[0]; else if ($moda[0] !== $moda[1]) $output .= $moda[0] . "-" . $moda[1]; else if (!empty(trim($moda[2], " 0"))) $output .= "{$moda[2]}.{$moda[0]}"; else $output .= $moda[0]; } $output .= ' - ' . $tlLoader->tl("tempi", "tempi", "time_disassemble") . '
'; return $output; } /** * Checks if any string of a list occurs in the haystack input string. * * @param string $haystack Haystack. * @param array $needles Needles. * * @return boolean */ private static function stri_occurs(string $haystack, array $needles):bool { foreach ($needles as $needle) { if (stripos($haystack, $needle) !== false) return true; } return false; } /** * Translate German month to two digits number. * * @param string $datum Date. * * @return array */ public static function is_valid_date(string $datum):array { $datum = self::clean_input($datum); if (preg_match("/^[0-9][0-9][0-9]\ v\. Chr\.$/", $datum)) { $start = "-0" . substr($datum, 0, 3); return [$start, $start, "00", "00", "-", ""]; } if (preg_match("/^[0-9][0-9][0-9][0-9](\-|\/)[0-9][0-9][0-9][0-9]\ v\. Chr\.$/", $datum)) { $start = "-" . substr($datum, 0, 4); $end = "-" . substr($datum, 5, 4); return [$start, $end, "00", "00", "-", ""]; } if (preg_match("/^[0-9][0-9][0-9][0-9](\-|\/)[0-9][0-9][0-9]\ v\. Chr\.$/", $datum)) { $start = "-" . substr($datum, 0, 4); $end = "-" . substr($datum, 5, 3); return [$start, $end, "00", "00", "-", ""]; } if (preg_match("/^[0-9][0-9][0-9](\-|\/)[0-9][0-9][0-9]\ v\. Chr\.$/", $datum)) { $start = "-" . substr($datum, 0, 3); $end = "-" . substr($datum, 4, 3); return [$start, $end, "00", "00", "-", ""]; } if (preg_match("/^[0-9][0-9](\-|\/)[0-9][0-9]\ v\. Chr\.$/", $datum)) { $start = "-00" . substr($datum, 0, 2); $end = "-00" . substr($datum, 3, 2); return [$start, $end, "00", "00", "-", ""]; } $datum = str_replace(". ", ".", $datum); if (self::stri_occurs($datum, self::STOP_STRINGS_GERMAN)) { return []; } if (strlen($datum) <= 6) return []; if (strlen($datum) <= 9) $use_day = false; else $use_day = true; foreach (self::MONTH_NAMES_GERMAN as $monthVal => $monthValidNames) { if (self::stri_occurs($datum, $monthValidNames)) { if (!empty($monat)) return []; $monat = (string)$monthVal; } } if (empty($monat) and self::is_numeric((string)substr($datum, 3, 2))) $monat = substr($datum, 3, 2); if (self::is_numeric((string)substr($datum, 0, 2))) $day = substr($datum, 0, 2); else if (substr($datum, 1, 1) === "." and self::is_numeric((string)substr($datum, 0, 1))) $day = "0" . substr($datum, 0, 1); if (self::is_numeric((string)substr($datum, -4))) $year = substr($datum, -4); if (!empty($year) and !empty($monat) and !empty($day) and $use_day) { return [$year, $year, $monat, $day, '+', ""]; } else if (!empty($year) and !empty($monat)) { return [$year, $year, $monat, "00", '+', ""]; } return []; } /** * Translate Hungarian month to two digits number. * * @param string $datum Date. * * @return array */ public static function is_valid_date_hungarian(string $datum):array { $datum = self::clean_input($datum); if (preg_match("/^Kr\.\ e\.\ [0-9][0-9][0-9][0-9]$/", $datum)) { $start = "-" . substr($datum, 7, 4); return [$start, $start, "00", "00", "-"]; } if (self::stri_occurs($datum, self::STOP_STRINGS_HUNGARIAN)) { return []; } if (strlen($datum) <= 9) return []; foreach (self::MONTH_NAMES_HUNGARIAN as $monthVal => $monthValidNames) { if (self::stri_occurs($datum, $monthValidNames)) { if (!empty($monat)) return []; $monat = (string)$monthVal; } } if (empty($monat) and self::is_numeric((string)substr($datum, 5, 2))) $monat = substr($datum, 5, 2); else if (empty($monat) and self::is_numeric((string)substr($datum, 6, 2))) $monat = substr($datum, 6, 2); $day = self::validateDateSubstr($datum, -2); if (empty($day)) $day = self::validateDateSubstr($datum, -3, 2); if (empty($day)) $day = self::validateDateSubstr($datum, -4, 2); if (empty($day)) $day = self::validateDateSubstr($datum, -5, 2); if (empty($day)) $day = self::validateDateSubstr($datum, -6, 2); if (substr($datum, -2, 1) === " " and self::is_numeric((string)substr($datum, -1, 1))) { $day = "0" . substr($datum, -1, 1); } else if ((empty($day)) and substr($datum, -3, 1) === " " and self::is_numeric((string)substr($datum, -2, 1))) { $day = "0" . substr($datum, -2, 1); } if (self::is_numeric((string)substr($datum, 0, 4))) $year = substr($datum, 0, 4); if (!empty($year) and !empty($monat) and !empty($day)) { return [$year, $year, $monat, $day, '+', ""]; } else if (!empty($year) and !empty($monat)) { return [$year, $year, $monat, "00", '+', ""]; } return []; } /** * Translate German month to two digits number. * * @param string $datum Date. * * @return array */ public static function is_valid_date_by_php(string $datum):array { $datum = self::clean_input($datum); if (!($timeInt = strtotime($datum))) { return []; } return [date("Y", $timeInt), date("m", $timeInt), date("d", $timeInt), '+', ""]; } /** * Checks if an input date is a timespan. * * @param string $datum Input date. * * @return array */ public static function is_timespan(string $datum):array { $datum = self::clean_input($datum); if (preg_match("/^[0-9][0-9][0-9][0-9](\-|\/)[0-9][0-9][0-9][0-9]$/", $datum)) { $start = substr($datum, 0, 4); $end = substr($datum, -4); return [$start, $end, "00", "00", "+", ""]; } if (preg_match("/^[0-9][0-9]\.[0-9]\.[0-9][0-9][0-9][0-9]$/", $datum)) { // German T.MM.JJJJ $start = substr($datum, 5, 4); $month = "0" . substr($datum, 3, 1); $day = substr($datum, 0, 2); return [$start, $start, $month, $day, "+", ""]; } if (preg_match("/^[0-9]\.[0-9][0-9]\.[0-9][0-9][0-9][0-9]$/", $datum)) { // German T.MM.JJJJ $start = substr($datum, 5, 4); $month = substr($datum, 2, 2); $day = "0" . substr($datum, 0, 1); return [$start, $start, $month, $day, "+", ""]; } if (preg_match("/^[0-9]\.[0-9]\.[0-9][0-9][0-9][0-9]$/", $datum)) { // German T.M.JJJJ $start = substr($datum, 4, 4); $month = "0" . substr($datum, 2, 1); $day = "0" . substr($datum, 0, 1); return [$start, $start, $month, $day, "+", ""]; } if (preg_match("/^[0-9][0-9]\.[0-9][0-9][0-9][0-9]$/", $datum)) { // German Y-m $start = substr($datum, 3, 4); $month = substr($datum, 0, 2); return [$start, $start, $month, "00", "+", ""]; } if (preg_match("/^[0-9]\.[0-9][0-9][0-9][0-9]$/", $datum)) { // German Y-m $start = substr($datum, 2, 4); $month = "0" . substr($datum, 0, 1); return [$start, $start, $month, "00", "+", ""]; } if (preg_match("/^[0-9][0-9][0-9][0-9]\.[0-9][0-9]$/", $datum)) { // Hungarian Y-m $start = substr($datum, 0, 4); $month = substr($datum, 5, 2); return [$start, $start, $month, "00", "+", ""]; } if (preg_match("/^[0-9][0-9][0-9][0-9]\-[0-9][0-9]$/", $datum)) { // Time spans: 1945-46 $start = substr($datum, 0, 4); $endDigits = substr($datum, 5, 2); if (intval($endDigits) > 12) return [$start, substr($datum, 0, 2) . $endDigits, "00", "00", "+", ""]; } if (preg_match("/^01\.01\.[0-9][0-9][0-9][0-9]\-31\.12\.[0-9][0-9][0-9][0-9]$/", $datum)) { // Hungarian Y-m $start = substr($datum, 6, 4); $end = substr($datum, -4); return [$start, $end, "00", "00", "+", ""]; } if (preg_match("/^[0-9][0-9][0-9]\-[0-9][0-9][0-9]$/", $datum)) { // Hungarian Y-m $start = substr($datum, 0, 3); $end = substr($datum, -3); if ($end > $start) return ["0" . $start, "0" . $end, "00", "00", "+", ""]; } if (preg_match("/^[0-9][0-9]\-[0-9][0-9]$/", $datum)) { // 20-40 (n. Chr.) $start = substr($datum, 0, 2); $end = substr($datum, -2); if ($end > $start) return ["00" . $start, "00" . $end, "00", "00", "+", ""]; } if (preg_match("/^[0-9][0-9][0-9][0-9]$/", $datum)) { $start = substr($datum, 0, 4); return [$start, $start, "00", "00", "+", ""]; } return []; } /** * Checks if an input date is an incomplete date: Before 1920, after 1930. * * @param string $datum Input date. * * @return array */ public static function is_incomplete_date(string $datum):array { $datum = self::clean_input($datum); if (preg_match("/^[0-9][0-9][0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\-$/", $datum)) { // Hungarian Y-m $start = substr($datum, 0, 4); $month = substr($datum, 5, 2); $day = substr($datum, 8, 2); return [$start, "?", $month, $day, "+", ""]; } if (preg_match("/^[0-9][0-9][0-9][0-9]\.[0-9][0-9]\-$/", $datum)) { // Hungarian Y-m $start = substr($datum, 0, 4); $month = substr($datum, 5, 2); return [$start, "?", $month, "00", "+", ""]; } if (preg_match("/^\-[0-9][0-9][0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]$/", $datum)) { // Hungarian Y-m $start = substr($datum, 1, 4); $month = substr($datum, 6, 2); $day = substr($datum, 9, 2); return ["?", $start, $month, $day, "+", ""]; } if (preg_match("/^\-[0-9][0-9][0-9][0-9]\.[0-9][0-9]$/", $datum)) { // Hungarian Y-m $start = substr($datum, 1, 4); $month = substr($datum, 6, 2); return ["?", $start, $month, "00", "+", ""]; } if (preg_match("/^(Ab|Seit|seit)\ /", $datum)) { if (($spacePos = strpos($datum, " ")) === false) { return []; } if ($output = self::attempt_splitting(substr($datum, $spacePos))) { $output[1] = "?"; return $output; } } if (preg_match("/^(Bis|bis)\ /", $datum)) { if (($spacePos = strpos($datum, " ")) === false) { return []; } if ($output = self::attempt_splitting(substr($datum, $spacePos))) { $output[0] = "?"; return $output; } } // Endings beginning with a space if (preg_match("/ (\(bis)$/", $datum)) { if (($spacePos = strrpos($datum, " ")) === false) { return []; } if ($output = self::attempt_splitting(substr($datum, 0, $spacePos))) { $output[0] = "?"; return $output; } } if (preg_match("/\-ig$/", $datum)) { if (($spacePos = strrpos($datum, "-")) === false) { return []; } if ($output = self::attempt_splitting(substr($datum, 0, $spacePos))) { $output[0] = "?"; return $output; } } return []; } /** * Checks if an input date is a century. * * @param string $datum Input date. * * @return array */ public static function is_century(string $datum):array { $datum = self::clean_input($datum); $bcBceIndicator = '+'; // 17. Jahrhundert if (preg_match("/^[0-9][0-9]\.\ (Jh\.|Jahrhundert|század)$/", $datum)) { if ($centuryNo = intval(substr($datum, 0, 2))) { $centuryNo--; return [(string)$centuryNo . "01", strval($centuryNo + 1) . "00", "00", "00", $bcBceIndicator, ""]; } } // 1. Jahrhundert if (preg_match("/^[0-9]\.\ (Jh\.|Jahrhundert|század)$/", $datum)) { echo "HI"; if ($centuryNo = intval(substr($datum, 0, 1))) { $centuryNo--; return [(string)$centuryNo . "01", strval($centuryNo + 1) . "00", "00", "00", $bcBceIndicator, ""]; } } // 17.-18. Jahrhundert if (preg_match("/^[0-9][0-9]\.\-[0-9][0-9]\.\ (Jh\.|Jahrhundert|század)$/", $datum)) { return [(string)(intval(substr($datum, 0, 2)) - 1) . "01", substr($datum, 4, 2) . "00", "00", "00", $bcBceIndicator, ""]; } if (preg_match("/^[0-9][0-9]\-[0-9][0-9]\.\ (Jh\.|Jahrhundert|század)$/", $datum)) { return [(string)(intval(substr($datum, 0, 2)) - 1) . "01", substr($datum, 3, 2) . "00", "00", "00", $bcBceIndicator, ""]; } if (preg_match("/^[0-9]\.\-[0-9]\.\ (Jh\.|Jahrhundert|század)$/", $datum)) { return [(string)(intval(substr($datum, 0, 1)) - 1) . "01", substr($datum, 3, 1) . "00", "00", "00", $bcBceIndicator, ""]; } return []; } /** * Checks if an input date is a decade. * * @param string $datum Input date. * * @return array */ public static function is_decade(string $datum):array { $datum = self::clean_input($datum); $bcBceIndicator = '+'; if (preg_match("/^[0-9]0(er\ Jahre)$/", $datum)) { $start = "19" . substr($datum, 0, 2); $ende = (string)(intval($start) + 9); return [$start, $ende, "00", "00", $bcBceIndicator, ""]; } if (preg_match("/^[0-9][0-9][0-9]0(er\ Jahre)$/", $datum)) { $start = substr($datum, 0, 4); $ende = (string)(intval($start) + 9); return [$start, $ende, "00", "00", $bcBceIndicator, ""]; } return []; } /** * Wrapper to check if any splitting command works. * * @param string $datum Input date. * * @return array */ public static function attempt_splitting(string $datum):array { $moda = NodaTimeSplitter::is_timespan($datum); if (!$moda) $moda = NodaTimeSplitter::is_incomplete_date($datum); if (!$moda) $moda = NodaTimeSplitter::is_valid_date($datum); if (!$moda) $moda = NodaTimeSplitter::is_valid_date_hungarian($datum); if (!$moda) $moda = NodaTimeSplitter::is_century($datum); if (!$moda) $moda = NodaTimeSplitter::is_decade($datum); return $moda; } }