正在格式化DateTime对象,并尊重Locale::getDefault()


Formatting DateTime object, respecting Locale::getDefault()

我有一个DateTime对象,我目前正在通过格式化它

$mytime->format("D d.m.Y")

这正是我需要的格式:

2012年3月3日星期二

唯一缺少的是正确的语言。我需要TueTuesday)的德语翻译,也就是DieDienstag)。

这为我提供了正确的区域设置

Locale::getDefault()

但我不知道如何告诉DateTime::format使用它。

难道没有办法做这样的事情吗:

$mytime->format("D d.m.Y", 'Locale::getDefault());

您可以使用Intl扩展名来格式化日期。它将根据所选的区域设置日期/时间的格式,或者您可以用IntlDateFormatter::setPattern()覆盖它。

对于您想要的输出格式,使用自定义模式的快速示例可能如下所示。

$dt = new DateTime;
$formatter = new IntlDateFormatter('de_DE', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT);
$formatter->setPattern('E d.M.yyyy');
echo $formatter->format($dt);

它输出以下内容(至少在今天)。

Di。4.6.2013

这是因为format不关注区域设置。您应该使用strftime

例如:

setlocale(LC_TIME, "de_DE"); //only necessary if the locale isn't already set
$formatted_time = strftime("%a %e.%l.%Y", $mytime->getTimestamp())

IntlDateFormatter是目前的方法(2023)。

<?php
$formatter = new IntlDateFormatter(
    $locale,  // the locale to use, e.g. 'en_GB'
    $dateFormat,  // how the date should be formatted, e.g. IntlDateFormatter::FULL
    $timeFormat,  // how the time should be formatted, e.g. IntlDateFormatter::FULL 
    'Europe/Berlin'  // the time should be returned in which timezone?
);
echo $formatter->format(time());

将给出不同的输出,具体取决于传递为$locale的内容以及日期和时间格式。我想添加一些样品以备将来参考。注意,CCD_ 10和CCD_ 11是可交换的。

Locale: en_US
Format for Date & Time:           Results in:
IntlDateFormatter::FULL           Friday, August 5, 2022 at 3:26:37 PM Central European Summer Time 
IntlDateFormatter::LONG           August 5, 2022 at 3:26:37 PM GMT+2 
IntlDateFormatter::MEDIUM         Aug 5, 2022, 3:26:37 PM 
IntlDateFormatter::SHORT          8/5/22, 3:26 PM 

Locale: en_GB
Format for Date & Time:           Results in:
IntlDateFormatter::FULL           Friday, 5 August 2022 at 15:26:37 Central European Summer Time 
IntlDateFormatter::LONG           5 August 2022 at 15:26:37 CEST 
IntlDateFormatter::MEDIUM         5 Aug 2022, 15:26:37 
IntlDateFormatter::SHORT          05/08/2022, 15:26 

Locale: de_DE
Format for Date & Time:           Results in:
IntlDateFormatter::FULL           Freitag, 5. August 2022 um 15:26:37 Mitteleuropäische Sommerzeit 
IntlDateFormatter::LONG           5. August 2022 um 15:26:37 MESZ 
IntlDateFormatter::MEDIUM         05.08.2022, 15:26:37 
IntlDateFormatter::SHORT          05.08.22, 15:26 

Locale: fr_FR
Format for Date & Time:           Results in:
IntlDateFormatter::FULL           vendredi 5 août 2022 à 15:26:37 heure d’été d’Europe centrale 
IntlDateFormatter::LONG           5 août 2022 à 15:26:37 UTC+2 
IntlDateFormatter::MEDIUM         5 août 2022 à 15:26:37 
IntlDateFormatter::SHORT          05/08/2022 15:26 

正如salath已经说过的,如果需要,您还可以使用$formatter->setPattern来进一步自定义输出。

虽然setlocale()是正确的答案,仍然有效,但现在已经过时了。

strftime自PHP 8.1.0起已弃用。强烈反对依赖此函数。

和提到的Intl扩展工作得很好,但并不总是方便。

使用日期和时间的最简单方法之一是使用Carbon2、CakePHP Chronos或类似的库。它为所有日期的操作、格式化和计算提供了一个单一的接口。如果你经常使用日期,我建议你使用Carbon,然后做一些类似的事情

$date = Carbon::now()->locale('fr_FR');
echo $date->isoFormat('dd DD.MM.YYYY');  

请注意,格式与date()不同。完整列表见Carbon文档,但提到的D d.m.Y可能类似于dd DD.MM.YYYY

如果您的项目接受第三方库,那么这确实是一条不错的道路。此外,如果您正在使用该框架,请检查,可能已经包含Carbon(或其包装)。

我做了一些这样的事情,因为除了strftime之外,似乎没有任何简单的在线解决方案,这是非常不推荐的!

我的解决方案用国际月份和日期名称扩展了DateTime::format(),不需要安装一堆模块,也不需要学习新的日期格式化方法等。

在包含下面提供的类之后,您可以按如下方式使用它。代替

$date = new DateTime("2010-01-01 1:23");
echo $date->format("l (D) Y-M-d (F)");

结果:Friday (Fri) 2010-Jan-01 (January)

您现在可以使用

$date = new DateTimeIntl("2010-01-01 1:23");
echo $date->format("l (D) Y-M-d (F)");

结果:vrijdag (vr) 2010-jan.-01 (januari)(荷兰语言环境)。

如果需要,可以动态更改$datetime->locale

$date = new DateTimeIntl("2010-01-01 1:23");
$date->locale = "it_IT" ;
echo $date->format("l (D) Y-M-d (F)");

结果:venerdì (ven) 2010-gen-01 (gennaio)

包括:

class DateTimePatternReplace {
    function __construct(public string $DateTimeCode, 
                         public string $IntDateFormatterCode,
                         public string $tempDateTimePlaceHolder) {}
}
trait addIntlDate {
    public string $locale="nl_NL" ;  // REPLACE BY YOUR FAVORITE LOCALE
    private function getIntResult(string $pattern) {
        if ( ! isset($this->formatter) || $this->formatter->getLocale(Locale::VALID_LOCALE) != $this->locale ) { 
            $this->formatter = new IntlDateFormatter($this->locale);
            $this->locale = $this->formatter->getLocale(Locale::VALID_LOCALE); // store the valid version of the locale
        }
        this->formatter->setPattern($pattern);
        return $this->formatter->format($this);
    }
    function format(string $pattern): string {
        // The third parameter can NOT contain normal latin letters, these are random, 
        // distinctive codes not likely to be in a date format string
        $replacePatterns = [/*weekdays*/new DateTimePatternReplace('l', 'EEEE', '[*ł*]'), 
                                        new DateTimePatternReplace('D', 'EEE', '[*Đ*]'),
                            /*month*/   new DateTimePatternReplace('F', 'MMMM', '[*ƒ*]'), 
                                        new DateTimePatternReplace('M', 'MMM', '[*μ*]'),
                                        // add new replacements here if needed
                           ] ;
        $codesFound=[] ;
        foreach($replacePatterns as $p) {
            if ( str_contains($pattern, $p->DateTimeCode)) {
                // replace codes not prepended by a backslash.
                // known bug: codes prepended by double backslashes will not be translated. Whatever.
                $pattern = preg_replace('/(?<!''')'.preg_quote($p->DateTimeCode)."/", $p->tempDateTimePlaceHolder, $pattern);
                $codesFound[] = $p ;
            }
        }
        $result = parent::format($pattern) ;
        foreach($codesFound as $p) {
            $code = $this->getIntResult($p->IntDateFormatterCode);
            $result = str_replace($p->tempDateTimePlaceHolder, $code, $result);
        }
        return $result ;
    }
} 
// you can remove this str_contains addition in PHP 8 or higher
if (!function_exists('str_contains')) {
    function str_contains($haystack, $needle) {
        return $needle !== '' && mb_strpos($haystack, $needle) !== false;
    }
}
// end str_contains addition

class DateTimeIntl extends DateTime {
    use addIntlDate;
}
class DateTimeImmutableIntl extends DateTimeImmutable {
    use addIntlDate;  
}

此代码扩展了DateTime和DateTimeImmutable,并使用区域设置扩展了它们的正常格式。因此,这使得一切都非常简单

如果需要,可以通过向数组中添加代码来添加要翻译的新模式:DateTime::format()语法中的格式模式、IntlDateFormatter::format语法中的相应格式模式,以及DateTime::format中要使用的占位符,该占位符不包含将被DateTime::format方法使用/替换的字母/代码/模式。请参阅当前四个不使用ASCII中低于128个字母的字母的代码作为示例。(他们使用波兰语、希腊语、荷兰语和斯洛伐克语字母只是为了好玩。)

在PHP 8.1中构建并测试。对于某些旧版本的PHP,您必须将第一个类更改为

class DateTimePatternReplace {
    public string $DateTimeCode;
    public string $IntDateFormatterCode;
    public string $tempDateTimePlaceHolder;
    function __construct(string $DateTimeCode, string $IntDateFormatterCode, string $tempDateTimePlaceHolder) {
         $this->DateTimeCode = $DateTimeCode;
         $this->IntDateFormatterCode = $IntDateFormatterCode;
         $this->tempDateTimePlaceHolder = $tempDateTimePlaceHolder;
    }
}

这就是我结合DateTimestrftime()功能解决问题的方法。

第一个允许我们使用奇怪的日期格式来管理字符串,例如";Ymd";(从日期选择器存储在数据库中)。第二个允许我们用某种语言翻译日期字符串。

例如,我们从一个值"0"开始;20201129";,我们希望以意大利语可读的日期结尾,日期和月份的名称,还有第一个大写字母:"Domenica 2020年11月29日";。

// for example we start from a variable like this
$yyyymmdd = '20201129';
// set the local time to italian
date_default_timezone_set('Europe/Rome');
setlocale(LC_ALL, 'it_IT.utf8');
// convert the variable $yyyymmdd to a real date with DateTime
$truedate = DateTime::createFromFormat('Ymd', $yyyymmdd);
// check if the result is a date (true) else do nothing
if($truedate){
  // output the date using strftime
  // note the value passed using format->('U'), it is a conversion to timestamp
  echo ucfirst(strftime('%A %d %B %Y', $truedate->format('U')));
}
// final result: Domenica 29 novembre 2020