-
-
Save ve3/a3b7924a85c3286b554f9e636b919883 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* PHP `strftime()` format to `IntlDateFormatter()` pattern converter. | |
* | |
* PHP `strftime()` is deprecated since v 8.1. They recommended to use `IntlDateFormatter()` instead. | |
* However `IntlDateFormatter()` pattern does not fully supported all format that `strftime()` does. | |
* | |
* Run this file to get the result of most close pattern to `strftime()` based on Thai locale. | |
* | |
* @license MIT | |
*/ | |
$locale = ($_POST['locale'] ?? 'en_US'); | |
setlocale(LC_ALL, $locale); | |
// replace from `strftime` format to `IntlDateFormatter` pattern. | |
// based on Thai locale. | |
$replaces = [ | |
'%a' => 'E', | |
'%A' => 'EEEE', | |
'%d' => 'dd', | |
'%e' => 'd', | |
'%j' => 'D', | |
'%u' => 'e',// not 100% correct | |
'%w' => 'c',// not 100% correct | |
'%U' => 'w', | |
'%V' => 'ww',// not 100% correct | |
'%W' => 'w',// not 100% correct | |
'%b' => 'MMM', | |
'%B' => 'MMMM', | |
'%h' => 'MMM',// alias of %b | |
'%m' => 'MM', | |
'%C' => 'yy',// no replace for this | |
'%g' => 'yy',// no replace for this | |
'%G' => 'Y',// not 100% correct | |
'%y' => 'yy', | |
'%Y' => 'yyyy', | |
'%H' => 'HH', | |
'%k' => 'H', | |
'%I' => 'hh', | |
'%l' => 'h', | |
'%M' => 'mm', | |
'%p' => 'a', | |
'%P' => 'a',// no replace for this | |
'%r' => 'hh:mm:ss a', | |
'%R' => 'HH:mm', | |
'%S' => 'ss', | |
'%T' => 'HH:mm:ss', | |
'%X' => 'HH:mm:ss',// no replace for this | |
'%z' => 'ZZ', | |
'%Z' => 'v',// no replace for this | |
'%c' => 'd/M/YYYY HH:mm:ss',// Buddhist era not converted. | |
'%D' => 'MM/dd/yy', | |
'%F' => 'yyyy-MM-dd', | |
'%s' => '',// no replace for this | |
'%x' => 'd/MM/yyyy',// Buddhist era not converted. | |
'%n' => "\n", | |
'%t' => "\t", | |
'%%' => '%', | |
]; | |
$format = ($_POST['format'] ?? ''); | |
$calendar = null; | |
if (!empty($format)) { | |
$pattern = $format; | |
// replace 1 single quote that is not following visible character or single quote and not follow by single quote or word or number. | |
// example: ' | |
// replace with 2 single quotes. example: '' | |
$pattern = preg_replace('/(?<![\'\S])(\')(?![\'\w])/u', "'$1", $pattern); | |
// replace 1 single quote that is not following visible character or single quote and follow by word. | |
// example: 'xx | |
// replace with 2 single quotes. example: ''xx | |
$pattern = preg_replace('/(?<![\'\S])(\')(\w+)/u', "'$1$2", $pattern); | |
// replace 1 single quote that is following word (a-z 0-9) and not follow by single quote. | |
// example: xx' | |
// replace with 2 single quotes. example: xx'' | |
$pattern = preg_replace('/([\w]+)(\')(?!\')/u', "$1'$2", $pattern); | |
// replace a-z (include upper case) that is not following %. example xxx. | |
// replace with wrap single quote. example: 'xxx'. | |
$pattern = preg_replace('/(?<![%a-zA-Z])([a-zA-Z]+)/u', "'$1$2'", $pattern); | |
// escape %%x with '%%x'. | |
$pattern = preg_replace('/(%%[a-zA-Z]+)/u', "'$1'", $pattern); | |
foreach ($replaces as $strftime => $intl) { | |
$pattern = preg_replace('/(?<!%)(' . $strftime . ')/u', $intl, $pattern); | |
}// endforeach; | |
unset($intl, $strftime); | |
} | |
if (class_exists('IntlDateFormatter')) { | |
$tz = null; | |
$calendar = ($_POST['calendar'] ?? null); | |
if ($calendar === 'default') { | |
$calendar = null; | |
} elseif ($calendar === 'traditional') { | |
$calendar = \IntlDateFormatter::TRADITIONAL; | |
} | |
$IntlDateFormatter = new \IntlDateFormatter($locale, \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, $tz, $calendar); | |
$defaultPattern = $IntlDateFormatter->getPattern(); | |
unset($tz); | |
} | |
// set locales for select box. | |
$locales = [ | |
'en_US.utf-8', | |
'en_US.utf8', | |
'en_us.utf-8', | |
'en_us.utf8', | |
'en-US.utf-8', | |
'en-US.utf8', | |
'en-us.utf-8', | |
'en-us.utf8', | |
'en.utf-8', | |
'en.utf8', | |
'en_US', | |
'en_us', | |
'en-US', | |
'en-us', | |
'en', | |
'th_TH.utf-8', | |
'th_TH.utf8', | |
'th_th.utf-8', | |
'th_th.utf8', | |
'th-TH.utf-8', | |
'th-TH.utf8', | |
'th-th.utf-8', | |
'th-th.utf8', | |
'th.utf-8', | |
'th.utf8', | |
'th_TH', | |
'th_th', | |
'th-TH', | |
'th-th', | |
'th', | |
]; | |
?> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>strftime to IntlDateFormatter format converter</title> | |
<style type="text/css"> | |
code { | |
background-color: #eee; | |
padding: 2px 5px; | |
} | |
form { | |
margin: 0 0 20px; | |
} | |
form > div { | |
margin: 0 0 10px; | |
} | |
pre { | |
background-color: #eee; | |
padding: 10px; | |
} | |
table { | |
border: 1px solid #ccc; | |
border-collapse: collapse; | |
width: 100%; | |
} | |
table th, | |
table td { | |
border: 1px solid #ccc; | |
text-align: left; | |
padding: 5px; | |
} | |
table tr.different-result { | |
background-color: rgba(255, 218, 148, 0.4); | |
} | |
</style> | |
</head> | |
<bodY> | |
<form method="post"> | |
<div> | |
Locale: | |
<select name="locale"> | |
<?php | |
if (isset($locales) && is_array($locales)) { | |
foreach ($locales as $eachLocale) { | |
echo '<option value="' . $eachLocale . '"'; | |
if ($locale === $eachLocale) { | |
echo ' selected'; | |
} | |
echo '>' . $eachLocale . '</option>' . PHP_EOL; | |
}// endforeach; | |
unset($eachLocale); | |
} | |
?> | |
</select> | |
</div> | |
<div> | |
Calendar: | |
<select name="calendar"> | |
<option value="default"<?php if ($calendar === 'default') {echo ' selected';} ?>>Default</option> | |
<option value="traditional"<?php if ($calendar === 'traditional') {echo ' selected';} ?>>Traditional</option> | |
</select> | |
</div> | |
<div> | |
<code>strftime()</code> format: | |
<input type="text" name="format" value="<?php echo htmlspecialchars($format, ENT_QUOTES); ?>" style="width: 300px;"> | |
</div> | |
<button type="submit">Submit</button> | |
</form> | |
<?php | |
if (isset($pattern)) { | |
?> | |
<h3>Converted result</h3> | |
<pre><?php | |
echo htmlspecialchars($pattern, ENT_QUOTES); | |
?></pre> | |
<h3>Execute</h3> | |
<?php if (function_exists('strftime')) { ?> | |
<p><code>strftime('<?php echo $format; ?>')</code>: <strong><?php echo htmlspecialchars(@strftime($format), ENT_QUOTES); ?></strong></p> | |
<?php } ?> | |
<?php | |
if (isset($IntlDateFormatter)) { | |
$IntlDateFormatter->setPattern($pattern); | |
?> | |
<p><code>IntlDateFormatter::setPattern('<?php echo $pattern; ?>')</code>: <strong><?php echo htmlspecialchars($IntlDateFormatter->format(time()), ENT_QUOTES); ?></strong></p> | |
<?php | |
} | |
?> | |
<?php | |
}// endif; | |
?> | |
<p>Current locale: <code><?php echo $locale; ?></code></p> | |
<?php | |
if (isset($IntlDateFormatter)) { | |
$IntlDateFormatter->setPattern($defaultPattern); | |
?> | |
<p> | |
Default <code>IntlDateFormatter()</code> pattern (<code><?php echo $defaultPattern; ?></code>): <strong><?php echo $IntlDateFormatter->format(time()); ?></strong> | |
</p> | |
<?php | |
} | |
?> | |
<table> | |
<thead> | |
<tr> | |
<th><code>strftime()</code> format</th> | |
<th><code>strftime()</code> result</th> | |
<th><code>IntlDateFormatter()</code> pattern</th> | |
<th><code>IntlDateFormatter()</code> result</th> | |
</tr> | |
</thead> | |
<tbody> | |
<?php | |
foreach ($replaces as $strftime => $intl) { | |
if (function_exists('strftime')) { | |
$strftimeResult = @strftime($strftime); | |
} | |
if (isset($IntlDateFormatter)) { | |
$IntlDateFormatter->setPattern($intl); | |
$intlDResult = $IntlDateFormatter->format(time()); | |
} | |
if (isset($strftimeResult) && isset($intlDResult)) { | |
if ($strftimeResult != $intlDResult) { | |
$rowDif = true; | |
} | |
} | |
?> | |
<tr<?php if (isset($rowDif)) { echo ' class="different-result"'; } ?>> | |
<td><code><?php echo htmlspecialchars($strftime, ENT_QUOTES); ?></code></td> | |
<td><?php | |
if (isset($strftimeResult)) { | |
echo htmlspecialchars($strftimeResult, ENT_QUOTES); | |
} | |
?></td> | |
<td><code><?php echo htmlspecialchars($intl, ENT_QUOTES); ?></code></td> | |
<td><?php | |
if (isset($intlDResult)) { | |
echo htmlspecialchars($intlDResult, ENT_QUOTES); | |
} | |
?></td> | |
</tr> | |
<?php | |
unset($intlDResult, $rowDif, $strftimeResult); | |
}// endforeach; | |
unset($intl, $strftime); | |
?> | |
</tbody> | |
</table> | |
<p>Reference: | |
<a href="https://www.php.net/manual/en/function.strftime.php" target="strftome">strftime format.</a> | |
<a href="https://unicode-org.github.io/icu/userguide/format_parse/datetime/" target="ICU">ICU format</a> | |
</p> | |
</bodY> | |
</html> | |
<?php | |
unset($IntlDateFormatter, $replaces); |
%b to M MM MMM MMMM is maybe not the best choice. In german MMM is "März" and not "Mär" or "Sept." (with dot) and not "Sep". With L LL LLL LLLL its working like expected, only three letters for month. See https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table
Yes, you're welcome to recommended in your locale. 👍 But in my code it is based on Thai locale that's why it is just like that. (see code commented on line 8.)
Everyone can recommended anything based on your locale here. And thank you very much for this.
First: Thank you, this saved a ton of work!
%C
seems completely wrong since there is no century in ICU date formats.
I've done this for german and have come up with the following (whilst not 100% the same, it should make sense). Weirdly enough I noticed some strange behaviour with %s
- strftime
is exactly 1 hour behind. That might have something to do with winter-/summertime here, but not sure. Getting the actual correct timestamp is better anyways, so I'll just leave it like this.
<?php
/**
* PHP `strftime()` format to `IntlDateFormatter()` pattern converter.
*
* PHP `strftime()` is deprecated since v 8.1. They recommended to use `IntlDateFormatter()` instead.
* However `IntlDateFormatter()` pattern does not fully supported all format that `strftime()` does.
*
* Run this file to get the result of most close pattern to `strftime()` based on Thai locale.
*
* @license MIT
*/
$locale = ($_POST['locale'] ?? 'de_DE.UTF-8');
setlocale(LC_ALL, $locale);
// replace from `strftime` format to `IntlDateFormatter` pattern.
// based on German locale.
$replaces = [
'%a' => 'ccc',
'%A' => 'cccc',
'%d' => 'dd',
'%e' => 'd',
'%j' => 'D',
'%u' => 'e',// not 100% correct
'%w' => 'c',// not 100% correct
'%U' => 'w',
'%V' => 'ww',// not 100% correct
'%W' => 'w',// not 100% correct
'%b' => 'LLL',
'%B' => 'LLLL',
'%h' => 'LLL',// alias of %b
'%m' => 'LL',
'%C' => '\'{{century}}\'',// no replace for this
'%g' => 'yy',// no replace for this
'%G' => 'Y',// not 100% correct
'%y' => 'yy',
'%Y' => 'yyyy',
'%H' => 'HH',
'%k' => 'H',
'%I' => 'hh',
'%l' => 'h',
'%M' => 'mm',
'%p' => 'a',
'%P' => 'a',// no replace for this
'%r' => 'hh:mm:ss a',
'%R' => 'HH:mm',
'%S' => 'ss',
'%T' => 'HH:mm:ss',
'%X' => 'HH:mm:ss',// no replace for this
'%z' => 'ZZ',
'%Z' => 'zzz',// no replace for this
'%c' => 'ccc d LLL YYYY HH:mm:ss zzz',// Buddhist era not converted.
'%D' => 'MM/dd/yy',
'%F' => 'yyyy-MM-dd',
'%s' => '\'{{timestamp}}\'',// no replace for this
'%x' => 'd.MM.yyyy',// Buddhist era not converted.
'%n' => "\n",
'%t' => "\t",
'%%' => '%',
];
$format = ($_POST['format'] ?? '');
$calendar = null;
if (!empty($format)) {
$pattern = $format;
// replace 1 single quote that is not following visible character or single quote and not follow by single quote or word or number.
// example: '
// replace with 2 single quotes. example: ''
$pattern = preg_replace('/(?<![\'\S])(\')(?![\'\w])/u', "'$1", $pattern);
// replace 1 single quote that is not following visible character or single quote and follow by word.
// example: 'xx
// replace with 2 single quotes. example: ''xx
$pattern = preg_replace('/(?<![\'\S])(\')(\w+)/u', "'$1$2", $pattern);
// replace 1 single quote that is following word (a-z 0-9) and not follow by single quote.
// example: xx'
// replace with 2 single quotes. example: xx''
$pattern = preg_replace('/([\w]+)(\')(?!\')/u', "$1'$2", $pattern);
// replace a-z (include upper case) that is not following %. example xxx.
// replace with wrap single quote. example: 'xxx'.
$pattern = preg_replace('/(?<![%a-zA-Z])([a-zA-Z]+)/u', "'$1$2'", $pattern);
// escape %%x with '%%x'.
$pattern = preg_replace('/(%%[a-zA-Z]+)/u', "'$1'", $pattern);
foreach ($replaces as $strftime => $intl) {
$pattern = preg_replace('/(?<!%)(' . $strftime . ')/u', $intl, $pattern);
}// endforeach;
unset($intl, $strftime);
}
if (class_exists('IntlDateFormatter')) {
$tz = null;
$calendar = ($_POST['calendar'] ?? null);
if ($calendar === 'default') {
$calendar = null;
} elseif ($calendar === 'traditional') {
$calendar = \IntlDateFormatter::TRADITIONAL;
}
$IntlDateFormatter = new \IntlDateFormatter($locale, \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, $tz, $calendar);
$defaultPattern = $IntlDateFormatter->getPattern();
unset($tz);
}
// set locales for select box.
$locales = [
'de_DE.UTF-8'
];
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>strftime to IntlDateFormatter format converter</title>
<style type="text/css">
code {
background-color: #eee;
padding: 2px 5px;
}
form {
margin: 0 0 20px;
}
form > div {
margin: 0 0 10px;
}
pre {
background-color: #eee;
padding: 10px;
}
table {
border: 1px solid #ccc;
border-collapse: collapse;
width: 100%;
}
table th,
table td {
border: 1px solid #ccc;
text-align: left;
padding: 5px;
}
table tr.different-result {
background-color: rgba(255, 218, 148, 0.4);
}
</style>
</head>
<bodY>
<form method="post">
<div>
Locale:
<select name="locale">
<?php
if (isset($locales) && is_array($locales)) {
foreach ($locales as $eachLocale) {
echo '<option value="' . $eachLocale . '"';
if ($locale === $eachLocale) {
echo ' selected';
}
echo '>' . $eachLocale . '</option>' . PHP_EOL;
}// endforeach;
unset($eachLocale);
}
?>
</select>
</div>
<div>
Calendar:
<select name="calendar">
<option value="default"<?php if ($calendar === 'default') {echo ' selected';} ?>>Default</option>
<option value="traditional"<?php if ($calendar === 'traditional') {echo ' selected';} ?>>Traditional</option>
</select>
</div>
<div>
<code>strftime()</code> format:
<input type="text" name="format" value="<?php echo htmlspecialchars($format, ENT_QUOTES); ?>" style="width: 300px;">
</div>
<button type="submit">Submit</button>
</form>
<?php
if (isset($pattern)) {
?>
<h3>Converted result</h3>
<pre><?php
echo htmlspecialchars($pattern, ENT_QUOTES);
?></pre>
<h3>Execute</h3>
<?php if (function_exists('strftime')) { ?>
<p><code>strftime('<?php echo $format; ?>')</code>: <strong><?php echo htmlspecialchars(@strftime($format), ENT_QUOTES); ?></strong></p>
<?php } ?>
<?php
if (isset($IntlDateFormatter)) {
$IntlDateFormatter->setPattern($pattern);
?>
<p><code>IntlDateFormatter::setPattern('<?php echo $pattern; ?>')</code>: <strong><?php echo htmlspecialchars($IntlDateFormatter->format(time()), ENT_QUOTES); ?></strong></p>
<?php
}
?>
<?php
}// endif;
?>
<p>Current locale: <code><?php echo $locale; ?></code></p>
<?php
if (isset($IntlDateFormatter)) {
$IntlDateFormatter->setPattern($defaultPattern);
?>
<p>
Default <code>IntlDateFormatter()</code> pattern (<code><?php echo $defaultPattern; ?></code>): <strong><?php echo $IntlDateFormatter->format(time()); ?></strong>
</p>
<?php
}
?>
<table>
<thead>
<tr>
<th><code>strftime()</code> format</th>
<th><code>strftime()</code> result</th>
<th><code>IntlDateFormatter()</code> pattern</th>
<th><code>IntlDateFormatter()</code> result</th>
</tr>
</thead>
<tbody>
<?php
foreach ($replaces as $strftime => $intl) {
$currentTime = time();
if (function_exists('strftime')) {
$strftimeResult = @strftime($strftime, $currentTime);
}
if (isset($IntlDateFormatter)) {
$IntlDateFormatter->setPattern($intl);
$intlDResult = $IntlDateFormatter->format($currentTime);
$matchcount = preg_match_all("|\{\{(.*)\}\}|U", $intlDResult, $matches, PREG_PATTERN_ORDER);
if((int)$matchcount > 0) {
foreach($matches[1] as $match) {
switch($match) {
case 'timestamp':
$intlDResult = str_replace('{{timestamp}}', $currentTime, $intlDResult);
break;
case 'century':
$IntlDateFormatter->setPattern('YYYY');
$intlDResult = str_replace('{{century}}', substr($IntlDateFormatter->format($currentTime), 0, 2), $intlDResult);
break;
}
}
}
}
if (isset($strftimeResult) && isset($intlDResult)) {
if ($strftimeResult != $intlDResult) {
$rowDif = true;
}
}
?>
<tr<?php if (isset($rowDif)) { echo ' class="different-result"'; } ?>>
<td><code><?php echo htmlspecialchars($strftime, ENT_QUOTES); ?></code></td>
<td><?php
if (isset($strftimeResult)) {
echo htmlspecialchars($strftimeResult, ENT_QUOTES);
}
?></td>
<td><code><?php echo htmlspecialchars($intl, ENT_QUOTES); ?></code></td>
<td><?php
if (isset($intlDResult)) {
echo htmlspecialchars($intlDResult, ENT_QUOTES);
}
?></td>
</tr>
<?php
unset($intlDResult, $rowDif, $strftimeResult);
}// endforeach;
unset($intl, $strftime);
?>
</tbody>
</table>
<p>Reference:
<a href="https://www.php.net/manual/en/function.strftime.php" target="strftome">strftime format.</a>
<a href="https://unicode-org.github.io/icu/userguide/format_parse/datetime/" target="ICU">ICU format</a>
</p>
</bodY>
</html>
<?php
unset($IntlDateFormatter, $replaces);
The script is a great helper! Thank you.
%b to M MM MMM MMMM is maybe not the best choice. In german MMM is "März" and not "Mär" or "Sept." (with dot) and not "Sep".
With L LL LLL LLLL its working like expected, LLL is only three letters for month.
And "E" should be "ccc"? In german "E" is not right. Its again with a dot.
See https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table