<?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.
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