L'affichage de dates est (souvent) un sujet épineux dans la création de contenus.
Il n'y a souvent pas de place côté design pour les dates longues, par exemple un mercredi 25 décembre 2024 ça prend beaucoup plus d'espace qu'un jeudi 1er mai 2025.
Pour peu qu'on y ajoute l'heure, ça va vite déborder de partout !
On peut être tenté de raccourcir le format avec 25/12/2024 ou 01/05/2024, mais ça peut poser un problème pour l'accessibilité. En effet, un lecteur d'écran va lire quelque chose comme :
vingt-cinq barre oblique douze barre oblique deux mille vingt-quatre.
Ce n'est pas très joyeux côté compréhension.
Une solution est d'utiliser la balise abbr avec l'attribut universel title pour afficher des formats plus courts tout en restant clair, en lecture d'écran et visuellement.
Le classe PHP ci-après permet de choisir le type d'affichage selon le contexte.
L'internationalisation est prise en charge pour Français, Anglais et Espagnol, avec possibilité de l'étendre si nécessaire.
Première chose, définition de constantes globales.
Constantes nécessaires
/*
* les clés des tableaux sont basées sur le format de la fonction PHP date("M")
*/
const DAYS = [
'long' => [
'fr' => ['Sun' => 'dimanche', 'Mon' => 'lundi', 'Tue' => 'mardi', 'Wed' => 'mercredi', 'Thu' => 'jeudi', 'Fri' => 'vendredi', 'Sat' => 'samedi'],
'en' => ['Sun' => 'sunday', 'Mon' => 'monday', 'Tue' => 'tuesday', 'Wed' => 'wednesday', 'Thu' => 'thursday', 'Fri' => 'friday', 'Sat' => 'saturday'],
'es' => ['Sun' => 'domingo', 'Mon' => 'lunes', 'Tue' => 'martes', 'Wed' => 'miércoles', 'Thu' => 'jueves', 'Fri' => 'viernes', 'Sat' => 'sábado']
],
'short' => [
'fr' => ['Sun' => 'di.', 'Mon' => 'lu.', 'Tue' => 'mar.', 'Wed' => 'mer.', 'Thu' => 'jeu.', 'Fri' => 'vend.', 'Sat' => 'sam.'],
'en' => ['Sun' => 'sun.', 'Mon' => 'mon.', 'Tue' => 'tue.', 'Wed' => 'wed.', 'Thu' => 'thu.', 'Fri' => 'fri.', 'Sat' => 'sat.'],
'es' => ['Sun' => 'dom.', 'Mon' => 'lun.', 'Tue' => 'mart.', 'Wed' => 'miérc.', 'Thu' => 'juev.', 'Fri' => 'vier.', 'Sat' => 'sáb.']
]
];
const MONTHS = [
'long' => [
'fr' => ['Jan' => 'janvier', 'Feb' => 'février', 'Mar' => 'mars', 'Apr' => 'avril', 'May' => 'mai', 'Jun' => 'juin', 'Jul' => 'juillet', 'Aug' => 'août', 'Sep' => 'septembre', 'Oct' => 'octobre', 'Nov' => 'novembre', 'Dec' => 'décembre'],
'en' => ['Jan' => 'january', 'Feb' => 'february', 'Mar' => 'march', 'Apr' => 'april', 'May' => 'may', 'Jun' => 'june', 'Jul' => 'july', 'Aug' => 'august', 'Sep' => 'september', 'Oct' => 'october', 'Nov' => 'november', 'Dec' => 'december'],
'es' => ['Jan' => 'enero', 'Feb' => 'febrero', 'Mar' => 'marzo', 'Apr' => 'abril', 'May' => 'mayo', 'Jun' => 'juno', 'Jul' => 'julio', 'Aug' => 'agosto', 'Sep' => 'septiembre', 'Oct' => 'octubre', 'Nov' => 'noviembre', 'Dec' => 'diciembre']
],
'short' => [
'fr' => ['Jan' => 'janv.', 'Feb' => 'fév.', 'Mar' => 'mars', 'Apr' => 'avr.', 'May' => 'mai', 'Jun' => 'juin', 'Jul' => 'juil.', 'Aug' => 'août', 'Sep' => 'sept.', 'Oct' => 'oct.', 'Nov' => 'nov.', 'Dec' => 'déc.'],
'en' => ['Jan' => 'jan.', 'Feb' => 'feb.', 'Mar' => 'mar', 'Apr' => 'apr.', 'May' => 'may', 'Jun' => 'june', 'Jul' => 'july', 'Aug' => 'aug.', 'Sep' => 'sept.', 'Oct' => 'oct.', 'Nov' => 'nov.', 'Dec' => 'dec.'],
'es' => ['Jan' => 'en.', 'Feb' => 'feb.', 'Mar' => 'mzo.', 'Apr' => 'abr.', 'May' => 'my.', 'Jun' => 'jun.', 'Jul' => 'jul.', 'Aug' => 'agt.', 'Sep' => 'sept.', 'Oct' => 'oct.', 'Nov' => 'nov.', 'Dec' => 'dic.']
]
];
La classe PHP NewDate
Les explications sont en commentaires dans le code.
Pour les exemples, on utilisera la première minute de 25 décembre 2024 avec 1735084860 comme valeur.
<?php
/*
* $timestamp : vide ou timestamp UNIX
* $complete : true affiche une date littérale complète, false affiche une date avec des abréviations et title
* $timeFormat : format d'affichage de l'heure du $timestamp
* $dayDisplayed : affiche le jour complet ou raccourci avant la date (permet de limiter l'espace)
* $lang : une valeur (fr, en, es, etc…) qui doit mapper les constantes DAYS et MONTHS
*/
class NewDate
{
public string $date;
public string $hour;
public string $prefix = '';
public string $suffix = '';
protected string $lang;
protected int $time;
public function __construct(
$timestamp = null,
bool $complete = true,
string $timeFormat = "H\hi",
bool $dayDisplayed = true,
string $lang = 'fr'
) {
// Vérification de l'existence de la langue dans les constantes
$this->lang = array_key_exists($lang, MONTHS['long']) ? $lang : 'en';
$this->time = $this->validateTimestamp($timestamp);
$numericDay = date("j", $this->time);
// Préfixes et suffixes basés sur la langue
[$this->prefix, $this->suffix] = $this->setPrefixesAndSuffixes($numericDay);
$outputDay = $dayDisplayed ? $this->formatDay($complete) : '';
$outputMonth = $this->formatMonth($complete);
// Formatage de la date
$this->date = implode(' ', array_filter([
$outputDay,
"{$this->prefix}{$numericDay}{$this->suffix}",
$outputMonth,
date('Y', $this->time),
]));
$this->hour = date($timeFormat, $this->time);
}
protected function validateTimestamp($timestamp): int
{
return is_numeric($timestamp) && checkdate(date("m", $timestamp), date("d", $timestamp), date("Y", $timestamp))
? $timestamp
: time();
}
protected function setPrefixesAndSuffixes(int $numericDay): array
{
return match ($this->lang) {
'fr' => ['', $numericDay === 1 ? '<sup>er</sup>' : ''],
'es' => [' el ', ' de '],
'en' => [' the ', "<sup>{$this->getEnglishOrdinal($numericDay)}</sup> of"],
default => ['', ''],
};
}
protected function getEnglishOrdinal(int $day): string
{
return match (true) {
in_array($day, [1, 21, 31]) => 'st',
in_array($day, [2, 22]) => 'nd',
in_array($day, [3, 23]) => 'rd',
default => 'th',
};
}
protected function formatDay(bool $complete): string
{
return $complete
? DAYS['long'][$this->lang][date('D', $this->time)]
: self::abbreviationsDays(date('D', $this->time), $this->lang);
}
protected function formatMonth(bool $complete): string
{
return $complete
? ($this->lang === 'en'
? ucfirst(MONTHS['long'][$this->lang][date('M', $this->time)])
: MONTHS['long'][$this->lang][date('M', $this->time)])
: self::abbreviationsMonths(date('M', $this->time), $this->lang, $this->lang === 'en');
}
public static function abbreviationsDays($abbr, string $lang = 'fr'): string
{
return sprintf(
'<abbr lang="%s" data-abbr="%s" title="%s"><span class="sr-only">%s</span></abbr>',
$lang,
DAYS['short'][$lang][$abbr],
DAYS['long'][$lang][$abbr],
DAYS['long'][$lang][$abbr]
);
}
public static function abbreviationsMonths($abbr, string $lang = 'fr', bool $ucFirst = false): string
{
return sprintf(
'<abbr lang="%s" data-abbr="%s" title="%s"><span class="sr-only">%s</span></abbr>',
$lang,
$ucFirst ? ucfirst(MONTHS['short'][$lang][$abbr]) : MONTHS['short'][$lang][$abbr],
MONTHS['long'][$lang][$abbr],
MONTHS['long'][$lang][$abbr]
);
}
public function __toString(): string
{
return $this->date;
}
}
Utilisation
-
Affichage de la date courante :
echo new NewDate()affiche la date littérale simple, sans heure.
Exemple : mercredi 25 décembre 2024 (si nous sommes le 25 décembre et en 2024…)Affichage de la date littérale + l'heure
$date = new NewDate(); echo $date->date . ' ' . $date->hour;echo new NewDate(dayDisplayed:false)affiche la date sans le jour (lundi, mardi…) et sans heure.
Exemple : 25 décembre 2024 -
Affichage d'une date littérale avec un
timestamp
echo new NewDate(1735084860)affiche la date littérale simple, sans heure. -
Affichage raccourci avec des abréviations
echo new NewDate(1735084860, complete: false, dayDisplayed: false)affichera 25 dec. 2024 avec ce code HTML
25 <abbr lang="fr" title="décembre" data-abbr="déc."><span class="sr-only">décembre</span></abbr> 2024
Comment ça s'affiche ?
En profitant des alt CSS avec content. La doc est sur MDN.
Résultat : visuellement nous avons l'abréviation et les lecteurs d'écran ne liront que le contenu.
abbr[data-abbr]::after {
/* content: attr(data-abbr); !* si navigateur sans alt CSS *! */
content: "\20" attr(data-abbr) / "";
}
La balise time
Pour que ce soit encore plus propre, on peut encadrer ce contenu avec une balise time qui apportera une couche SEO sympathique.
<time datetime="2024-12-25T00:01:00+01:00">
25 <abbr lang="fr" title="décembre" data-abbr="déc."><span class="sr-only">décembre</span></abbr> 2024
</time>
Pour retourner une valeur de datetime conforme, une petite fonction :
function Datetime($timestamp = null, $timezone = false): string
{
$timezone_get = date_default_timezone_get();
date_default_timezone_set(!is_bool($timezone) ? $timezone : (!$timezone ? 'Europe/Paris' : 'UTC'));
$return = date('c', is_null($timestamp) ? time() : $timestamp);
date_default_timezone_set($timezone_get);
// Output 2024-12-19T17:25:38+01:00
return $return;
}
Version pour les têtus…
Si vous ne pouvez (voulez…) pas faire autrement que d'afficher une date au format 25/12/2024, il peut être intéressant de proposer une version qui réponde à tous les besoins.
<time datetime="2024-12-25T00:01:00+01:00">
<span class="screen-readers">25 décembre 2024</span>
<span aria-hidden="true">25/12/2024</span>
</time>
La classe screen-readers est l'équivalent de sr-only ou visually-hidden selon les versions d'environnements.
Dans cette version, la date avec les fameuses barres obliques tant aimées pas Gigi sera évitée pas les lecteurs d'écrans. La date littérale, sera lue correctement.
Astuce
On peut mélanger toutes les options :
-
date abrégée + heure
$date = new NewDate(1735084860, false); echo implode(' à ', [$date->date, $date->hour]);Affiche mer. 25 déc. 2024 à 00h01 visuellement.
Code généré :
<abbr lang="fr" title="mercredi" data-abbr="mer."><span class="sr-only">mercredi</span></abbr> 25 <abbr lang="fr" title="décembre" data-abbr="déc."><span class="sr-only">décembre</span></abbr> 2024 à 00h01 -
date abrégée + heure, sans jour littéral
$date = new NewDate(1735084860, false, dayDisplayed:false); echo implode(' à ', [$date->date, $date->hour]);Visuellement, affiche 25 déc. 2024 à 00h01.
Code généré :25 <abbr lang="fr" title="décembre" data-abbr="déc."> <span class="sr-only">décembre</span></abbr> 2024 à 00h01
That's all folks !