AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace core_calendar\output;use DateInterval;use DateTimeInterface;use DateTimeImmutable;use core\output\pix_icon;use core\output\templatable;use core\output\renderable;use core\output\renderer_base;use core\clock;use core\url;/*** Class humandate.** This class is used to render a timestamp as a human readable date.* The main difference between userdate and this class is that this class* will render the date as "Today", "Yesterday", "Tomorrow" if the date is* close to the current date. Also, it will add alert styling if the date* is near.** @package core_calendar* @copyright 2024 Ferran Recio <ferran@moodle.com>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class humandate implements renderable, templatable {/** @var int|null The number of seconds within which a date is considered near. 1 day by default. */protected ?int $near = DAYSECS;/** @var bool Whether we should show time only or date and time. */protected bool $timeonly = false;/** @var url|null Link for the date. */protected ?url $link = null;/** @var string|null An optional date format to apply. */protected ?string $langtimeformat = null;/** @var bool Whether to use human relative terminology. */protected bool $userelatives = true;/** @var clock The clock interface to handle time. */protected clock $clock;/*** Class constructor.** Use the factory methods, such as create_from_timestamp or create_from_datetime, instead.** @param DateTimeImmutable $datetime The datetime.*/protected function __construct(/** @var DateTimeImmutable $datetime The datetime. **/protected DateTimeImmutable $datetime,) {$this->clock = \core\di::get(clock::class);}/*** Creates a new humandate instance from a timestamp.** @param int $timestamp The timestamp.* @param int|null $near The number of seconds within which a date is considered near. 1 day by default.* @param bool $timeonly Whether we should show time only or date and time.* @param url|null $link Link for the date.* @param string|null $langtimeformat An optional date format to apply.* @param bool $userelatives Whether to use human relative terminology.* @return humandate The new instance.*/public static function create_from_timestamp(int $timestamp,?int $near = DAYSECS,bool $timeonly = false,?url $link = null,?string $langtimeformat = null,bool $userelatives = true,): self {return self::create_from_datetime((new DateTimeImmutable("@{$timestamp}")),$near,$timeonly,$link,$langtimeformat,$userelatives);}/*** Creates a new humandate instance from a datetime.** @param DateTimeInterface $datetime The datetime.* @param int|null $near The number of seconds within which a date is considered near. 1 day by default.* @param bool $timeonly Whether we should show time only or date and time.* @param url|null $link Link for the date.* @param string|null $langtimeformat An optional date format to apply.* @param bool $userelatives Whether to use human relative terminology.* @return humandate The new instance.*/public static function create_from_datetime(DateTimeInterface $datetime,?int $near = DAYSECS,bool $timeonly = false,?url $link = null,?string $langtimeformat = null,bool $userelatives = true,): self {if (!($datetime instanceof DateTimeImmutable)) {// Always use an Immutable object to ensure that the value does not change externally before it is rendered.$datetime = DateTimeImmutable::createFromInterface($datetime);}return (new self($datetime))->set_near_limit($near)->set_display_time_only($timeonly)->set_link($link)->set_lang_time_format($langtimeformat)->set_use_relatives($userelatives);}/*** Sets the number of seconds within which a date is considered near.** @param int|null $near The number of seconds within which a date is considered near.* @return humandate The instance.*/public function set_near_limit(?int $near): self {$this->near = $near;return $this;}/*** Sets whether we should show time only or date and time.** @param bool $timeonly Whether we should show time only or date and time.* @return humandate The instance.*/public function set_display_time_only(bool $timeonly): self {$this->timeonly = $timeonly;return $this;}/*** Sets the link for the date. If null, no link will be added.** @param url|null $link The link for the date.* @return humandate The instance.*/public function set_link(?url $link): self {$this->link = $link;return $this;}/*** Sets an optional date format to apply.** @param string|null $langtimeformat Lang date and time format to use to format the date.* @return humandate The instance.*/public function set_lang_time_format(?string $langtimeformat): self {$this->langtimeformat = $langtimeformat;return $this;}/*** Sets whether to use human relative terminology.** @param bool $userelatives Whether to use human relative terminology.* @return humandate The instance.*/public function set_use_relatives(bool $userelatives): self {$this->userelatives = $userelatives;return $this;}#[\Override]public function export_for_template(renderer_base $output): array {$userdate = $this->default_userdate();$relative = null;if ($this->userelatives) {$relative = $this->format_relative_date();}if ($this->timeonly) {$date = null;} else {$date = $relative ?? $userdate;}$data = ['timestamp' => $this->datetime->getTimestamp(),'userdate' => $userdate,'date' => $date,'time' => $this->format_time(),'ispast' => $this->datetime < $this->clock->now(),'needtitle' => ($relative !== null || $this->timeonly),'link' => $this->link ? $this->link->out(false) : '',];if ($this->is_near()) {$icon = new pix_icon(pix: 'i/warning',alt: get_string('warning'),component: 'moodle',attributes: ['class' => 'me-0 pb-1']);$data['isnear'] = true;$data['nearicon'] = $icon->export_for_template($output);}return $data;}/*** Returns the default user date format.** @return string The formatted date.*/private function default_userdate(): string {$timestamp = $this->datetime->getTimestamp();if ($this->is_current_year()) {$format = get_string('strftimedayshort', 'langconfig');} else {$format = get_string('strftimedaydate', 'langconfig');}return userdate($timestamp, $format);}/*** Checks if the date is near.** @return bool Whether the date is near.*/private function is_near(): bool {if ($this->near === null) {return false;}$due = $this->datetime->diff($this->clock->now());$intervalseconds = $this->interval_to_seconds($due);return $intervalseconds < $this->near && $intervalseconds > 0;}/*** Checks if the datetime is from the current year.** @return bool True if the datetime is from the current year, false otherwise.*/private function is_current_year(): bool {$currentyear = $this->clock->now()->format('Y');$datetimeyear = $this->datetime->format('Y');return $currentyear === $datetimeyear;}/*** Converts a DateInterval object to total seconds.** @param \DateInterval $interval The interval to convert.* @return int The total number of seconds.*/private function interval_to_seconds(DateInterval $interval): int {$reference = new DateTimeImmutable();$entime = $reference->add($interval);return $reference->getTimestamp() - $entime->getTimestamp();}/*** Formats the timestamp as a relative date string (e.g., "Today", "Yesterday", "Tomorrow").** This method compares the given timestamp with the current date and returns a formatted* string representing the relative date. If the timestamp corresponds to today, yesterday,* or tomorrow, it returns the appropriate string. Otherwise, it returns null.** @return string|null*/private function format_relative_date(): ?string {$usertimestamp = $this->get_user_date($this->datetime->getTimestamp());if ($usertimestamp == $this->get_user_date($this->clock->now()->getTimestamp())) {$format = get_string('timerelativetoday','calendar',get_string('strftimedateshort', 'langconfig'));} else if ($usertimestamp == $this->get_user_date(strtotime('yesterday', $this->clock->now()->getTimestamp()))) {$format = get_string('timerelativeyesterday','calendar',get_string('strftimedateshort', 'langconfig'));} else if ($usertimestamp == $this->get_user_date(strtotime('tomorrow', $this->clock->now()->getTimestamp()))) {$format = get_string('timerelativetomorrow','calendar',get_string('strftimedateshort', 'langconfig'));} else {return null;}return userdate($this->datetime->getTimestamp(), $format);}/*** Formats the timestamp as a human readable time.** @param int $timestamp The timestamp to format.* @param string $format The format to use.* @return string The formatted date.*/private function get_user_date(int $timestamp, string $format = '%Y-%m-%d'): string {$calendartype = \core_calendar\type_factory::get_calendar_instance();$timezone = \core_date::get_user_timezone_object();return $calendartype->timestamp_to_date_string(time: $timestamp,format: $format,timezone: $timezone->getName(),fixday: true,fixhour: true,);}/*** Formats the timestamp as a human readable time.** This method compares the given timestamp with the current date and returns a formatted* string representing the time.** @return string*/private function format_time(): string {global $CFG;// Ensure calendar constants are loaded.require_once($CFG->dirroot . '/calendar/lib.php');$timeformat = get_user_preferences('calendar_timeformat');if (empty($timeformat)) {$timeformat = get_config(null, 'calendar_site_timeformat');}// Allow language customization of selected time format.if ($timeformat === CALENDAR_TF_12) {$timeformat = get_string('strftimetime12', 'langconfig');} else if ($timeformat === CALENDAR_TF_24) {$timeformat = get_string('strftimetime24', 'langconfig');}if ($timeformat) {return userdate($this->datetime->getTimestamp(), $timeformat);}// Let's use default format.if ($this->langtimeformat === null) {$langtimeformat = get_string('strftimetime');}return userdate($this->datetime->getTimestamp(), $langtimeformat);}}