将任意日期(从任何位置)转换为DD-MM-YYYY


Transform any date (from anywhere) to DD-MM-YYYY

我试图创建一个函数,可以获得不同格式和(语言)的日期,并将其转换为DD-MM-YYYY。例如,这个函数可以得到22 Fev 2011(葡萄牙语)和22 Feb 2011(英语)。对于两者,它都应该返回22-02-2011

让我们假设我有有限的语言,所以我可以有某种数据结构来携带月份和这些月份的缩写。我的问题是:"假设strtotime适用于英语字符串,那么创建一个函数的最佳选择是什么?该函数给定X语言中的日期字符串,返回格式为DD-MM-YYY的日期?"

日期/时间操作确实很麻烦。:)

解决方案

1。语言环境数据

我刚刚访问了Yii的svn存储库,无耻地复制了这些:

$locales = array(
    'pt' => array(
        'monthNames' => array(
            'wide' => array (
              1 => 'janeiro',
              2 => 'fevereiro',
              3 => 'marco',
              4 => 'abril',
              5 => 'maio',
              6 => 'junho',
              7 => 'julho',
              8 => 'agosto',
              9 => 'setembro',
              10 => 'outubro',
              11 => 'novembro',
              12 => 'dezembro',
            ),
            'abbreviated' => array(
              1 => 'jan',
              2 => 'fev',
              3 => 'mar',
              4 => 'abr',
              5 => 'mai',
              6 => 'jun',
              7 => 'jul',
              8 => 'ago',
              9 => 'set',
              10 => 'out',
              11 => 'nov',
              12 => 'dez',
            ),
        ),
        'weekDayNames' => array(
            'wide' => array (
              0 => 'domingo',
              1 => 'segunda-feira',
              2 => 'terca-feira',
              3 => 'quarta-feira',
              4 => 'quinta-feira',
              5 => 'sexta-feira',
              6 => 'sabado',
            ),
            'abbreviated' => array(
              0 => 'dom',
              1 => 'seg',
              2 => 'ter',
              3 => 'qua',
              4 => 'qui',
              5 => 'sex',
              6 => 'sab',
            ),
        ),
     ),
    'en' => array(
        'monthNames' => array(
            'wide' => array (
              1 => 'January',
              2 => 'February',
              3 => 'March',
              4 => 'April',
              5 => 'May',
              6 => 'June',
              7 => 'July',
              8 => 'August',
              9 => 'September',
              10 => 'October',
              11 => 'November',
              12 => 'December',
            ),
            'abbreviated' => array(
              1 => 'Jan',
              2 => 'Feb',
              3 => 'Mar',
              4 => 'Apr',
              5 => 'May',
              6 => 'Jun',
              7 => 'Jul',
              8 => 'Aug',
              9 => 'Sep',
              10 => 'Oct',
              11 => 'Nov',
              12 => 'Dec',
            ),
        ),
        'weekDayNames' => array(
            'wide' => array (
              0 => 'Sunday',
              1 => 'Monday',
              2 => 'Tuesday',
              3 => 'Wednesday',
              4 => 'Thursday',
              5 => 'Friday',
              6 => 'Saturday',
            ),
            'abbreviated' => array(
              0 => 'Sun',
              1 => 'Mon',
              2 => 'Tue',
              3 => 'Wed',
              4 => 'Thu',
              5 => 'Fri',
              6 => 'Sat',
            ),
        ),
    ),
);

2。暴力破解问题

假设你的应用程序没有把所有的时间都花在转换人类可读的日期上,那么速度应该不是真正的问题。因此,我选择了一个具有良好可扩展性的短期解决方案,代价是不尝试优化和稍微模糊。

function strtotimeIntl($timeString, $locales, $normalizeCallback = 'strtolower') {
    // STEP 1 -- TRY ENGLISH
    $ts = strtotime($timeString);
    if ($ts !== false) {
        return $ts;
    }
    // STEP 2 -- BRUTE FORCE
    $english = $locales['en'];
    foreach($locales as $code => $localeInfo) {
        if($code == 'en') {
            continue; // don't try english again
        }
        $subject = $normalizeCallback($timeString); // reset
        // These reflect the structure of $localeInfo
        $replacementKeys = array(
            array('monthNames', 'wide'),
            array('monthNames', 'abbreviated'),
            array('weekDayNames', 'wide'),
            array('weekDayNames', 'abbreviated'),
        );
        // Replace everything present in the string with english equivalents
        foreach($replacementKeys as $keys) {
            $map = array_map($normalizeCallback, $localeInfo[$keys[0]][$keys[1]]);
            $flipped = array_flip($map);
            $subject = preg_replace('/'b('.implode('|', $map).')'b/e',
                                   '$english[$keys[0]][$keys[1]][$flipped[''$1'']]',
                                   $subject);
        }
        // Does this look right?
        $ts = strtotime($subject);
        if ($ts !== false) {
            return $ts;
        }
    }
    // Give up, it's not like we didn't try
    return false;
}

里面的foreach看起来确实很臭,但我认为它是可以接受的。它所做的是尝试替换由索引$keys[0]$keys[1]标识的$localeInfo(正在测试的当前语言环境)子数组中任何看起来像项之一的子字符串。为了使替换尽可能简洁,它在求值模式下使用辅助数组$flippedpreg_replace;如果您不喜欢这种代码,当然可以用更熟悉的基于循环的方法来替换它。

3。如何使用

$timeString = '22 Feb 2011';
echo strtotimeIntl($timeString, $locales);
$timeString = '22 Fev 2011';
echo strtotimeIntl($timeString, $locales);

4。第三个论点是什么?

嗯,如果替换能以不区分大小写的方式工作就好了。这样做的问题是,您不能盲目地使用strtolower/i正则表达式修饰符,因为至少前者将无法工作,除非您更改LC_TEXT区域设置,这是一个痛苦的要求,并且不可靠的引导(区域设置名称依赖于操作系统)。霰弹枪的论点是,即使到目前为止一切都很顺利,您仍然需要以ANSI编码保存您的区域设置数据(这意味着您不能将它们全部保存在同一个文件中)。

因此,如果需要,调用者可以选择提供自己的规范化函数;如果您的数据以UTF-8保存,mb_strtolower将是一个很好的选择。

5。这有用吗?

6。没有任何警告吗?

嗯,除了标准化函数,我还能想到一个:strtotime内部使用本地时区将解析后的日期转换为时间戳。这意味着在给定适当的区域设置数据的情况下,将正确解析法语格式的日期,但是将为本地时区生成时间戳,而不是为CET/CEST(法国使用的时区)生成时间戳。根据您的需求,您可能还需要考虑时区差异。

除了使用(即付费)翻译API服务之外,您还可以为languages, weekdays, abbreviated weekdays, months, abbreviated months创建数据库表。四个工作日/月表将有一个language_id外键。您可以在行中存储英文对等物,或者更好地将它们规范化。

然后让函数从日期字符串(preg_match)中获取alpha标记,并查询表中与标记和语言匹配的行。然后,如果返回了适当的行,则将英文标记替换为日期字符串并传递给date函数。