简体   繁体   English

将任何日期(从任何地方)转换为DD-MM-YYYY

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

I'm trying to create a function that could get a date in different formats and (languages) and transform it to DD-MM-YYYY . 我正在尝试创建一个函数,该函数可以获取具有不同格式和(语言)的日期并将其转换为DD-MM-YYYY For example, this function could get 22 Fev 2011 (Portuguese) and also 22 Feb 2011 (English). 例如,此功能可能会获得22 Fev 2011 (葡萄牙语)和22 Feb 2011 (英语)的信息。 For both it should return 22-02-2011 . 对于两者,都应返回22-02-2011

Let's assume that I have a limited amount of languages, so I can have some kind of data structure that carries the months and shortens of those. 假设我的语言数量有限,所以我可以使用某种数据结构来承载这些语言的几个月和几个月的时间。 My question is: "Assuming that strtotime works for English strings, what is my best choice for creating a function that given a string of a date in a language X, returns a date with the format DD-MM-YYY ?" 我的问题是:“假设strtotime适用于英语字符串,那么对于创建一个以语言X给出给定日期字符串,返回格式为DD-MM-YYY的日期的函数,我的最佳选择是什么?”

Other than using (ie paying for) translation API services, you could create database tables for languages , weekdays , abbreviated weekdays , months , abbreviated months . 除了使用(即付费)翻译API服务之外,您还可以为languagesweekdaysabbreviated weekdaysmonthsabbreviated months创建数据库表。 The four weekday/month tables will have a language_id foreign key. 四个工作日/月表将具有language_id外键。 You can store the english equivalents in the rows or, better, normalize those out. 您可以将英语等效项存储在行中,或者更好地将它们标准化。

Then have the function grab the alpha tokens from the date string ( preg_match ) and query the tables for rows that match the token and language. 然后让该函数从日期字符串( preg_match )中获取字母标记,并在表中查询与标记和语言匹配的行。 Then, if the appropriate rows are returned, substitute the english tokens into the date string and pass to the date function. 然后,如果返回了适当的行,则将英语标记替换为日期字符串,然后传递给date函数。

Date/time manipulation sure is a pain. 日期/时间操作肯定很痛苦。 :) :)

Proposed solution 拟议的解决方案

1. Locale data 1.语言环境数据

I just paid a visit to Yii's svn repository and shamelessly copied these: 我刚刚访问了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. Brute forcing the problem 2.强行解决问题

Assuming that your app isn't spending all its time converting human-readable dates, speed shouldn't really matter. 假设您的应用没有花所有的时间来转换人类可读的日期,那么速度实际上就没有关系。 Therefore I went for a shortish solution with good extensibility, at the cost of not trying to optimize and being slightly cryptic. 因此,我选择了一种具有良好扩展性的矮型解决方案,但这样做的代价是不尝试进行优化,并且有些晦涩难懂。

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;
}

That inner foreach does look smelly, but I think it's acceptable. 内在的foreach看起来确实很臭,但是我认为这是可以接受的。 What it does is try to replace any substring that looks like one of the items inside the sub-array of $localeInfo (current locale being tested) identified by the indexes $keys[0] and $keys[1] . 它的作用是尝试替换任何看起来像由索引$keys[0]$keys[1]标识的$localeInfo (正在测试的当前语言环境)子数组内的项之一的子字符串。 To make the replacement as tersely as possible it uses an auxiliary array $flipped and preg_replace in evaluation mode; 为了使替换尽可能简洁,它在评估模式下使用辅助数组$flippedpreg_replace if you don't like this kind of code, it can certainly be replaced with a more familiar loop-based approach. 如果您不喜欢这种代码,那么可以肯定地将其替换为更熟悉的基于循环的方法。

3. How to use it 3.使用方法

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

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

4. What's with the third argument? 4.第三个论点是什么?

Well, it would be nice if the replacement worked in a case-insensitive manner. 好吧,如果替换以不区分大小写的方式起作用,那就太好了。 The problem with this is that you can't blindly use strtolower and the /i regex modifier, because at least the former will not work unless you change the LC_TEXT locale which is a painful requirement and not reliable to boot (locale names are OS-dependent). 这样做的问题是,您不能盲目地使用strtolower/i regex修饰符,因为至少前者将无法工作,除非您更改LC_TEXT语言环境,这是一个痛苦的要求,并且引导不可靠(语言环境名称为OS-取决于)。 And the shotgun argument is that even if everything goes well that far, you still need to have your locale data saved in an ANSI encoding (which means you can't save them all in the same file). Shotgun的论点是,即使一切进展顺利,您仍然需要将区域设置数据保存为ANSI编码(这意味着您无法将它们全部保存在同一文件中)。

Therefore the caller has the option of supplying their own normalization function if needed; 因此,如果需要,调用者可以选择提供自己的规范化功能。 mb_strtolower would be an excellent choice here if your data is saved in UTF-8. 如果您的数据保存在UTF-8中, mb_strtolower将是一个很好的选择。

5. Does that even work? 5.那还行吗?

Sure it does . 当然可以

6. And there are no caveats? 6.没有警告吗?

Well, apart from the normalization function there is one more I can think of: strtotime internally uses the local timezone to convert the parsed date to a timestamp. 好吧,除了规范化功能外,我还能想到的是: strtotime内部使用本地时区将解析的日期转换为时间戳。 This means that a date in eg French will be parsed correctly given the appropriate locale data, but the timestamp will be produced for the local time zone, not CET/CEST (the timezone France uses). 这意味着在给定适当的语言环境数据的情况下,可以正确解析例如法语的日期,但是时间戳将针对本地时区而不是CET / CEST(法国使用的时区)生成。 Depending on your requirements, you might also want to account for the timezone difference. 根据您的要求,您可能还需要考虑时区差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM