1: <?php
2: namespace TIC\TwigBundle\Extension;
3:
4: use TIC\TwigBundle\Base\TICTwigExtension as BaseExtension;
5: use TIC\CoreBundle\Util\ConvertHelper;
6: #use TIC\CoreBundle\Util\StringHelper;
7:
8: use Twig\TwigFilter;
9: use Twig\Environment;
10:
11: /**
12: * Filtres et fonctions twig de formattage de dates.
13: * https://twig.symfony.com/doc/3.x/filters/format_datetime.html
14: */
15: class DatetimeExtension extends BaseExtension
16: {
17:
18: public function getFilters(): array
19: {
20: return [
21: new TwigFilter('fdate', [$this, 'fdateFilter'] ),
22: new TwigFilter('ftime', [$this, 'ftimeFilter'] ),
23: new TwigFilter('fdatetime', [$this, 'fdatetimeFilter'] ),
24: new TwigFilter('ndatetime', [$this, 'ndatetimeFilter'] ),
25: new TwigFilter('ldatetime', [$this, 'ldatetimeFilter'], ['needs_environment'=>true] ),
26: new TwigFilter('ldate', [$this, 'ldateFilter'], ['needs_environment'=>true] ),
27: new TwigFilter('ltime', [$this, 'ltimeFilter'], ['needs_environment'=>true] ),
28: new TwigFilter('ldatehour', [$this, 'ldatehourFilter'], ['needs_environment'=>true] ),
29: new TwigFilter('lhour', [$this, 'lhourFilter'], ['needs_environment'=>true] ),
30: new TwigFilter('lmonth', [$this, 'lmonthFilter'], ['needs_environment'=>true] ),
31: new TwigFilter('duration', [$this, 'durationFilter'], ['is_safe' => ['html']] ),
32: ];
33: }
34:
35:
36: /**
37: * Formattage d'une date (ou chaine par défaut si date vide).
38: *
39: * @param mixed $value Variable indiquant la date (objet DateTime sinon timestamp numérique)
40: * @param string $format Motif avec la syntaxe de strftime() sinon date()
41: * @param string $default Chaine à retourner par défaut si la variable est vide
42: * @return string Chaine représentant la date avec le format indiqué
43: */
44: public function fdateFilter($value, string $format = '%A %e %B %Y', string $default = ''): string
45: {
46: $date = ConvertHelper::timestamp($value);
47: if ($date === null) return $default;
48:
49: if (\strpos($format, '%') === false) return \date($format, $date);
50:
51: $locale = \setlocale(LC_ALL, '0');
52: \setlocale(LC_ALL, 'fr_FR.UTF8', 'fr_FR', 'fr', 'french');
53: if (PHP_OS != 'Linux') $format = \str_replace('%e', '%#d', $format);
54: $date = \strftime($format, $date);
55: \setlocale(LC_ALL, $locale);
56: return $date;
57: }
58:
59: /**
60: * Formattage d'une date (ou chaine par défaut si date vide).
61: * Note: variante du filtre "fdate" avec un format par défaut incluant les heures
62: *
63: * @param mixed $value Variable indiquant la date (objet DateTime sinon timestamp numérique)
64: * @param string $format Motif avec la syntaxe de strftime() sinon date()
65: * @param string $default Chaine à retourner par défaut si la variable est vide
66: * @return string Chaine représentant la date avec le format indiqué
67: */
68: public function fdatetimeFilter($value, string $format = '%A %e %B %Y - %H:%M', string $default = ''): string
69: {
70: return $this->fdateFilter($value, $format, $default);
71: }
72:
73: /**
74: * Formattage d'un horaire (ou chaine par défaut si date vide).
75: *
76: * @param mixed $value Variable indiquant la date (objet DateTime sinon timestamp numérique)
77: * @param string $format Motif avec la syntaxe de strftime() sinon date()
78: * @param string $default Chaine à retourner par défaut si la variable est vide
79: * @return string Chaine représentant l'horaire avec le format indiqué
80: *
81: */
82: public function ftimeFilter($value, string $format = 'H:i', string $default = ''): string
83: {
84: $date = ConvertHelper::timestamp($value);
85: if ($date === null) return $default;
86:
87: return (\strpos($format, '%') === false) ? \date($format, $date) : \strftime($format, $date);
88: }
89:
90: /**
91: * Formattage d'une date au format numérique 'YYYYMMDDhhmmss' (ou chaine par défaut si date vide).
92: *
93: * @param mixed $value Variable indiquant la date (objet DateTime sinon timestamp numérique)
94: * @param string $default Chaine à retourner par défaut si la variable est vide
95: * @return string Chaine représentant la date sous sa forme numérique
96: */
97: public function ndatetimeFilter($value, string $default = ''): string
98: {
99: $date = ConvertHelper::timestamp($value);
100: if ($date === null) return $default;
101:
102: return \date('YmdHis', $date);
103: }
104:
105: /**
106: * Formattage localisé d'une date avec horaire (selon des profils de formats).
107: */
108: public function ldatetimeFilter(Environment $env, $date, ?string $dateFormat = 'medium', ?string $timeFormat = null, string $default = '', string $locale = null, $timezone = null, string $pattern = ''): string
109: {
110: // retourner NULL (ou autre valeur par défaut spécifiée) en l'absence de date
111: if (($date === null) || ($date === '')) return $default;
112:
113: // timeFormat par défaut identique au dateFormat
114: if ($timeFormat === null) $timeFormat = $dateFormat;
115:
116: // code original de Twig_Extensions_Extension_Intl::twig_localized_date_filter :
117: $filter = $env->getFilter('format_datetime')->getCallable();
118: return \call_user_func($filter, $env, $date, $dateFormat, $timeFormat, $pattern, $timezone, 'gregorian', $locale);
119: }
120:
121: /**
122: * Formattage localisé d'une date sans horaire (selon des profils de formats).
123: */
124: public function ldateFilter(Environment $env, $date, ?string $dateFormat = 'medium', string $default = '', string $locale = null, $timezone = null): string
125: {
126: return $this->ldatetimeFilter($env, $date, $dateFormat, 'none', $default, $locale, $timezone);
127: }
128:
129: /**
130: * Formattage localisé d'un horaire (selon des profils de formats).
131: */
132: public function ltimeFilter(Environment $env, $date, ?string $timeFormat = 'medium', string $default = '', string $locale = null, $timezone = null): string
133: {
134: return $this->ldatetimeFilter($env, $date, 'none', $timeFormat, $default, $locale, $timezone);
135: }
136:
137: /**
138: * Formattage localisé d'une date avec horaire, mais sans les secondes (selon des profils de formats).
139: */
140: public function ldatehourFilter(Environment $env, $date, ?string $dateFormat = 'medium', ?string $timeFormat = null, string $default = '', string $locale = null, $timezone = null): string
141: {
142: return \preg_replace('/(\d\d?:\d\d?):\d\d?/', '$1', $this->ldatetimeFilter($env, $date, $dateFormat, $timeFormat, $default, $locale, $timezone));
143: }
144:
145: /**
146: * Formattage localisé d'un horaire, mais sans les secondes (selon des profils de formats).
147: */
148: public function lhourFilter(Environment $env, $date, ?string $timeFormat = 'short', string $default = '', string $locale = null, $timezone = null): string
149: {
150: return \preg_replace('/(\d\d?:\d\d?):\d\d?/', '$1', $this->ldatetimeFilter($env, $date, 'none', $timeFormat, $default, $locale, $timezone));
151: }
152:
153: /**
154: * Formattage localisé d'un mois (selon des profils de formats).
155: */
156: public function lmonthFilter(Environment $env, $date, ?string $format = 'long', string $default = '', string $locale = null, $timezone = null): string
157: {
158: switch ($format) {
159: case 'short' : $pattern = 'L'; break;
160: case 'medium': $pattern = 'LL'; break;
161: case 'long' : $pattern = 'LLL'; break;
162: case 'full' : $pattern = 'LLLL'; break;
163: default : $pattern = 'LLL';
164: }
165: return $this->ldatetimeFilter($env, $date, 'none', 'none', $default, $locale, $timezone, $pattern);
166: }
167:
168: /**
169: * Formattage d'une durée en décomposant un nombre de secondes avec toutes les unités intermédiaires appropriées.
170: *
171: * @param mixed $seconds Durée à afficher en nombre de secondes
172: * @param string $sep Séparateur pour la construction de la chaine de caractère
173: * @param array $units Liste des unités utilisables pour la décomposition (parmis: C,D,Y,M,W,d,h,m,s)
174: * @param string $pluriel Caractère à ajouter aux unités pour leurs formes plurielles
175: * @param bool $opt_week Unité 'week' supprimée automatiquement si durée supérieure au mois (quelque soit la liste $units)
176: * @return string Chaine de caractères décrivant la durée (ex: 123456789 => "46 mois 28 jours 19 heures 16 minutes")
177: */
178: public function durationFilter($seconds, string $sep=' ', array $units=array('M'=>' mois','d'=>' jour','h'=>' heure','m'=>' minute'), string $pluriel='s', bool $opt_week=true): string
179: {
180: if ($seconds === null) return '';
181: $seconds = \is_object($seconds) ? $seconds->getTimestamp() + $seconds->getOffset() : \intval($seconds);
182: $sign = ($seconds < 0) ? '- ' : '';
183: $seconds = \abs($seconds);
184:
185: $periods = array(
186: 'C' => 3155692600,
187: 'D' => 315569260,
188: 'Y' => 31556926,
189: 'M' => 2629743,
190: 'W' => 604800,
191: 'd' => 86400,
192: 'h' => 3600,
193: 'm' => 60,
194: 's' => 1,
195: );
196: // on supprime le découpage par semaines si on dépasse le mois
197: if (($opt_week) && ($seconds >= $periods['M'])) unset($periods['W']);
198:
199: $durations = array();
200: $range = \array_keys($units);
201: foreach ($periods as $p=>$d) {
202: if (! \in_array($p, $range)) continue;
203: if ($seconds >= $d) {
204: $durations[$p] = \floor($seconds / $d);
205: $seconds -= $durations[$p] * $d;
206: }
207: }
208:
209: $list = array();
210: foreach ($durations as $p=>$n) \array_push($list, $n . $units[$p] . ((($n>1)&&(\substr($units[$p],-1)!=$pluriel))?$pluriel:''));
211: if (\count($list) == 0) \array_push($list, 0 . \array_pop($units));
212:
213: return $sign . \implode($sep, $list);
214: }
215:
216: }
217: