1<?php 2 3namespace ComboStrap; 4 5 6use syntax_plugin_combo_variable; 7 8/** 9 * Format a date 10 */ 11class DateTag 12{ 13 public const CANONICAL = "variable:date"; 14 public const FORMAT_ATTRIBUTE = "format"; 15 public const DATE_ATTRIBUTE = "date"; 16 public const TAG = "date"; 17 /** 18 * https://www.php.net/manual/en/function.strftime.php 19 */ 20 public const DEFAULT_FORMAT = "%A, %d %B %Y"; 21 22 23 /** 24 * @param string $date 25 * @param string $format 26 * @param string|null $lang 27 * @return string 28 * @throws ExceptionBadSyntax 29 */ 30 public static function formatDateString(string $date, string $format = DateTag::DEFAULT_FORMAT, string $lang = null): string 31 { 32 // https://www.php.net/manual/en/function.date.php 33 // To format dates in other languages, you should use the setlocale() and strftime() functions instead of date(). 34 $localeSeparator = '_'; 35 if ($lang === null) { 36 try { 37 $lang = Lang::createFromRequestedMarkup()->getValueOrDefault(); 38 } catch (ExceptionNotFound $e) { 39 // should never happen but yeah 40 LogUtility::error("Internal Error: The requested page was not found. We were unable to get the page language. Defaulting to the site language"); 41 $lang = Site::getLang(); 42 } 43 } 44 $actualLocale = setlocale(LC_ALL, 0); 45 try { 46 if ($lang !== null && trim($lang) !== "") { 47 // Set local takes several possible locales value 48 // The lang just works fine but the second argument can be seen in the doc 49 if (strlen(trim($lang)) === 2) { 50 $derivedLocale = strtolower($lang) . $localeSeparator . strtoupper($lang); 51 } else { 52 $derivedLocale = $lang; 53 } 54 $newLocale = setlocale(LC_TIME, $lang, $derivedLocale); 55 if ($newLocale === false) { 56 $newLocale = setlocale(LC_TIME, $lang); 57 if ($newLocale === false) { 58 throw new ExceptionBadSyntax("The language ($lang) / locale ($derivedLocale) is not available as locale on the server. You can't then format the value ($date) in this language."); 59 } 60 } 61 } 62 $date = syntax_plugin_combo_variable::replaceVariablesWithValuesFromContext($date); 63 $timeStamp = Iso8601Date::createFromString($date)->getDateTime()->getTimestamp(); 64 $formatted = strftime($format, $timeStamp); 65 if ($formatted === false) { 66 if ($lang === null) { 67 $lang = ""; 68 } 69 throw new ExceptionBadSyntax("Unable to format the date ($date) with the format ($format) and lang ($lang)"); 70 } 71 return $formatted; 72 } finally { 73 /** 74 * Restore the locale 75 */ 76 setlocale(LC_ALL, $actualLocale); 77 } 78 79 } 80 81 public static function handleEnterAndSpecial() 82 { 83 LogUtility::warning("The date component has been deprecated for the date variable", DateTag::CANONICAL); 84 } 85 86 public static function renderHtml(TagAttributes $tagAttributes): string 87 { 88 /** 89 * Locale 90 */ 91 $lang = $tagAttributes->getComponentAttributeValue(Lang::PROPERTY_NAME); 92 93 /** 94 * The format 95 */ 96 $format = $tagAttributes->getValue(DateTag::FORMAT_ATTRIBUTE, DateTag::DEFAULT_FORMAT); 97 /** 98 * The date 99 */ 100 $defaultDateTime = Iso8601Date::createFromNow()->toString(); 101 $date = $tagAttributes->getComponentAttributeValue(DateTag::DATE_ATTRIBUTE, $defaultDateTime); 102 try { 103 return DateTag::formatDateString($date, $format, $lang); 104 } catch (ExceptionBadSyntax $e) { 105 $message = "Error while formatting a date. Error: {$e->getMessage()}"; 106 LogUtility::error($message, DateTag::CANONICAL); 107 return LogUtility::wrapInRedForHtml($message); 108 } 109 } 110 111 public static function handleExit(\Doku_Handler $handler) 112 { 113 $callStack = CallStack::createFromHandler($handler); 114 $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 115 $call = $callStack->next(); 116 if ($call !== false) { 117 $date = $call->getCapturedContent(); 118 $openingTag->addAttribute(DateTag::DATE_ATTRIBUTE, $date); 119 $callStack->deleteActualCallAndPrevious(); 120 } 121 } 122} 123