我想做的事情很简单:我想用中文(或俄语)打印日期(时间戳)。
对于我正在使用的所有语言
setlocale(LC_TIME, 'hu_HU.utf8', 'hu_HU.UTF-8', 'hu_HU', 'hr');
$date = strftime('%a %e %b %Y, %H:%M');
$date = utf8_encode($date);
即使没有utf8_encode()
,这也会返回UTF-8字符串。一切都好。现在,当我对'zh_CN.utf8'
语言环境(或'zh_CN.UTF-8'
,'zh_CN'
或'zh'
做完全相同的操作时,这不会返回正确的日期。有或没有utf8_encode()
都会返回
'2018å¹?mæ?#dæ?'
我不会说中文,但这显然是错误的。我发现它应该返回类似'年'
的内容。该字符具有UTF-8十六进制编码E5 B9 B4
,但是当我查看返回的String时,十六进制值错误。 (在2018年之后)有C3 A5 C2 B9 3F 6D C3 A6 ...
。
当我使用mb_detect_encoding()
检查返回的String的编码时,它总是返回UTF-8。我期待如此,因为我使用的是'zh_CN.utf8'
语言环境,该语言环境将编码设置为UTF-8。
环顾了一段时间后,我遇到了this answer of Peter。他建议在'%Y年%m月%e日'
函数中使用格式strftime()
。当我使用它时,我得到的结果和以前一样。
这使我想到编码错误。但这是真的吗?编码错误吗?如何将结果转换为正确的编码?
我在俄语上遇到的问题更少。
答案 0 :(得分:0)
我花了几个小时才找到正确的编码。 strftime()
是不传递UTF-8
字符串。有关详细信息,请查看此答案的底部。我最后得到了一个formatTime()
函数,该函数以正确的编码(对我来说UTF-8
)为我提供了正确的时间。
function formatTime($format, $language = null, $timestamp = null){
switch($language){
case 'chinese':
$locale = setlocale(LC_TIME, 'zh_CN.utf8', 'zh_CN.UTF-8', 'zh_CN', 'zh');
break;
case 'hungarian':
$locale = setlocale(LC_TIME, 'hu_HU.utf8', 'hu_HU.UTF-8', 'hu_HU', 'hr');
break;
case 'russian':
$locale = setlocale(LC_TIME, 'ru_RU.utf8', 'ru_RU.UTF-8', 'ru_RU', 'ru');
break;
case 'german':
$locale = setlocale(LC_TIME, 'de_DE.utf8', 'de_DE.UTF-8', 'de_DE', 'de');
break;
case 'french':
$locale = setlocale(LC_TIME, 'fr_FR.utf8', 'fr_FR.UTF-8', 'fr_FR', 'fr');
break;
case 'polish':
$locale = setlocale(LC_TIME, 'pl_PL.utf8', 'pl_PL.UTF-8', 'pl_PL', 'pl');
break;
case 'turkish':
$locale = setlocale(LC_TIME, 'tr_TR.utf8', 'tr_TR.UTF-8', 'tr_TR', 'tr');
break;
case 'english':
$locale = setlocale(LC_TIME, 'en_GB.utf8', 'en_GB.UTF-8', 'en_GB', 'en');
break;
// ...
default: break;
}
if(!is_numeric($timestamp)){
$datetime = strftime($format);
}
else{
$datetime = strftime($format, $timestamp);
}
$current_locale = strtolower(setlocale(LC_TIME, 0));
if(($pos = strpos("utf", $current_locale)) === false || strpos("8", $current_locale, $pos) === false){
// UTF-8 locale is not used, the encodings are found out with the code shown below
$locale_default_encodings = array(
"german" => "ISO-8859-1",
"french" => "ISO-8859-1",
"polish" => "ISO-8859-2",
"turkish" => "ISO-8859-9",
// Testing hungarian results in "Windows-1252", but php.net recommends to
// use ISO-8859-2, in fact Windows-1252 is based on ISO-8859-2 so it should
// (hopefully) work with both (*)
"hungarian" => "ISO-8859-2",
"chinese" => "CP936",
"russian" => "KOI8-R"
);
$target_encoding = mb_internal_encoding(); // or "UTF-8" or whatever
if(isset($locale_default_encodings[$language])){
$datetime = mb_convert_encoding(
$datetime,
$target_encoding,
$locale_default_encodings[$language]
);
}
else{
// try to avoid this case
$datetime = mb_convert_encoding($datetime, $target_encoding);
}
}
setlocale(LC_TIME, $locale);
return $datetime;
}
(*):http://php.net/manual/de/function.strftime.php#94399
我检查了特定语言的strftime("%B")
结果。这是完整的月份名称。我检查了翻译的语言,然后查看UTF-8
的十六进制值来查找翻译的不同字母。
现在,我正在遍历php支持的所有编码。我将strftime()
给出的结果从当前的迭代编码转换为UTF-8
。现在,我可以将转换为strftime()
的{{1}}的结果与手动转换的十六进制值(也就是UTF-8
的十六进制值)进行比较。如果它们匹配,则UTF-8
的结果具有当前插入编码的编码。
我选择十六进制值是因为它们在定义上是相同的,并且不依赖于内部编码,因为它们是ASCII字符串(甚至是php中的数字)。
这给了我以下输出,代码发布在下面:
strftime()
请注意,此html以UTF-8编码。 <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Detecting the font encoding of <code>strftime()</code>
</h1>
<h2>hungarian</h2>
<p>
<code>strftime()</code> for March for language hungarian. Expected hex: <code>6fc5be756a616b</code>, converted expected hex to string: <code>ožujak</code>
</p>
<table>
<tr>
<td>initial return value</td>
<td>oߵjak</td>
<td>6f9e756a616b</td>
</tr>
<tr>
<td colspan='3'>Encodings that deliver the correct result:</td>
</tr>
<tr style='background: green;'>
<td>Windows-1252</td>
<td>ožujak</td>
<td>6fc5be756a616b</td>
</tr>
</table>
<h2>chinese</h2>
<p>
<code>strftime()</code> for December for language chinese. Expected hex: <code>e58d81e4ba8ce69c88</code>, converted expected hex to string: <code>十二月</code>
</p>
<table>
<tr>
<td>initial return value</td>
<td>ʮՂ</td>
<td>caaeb6fed4c2</td>
</tr>
<tr>
<td colspan='3'>Encodings that deliver the correct result:</td>
</tr>
<tr style='background: green;'>
<td>EUC-CN</td>
<td>十二月</td>
<td>e58d81e4ba8ce69c88</td>
</tr>
<tr style='background: green;'>
<td>CP936</td>
<td>十二月</td>
<td>e58d81e4ba8ce69c88</td>
</tr>
<tr style='background: green;'>
<td>GB18030</td>
<td>十二月</td>
<td>e58d81e4ba8ce69c88</td>
</tr>
</table>
<h2>russian</h2>
<p>
<code>strftime()</code> for December for language russian. Expected hex: <code>d0b4d095d099d0aed090d09fd0ad</code>, converted expected hex to string: <code>дЕЙЮАПЭ</code>
</p>
<table>
<tr>
<td>initial return value</td>
<td>ť롡td>
<td>c4e5eae0e1f0fc</td>
</tr>
<tr>
<td colspan='3'>Encodings that deliver the correct result:</td>
</tr>
<tr style='background: green;'>
<td>KOI8-R</td>
<td>дЕЙЮАПЭ</td>
<td>d0b4d095d099d0aed090d09fd0ad</td>
</tr>
<tr style='background: green;'>
<td>KOI8-U</td>
<td>дЕЙЮАПЭ</td>
<td>d0b4d095d099d0aed090d09fd0ad</td>
</tr>
</table>
</body>
</html>
函数给出的结果仍然是错误的!注释中指出,这与浏览器或编辑器的编码无关。
strftime()