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

发布于 2024-12-06 06:26:52 字数 318 浏览 1 评论 0原文

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

假设我的语言数量有限,因此我可以拥有某种数据结构来存储月份和缩写。我的问题是:“假设 strtotime 适用于英语字符串,那么创建一个函数的最佳选择是什么,该函数给出 X 语言中的日期字符串,返回格式为 DD-MM-YYY?”

I'm trying to create a function that could get a date in different formats and (languages) and transform it to DD-MM-YYYY. For example, this function could get 22 Fev 2011 (Portuguese) and also 22 Feb 2011 (English). For both it should return 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?"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

時窥 2024-12-13 06:26:52

日期/时间操作确实很痛苦。 :)

建议的解决方案

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]< 标识的 $localeInfo 子数组(当前正在测试的语言环境)内的项目之一的子字符串。 /code> 和 $keys[1]。为了使替换尽可能简洁,它在评估模式下使用辅助数组 $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(法国使用的时区)。根据您的要求,您可能还需要考虑时区差异。

Date/time manipulation sure is a pain. :)

Proposed solution

1. Locale data

I just paid a visit to Yii's svn repository and shamelessly copied these:

$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

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. 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]. To make the replacement as tersely as possible it uses an auxiliary array $flipped and preg_replace in evaluation mode; 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

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

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

4. What's with the third argument?

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

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.

5. Does that even work?

Sure it does.

6. And there are no caveats?

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. This means that a date in e.g. 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). Depending on your requirements, you might also want to account for the timezone difference.

猫七 2024-12-13 06:26:52

除了使用(即付费)翻译 API 服务之外,您还可以为语言工作日缩写工作日月份创建数据库表缩写月份。四个工作日/月份表将有一个 language_id 外键。您可以将英语等效项存储在行中,或者更好的是,将它们标准化。

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

Other than using (i.e. paying for) translation API services, you could create database tables for languages, weekdays, abbreviated weekdays, months, abbreviated months. The four weekday/month tables will have a language_id foreign key. 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. Then, if the appropriate rows are returned, substitute the english tokens into the date string and pass to the date function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文