Skip to content

Commit 3d7b417

Browse files
authored
Merge pull request #6613 from christianbeeznest/ofaj-22865
Internal: Resolve locale from custom ISOs in date_to_str_ago - refs BT#22865
2 parents 23d9c58 + 668b6f4 commit 3d7b417

File tree

2 files changed

+133
-39
lines changed

2 files changed

+133
-39
lines changed

public/main/inc/lib/internationalization.lib.php

Lines changed: 132 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -503,63 +503,157 @@ function api_format_date($time, $format = null, $language = null)
503503
}
504504

505505
/**
506-
* Returns the difference between the current date (date(now)) with the parameter
507-
* $date in a string format like "2 days ago, 1 hour ago".
508-
* You can use it like this:
509-
* Display::dateToStringAgoAndLongDate($dateInUtc);.
506+
* Return a Westsworld\TimeAgo translation instance based on Chamilo's isocode.
507+
* Tolerates custom isocodes like "pl_polish2", "es_spanish", etc.
510508
*
511-
* @param string|DateTime $date Result of a date function in this format -> date('Y-m-d H:i:s', time());
512-
* @param string $timeZone
513-
* @param bool $returnDateDifference
514-
*
515-
* @return string
516-
*
517-
* @author Julio Montoya
509+
* @return object Instance of Westsworld\TimeAgo\Translations\*
518510
*/
519-
function date_to_str_ago($date, $timeZone = 'UTC', $returnDateDifference = false)
511+
function timeago_resolve_language(?string $iso)
520512
{
521-
if ('0000-00-00 00:00:00' === $date) {
522-
return '';
513+
// Normalize ISO (snake_case, lowercase)
514+
$iso = $iso ?: api_get_language_isocode();
515+
$norm = strtolower(str_replace('-', '_', trim($iso)));
516+
517+
// Candidates: full, parent chain (if available), then base (first 2 letters)
518+
$candidates = [$norm];
519+
520+
if (class_exists('SubLanguageManager')) {
521+
$tmp = $iso;
522+
// SubLanguageManager returns parents like "en_US"; normalize to snake_case
523+
while (!empty($parent = SubLanguageManager::getParentLocale($tmp))) {
524+
$candidates[] = strtolower(str_replace('-', '_', $parent));
525+
$tmp = $parent;
526+
}
523527
}
524528

525-
$getOldTimezone = api_get_timezone();
526-
$isoCode = api_get_language_isocode();
527-
if ('pt' === $isoCode) {
528-
$isoCode = 'pt-BR';
529+
if (preg_match('/^[a-z]{2}/', $norm, $m)) {
530+
$base = $m[0]; // e.g. "pl" from "pl_polish2", "es" from "es_spanish"
531+
$candidates[] = $base;
532+
} else {
533+
$base = $norm;
534+
}
535+
536+
// Map of classes supported by the library (key = snake_case, value = class suffix)
537+
$map = [
538+
'ar' => 'Ar', 'bg' => 'Bg', 'cs' => 'Cs', 'da' => 'Da',
539+
'de' => 'De', 'el' => 'El', 'en' => 'En', 'es' => 'Es',
540+
'fa' => 'Fa', 'fi' => 'Fi', 'fr' => 'Fr', 'he' => 'He',
541+
'hr' => 'Hr', 'hu' => 'Hu', 'id' => 'Id', 'it' => 'It',
542+
'ja' => 'Ja', 'ko' => 'Ko', 'nb' => 'Nb', 'nl' => 'Nl',
543+
'no' => 'No', 'pl' => 'Pl',
544+
'pt_br' => 'Pt_BR', 'pt_pt' => 'Pt_PT',
545+
'ro' => 'Ro', 'ru' => 'Ru', 'sk' => 'Sk', 'sr' => 'Sr',
546+
'sv' => 'Sv', 'tr' => 'Tr', 'uk' => 'Uk', 'vi' => 'Vi',
547+
'zh_cn' => 'Zh_CN', 'zh_tw' => 'Zh_TW',
548+
'pt' => 'Pt_BR',
549+
'zh' => 'Zh_CN',
550+
'nn' => 'Nb',
551+
];
552+
553+
// Try exact candidates first, then special rules, then the base
554+
foreach ($candidates as $cand) {
555+
if (isset($map[$cand])) {
556+
$class = "Westsworld\\TimeAgo\\Translations\\{$map[$cand]}";
557+
if (class_exists($class)) {
558+
return new $class();
559+
}
560+
}
561+
562+
// Special handling when the candidate is an ambiguous base
563+
if ($cand === 'pt') {
564+
foreach (['Pt_BR', 'Pt_PT'] as $suf) {
565+
$class = "Westsworld\\TimeAgo\\Translations\\$suf";
566+
if (class_exists($class)) {
567+
return new $class();
568+
}
569+
}
570+
}
571+
if ($cand === 'zh') {
572+
foreach (['Zh_CN', 'Zh_TW'] as $suf) {
573+
$class = "Westsworld\\TimeAgo\\Translations\\$suf";
574+
if (class_exists($class)) {
575+
return new $class();
576+
}
577+
}
578+
}
579+
if ($cand === 'nn') {
580+
foreach (['Nb', 'No'] as $suf) {
581+
$class = "Westsworld\\TimeAgo\\Translations\\$suf";
582+
if (class_exists($class)) {
583+
return new $class();
584+
}
585+
}
586+
}
529587
}
530-
if ('fr_FR' === $isoCode) {
531-
$isoCode = 'Fr';
588+
589+
// Last attempt: try base directly if it has a mapping
590+
if (isset($map[$base])) {
591+
$class = "Westsworld\\TimeAgo\\Translations\\{$map[$base]}";
592+
if (class_exists($class)) {
593+
return new $class();
594+
}
532595
}
533-
$isoCode = ucfirst($isoCode);
534-
$class = "Westsworld\TimeAgo\Translations\\".$isoCode;
535-
if (class_exists($class)) {
536-
$language = new $class();
537-
} else {
538-
$language = new Westsworld\TimeAgo\Translations\En();
596+
597+
// Final fallback: English
598+
return new Westsworld\TimeAgo\Translations\En();
599+
}
600+
601+
/**
602+
* Time-ago function with proper locale resolution (including custom isocodes)
603+
* and consistent timezone handling.
604+
*/
605+
function date_to_str_ago($date, $timeZone = null, $returnDateDifference = false)
606+
{
607+
if (empty($date) || '0000-00-00 00:00:00' === $date) {
608+
return '';
539609
}
540610

541-
$timeAgo = new TimeAgo($language);
611+
// Resolve timezone: prefer parameter, otherwise user/platform timezone
612+
$tz = $timeZone ?: api_get_timezone();
613+
614+
// Resolve language for TimeAgo (tolerant to pl_polish2, es_spanish, etc.)
615+
$language = timeago_resolve_language(api_get_language_isocode());
616+
$timeAgo = new TimeAgo($language);
617+
618+
// Normalize $date to DateTime in the same timezone as "now"
542619
if (!($date instanceof DateTime)) {
543-
$date = api_get_utc_datetime($date, null, true);
620+
if (is_numeric($date)) {
621+
// Timestamp: create from UTC epoch and then set target TZ
622+
$dateObj = new DateTime('@'.(int) $date);
623+
$dateObj->setTimezone(new DateTimeZone($tz));
624+
$date = $dateObj;
625+
} else {
626+
// Assume DB string is UTC, then convert to target TZ
627+
$date = new DateTime($date, new DateTimeZone('UTC'));
628+
$date->setTimezone(new DateTimeZone($tz));
629+
}
630+
} else {
631+
// Ensure provided DateTime uses the target TZ
632+
$date->setTimezone(new DateTimeZone($tz));
544633
}
545634

546-
$value = $timeAgo->inWords($date);
547-
date_default_timezone_set($getOldTimezone);
635+
// Ensure the library computes "now" in the same TZ
636+
$oldTz = date_default_timezone_get();
637+
date_default_timezone_set($tz);
548638

549639
if ($returnDateDifference) {
550-
$now = new DateTime('now', $date->getTimezone());
551-
$value = $date->diff($now);
640+
$now = new DateTime('now', new DateTimeZone($tz));
641+
$diff = $date->diff($now);
642+
date_default_timezone_set($oldTz);
552643

553644
return [
554-
'years' => $value->y,
555-
'months' => $value->m,
556-
'days' => $value->d,
557-
'hours' => $value->h,
558-
'minutes' => $value->i,
559-
'seconds' => $value->s,
645+
'years' => $diff->y,
646+
'months' => $diff->m,
647+
'days' => $diff->d,
648+
'hours' => $diff->h,
649+
'minutes' => $diff->i,
650+
'seconds' => $diff->s,
560651
];
561652
}
562653

654+
$value = $timeAgo->inWords($date);
655+
date_default_timezone_set($oldTz);
656+
563657
return $value;
564658
}
565659

public/main/ticket/tickets.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ function display_advanced_search_form () {
194194
0 => get_lang('Unassigned'),
195195
];
196196
foreach ($admins as $admin) {
197-
$selectAdmins[$admin['user_id']] = $admin['complete_name_with_username'];
197+
$selectAdmins[$admin['id']] = $admin['firstname'].' '.$admin['lastname'];
198198
}
199199
$status = TicketManager::get_all_tickets_status();
200200
$selectStatus = [];

0 commit comments

Comments
 (0)