Issues

ZF-12141: Zend_Date::toString returns wrong date for some 'php' formats

Issue Type: Bug Created: 2012-04-09T13:56:27.000+0000 Last Updated: 2012-08-11T08:08:38.000+0000 Status: Open Fix version(s): Reporter: Vladyslav Mustafin (vladm) Assignee: Thomas Weidner (thomas) Tags: - Zend_Date

  • Zend_Locale
  • zend_date

Related issues: Attachments:

Description

Simple example:

<pre class="highlight">
$date = new Zend_Date();
echo $date->toString(DateTime::ATOM, 'php'); // 2012-04-09\UTC12:21:48+00:00

This code display 2012-04-09\UTC12:21:48+00:00 but 2012-04-09T12:21:48+00:00 is expected instead.

But example bellow works as expected:

<pre class="highlight">
$date = new Zend_Date();
echo $date->toString(Zend_Date::ATOM); // 2012-04-09T12:21:48+00:00

The basic problem is that Zend_Date::toString uses Zend_Locale_Format::convertPhpToIsoFormat for php format type that works incorrectly with formats that contain symbols are escaped with backslash (\T in example). Zend_Locale_Format::convertPhpToIsoFormat just splits format string to array and convert each symbol to ISO separately. But it should be more complicated. The simplest way to fix it is to get escaped symbol right after backslash and put it into single quotes so Zend_Date will parse it as comment.

Comments

Posted by Vladyslav Mustafin (vladm) on 2012-04-09T16:19:03.000+0000

It might looks like below:

<pre class="highlight">
    public static function convertPhpToIsoFormat($format)
    {
        if ($format === null) {
            return null;
        }

        $convert = array('d' => 'dd'  , 'D' => 'EE'  , 'j' => 'd'   , 'l' => 'EEEE', 'N' => 'eee' , 'S' => 'SS'  ,
                         'w' => 'e'   , 'z' => 'D'   , 'W' => 'ww'  , 'F' => 'MMMM', 'm' => 'MM'  , 'M' => 'MMM' ,
                         'n' => 'M'   , 't' => 'ddd' , 'L' => 'l'   , 'o' => 'YYYY', 'Y' => 'yyyy', 'y' => 'yy'  ,
                         'a' => 'a'   , 'A' => 'a'   , 'B' => 'B'   , 'g' => 'h'   , 'G' => 'H'   , 'h' => 'hh'  ,
                         'H' => 'HH'  , 'i' => 'mm'  , 's' => 'ss'  , 'e' => 'zzzz', 'I' => 'I'   , 'O' => 'Z'   ,
                         'P' => 'ZZZZ', 'T' => 'z'   , 'Z' => 'X'   , 'c' => 'yyyy-MM-ddTHH:mm:ssZZZZ',
                         'r' => 'r'   , 'U' => 'U');
        $values = str_split($format);
        $escaped = false;
        $temp = array();
        foreach ($values as $key => $value) {
            if ($v == '\\') {
                $escaped = true;
                continue;
            }
            if ($escaped) {
                $temp[] = str_replace("'''", "''", "'$value'");
                $escaped = false;
            } else if ($value == "'") {
                $temp[] = "''";
            } else if (isset($convert[$value]) === true) {
                $temp[] = $convert[$value];
            } else {
                $temp[] = $value;
            }
        }

        return join($values);
    }

Posted by Vladyslav Mustafin (vladm) on 2012-04-09T16:21:41.000+0000

Wrong return in my example, it should be: ``` Sorry for typo.

Posted by Patrick Heck (patrickheck) on 2012-05-02T17:18:34.000+0000

Hi Mustafin,

thanks for the code snippet. However I found two issues that still exist: 1. Inserting of consecutive escaped characters like "\a\t" result in "a't" 2. A masked backslash was not displayed

My suggestion is to improve it like this:

<pre class="highlight">
public static function convertPhpToIsoFormat($format)
    {
        if ($format === null) {
            return null;
        }

        $convert = array('d' => 'dd'  , 'D' => 'EE'  , 'j' => 'd'   , 'l' => 'EEEE', 'N' => 'eee' , 'S' => 'SS'  ,
                         'w' => 'e'   , 'z' => 'D'   , 'W' => 'ww'  , 'F' => 'MMMM', 'm' => 'MM'  , 'M' => 'MMM' ,
                         'n' => 'M'   , 't' => 'ddd' , 'L' => 'l'   , 'o' => 'YYYY', 'Y' => 'yyyy', 'y' => 'yy'  ,
                         'a' => 'a'   , 'A' => 'a'   , 'B' => 'B'   , 'g' => 'h'   , 'G' => 'H'   , 'h' => 'hh'  ,
                         'H' => 'HH'  , 'i' => 'mm'  , 's' => 'ss'  , 'e' => 'zzzz', 'I' => 'I'   , 'O' => 'Z'   ,
                         'P' => 'ZZZZ', 'T' => 'z'   , 'Z' => 'X'   , 'c' => 'yyyy-MM-ddTHH:mm:ssZZZZ',
                         'r' => 'r'   , 'U' => 'U');
        $values = str_split($format);
        $escaped = false;
        $lastescaped = false;
        $temp = array();
        foreach ($values as $key => $value) {
            if (!$escaped && $value == '\\') {
                $escaped = true;
                continue;
            }
            if ($escaped) {
                if (!$lastescaped) {
                    $temp[] = "'";
                    $lastescaped = true;
                } 
                $temp[] = $value;
                $escaped = false;
            } else { 
                if ($lastescaped) {
                    $temp[] = "'";
                    $lastescaped = false;
                }
                if ($value == "'") {
                    $temp[] = "''";
                } else if (isset($convert[$value]) === true) {
                    $temp[] = $convert[$value];
                } else {
                    $temp[] = $value;
                }
            }
        }
        return join($temp);
    }

Posted by Patrick Heck (patrickheck) on 2012-05-02T18:10:01.000+0000

I just realized that the string "\a\'\b" wouldn't work properly with this code. The result would be "a''b" instead of "a'b". To fix this the escaped sequence should only be terminated if the current character is != "'".

<pre class="highlight">
    public static function convertPhpToIsoFormat($format)
    {
        if ($format === null) {
            return null;
        }

        $convert = array('d' => 'dd'  , 'D' => 'EE'  , 'j' => 'd'   , 'l' => 'EEEE', 'N' => 'eee' , 'S' => 'SS'  ,
                         'w' => 'e'   , 'z' => 'D'   , 'W' => 'ww'  , 'F' => 'MMMM', 'm' => 'MM'  , 'M' => 'MMM' ,
                         'n' => 'M'   , 't' => 'ddd' , 'L' => 'l'   , 'o' => 'YYYY', 'Y' => 'yyyy', 'y' => 'yy'  ,
                         'a' => 'a'   , 'A' => 'a'   , 'B' => 'B'   , 'g' => 'h'   , 'G' => 'H'   , 'h' => 'hh'  ,
                         'H' => 'HH'  , 'i' => 'mm'  , 's' => 'ss'  , 'e' => 'zzzz', 'I' => 'I'   , 'O' => 'Z'   ,
                         'P' => 'ZZZZ', 'T' => 'z'   , 'Z' => 'X'   , 'c' => 'yyyy-MM-ddTHH:mm:ssZZZZ',
                         'r' => 'r'   , 'U' => 'U');
        $values = str_split($format);
        $escaped = false;
        $lastescaped = false;
        $temp = array();
        foreach ($values as $key => $value) {
            if (!$escaped && $value == '\\') {
                $escaped = true;
                continue;
            }
            if ($escaped) {
                if (!$lastescaped) {
                    $temp[] = "'";
                    $lastescaped = true;
                } 
                $temp[] = $value;
                $escaped = false;
            } else { 
                if ($value == "'") {
                    $temp[] = "''";
                } else {
                    if ($lastescaped) {
                        $temp[] = "'";
                        $lastescaped = false;
                    }
                    if (isset($convert[$value]) === true) {
                        $temp[] = $convert[$value];
                    } else {
                        $temp[] = $value;
                    }
                }
            }
        }
        echo join($temp) . "<br></br>";
        return join($temp);
    }

Posted by Vladyslav Mustafin (vladm) on 2012-08-09T08:55:34.000+0000

Hi Heck,

{quote} 2. A masked backslash was not displayed {quote} If you try to get an output of

<pre class="highlight">
you'll get a string



<pre class="literal">
For this output the valid iso date format is


You could check it with this code

So masked backslash in php date format should be converted to a single backslash for iso date format. No needs to escape it in iso, but with your last snippet it will.

<pre class="highlight">
will return

`

\ With small additional condition we can get the expected behaviour:

<pre class="highlight">
...
            if ($escaped) {
                if ($v != '\\' && !$lastescaped) { // added *$v != '\\' &&* condition here allows us to get correct backslashes for iso date format
                    $temp[] = "'";
                    $lastescaped = true;
                } 
                $temp[] = $value;
                $escaped = false;
            }
...

Posted by Vladyslav Mustafin (vladm) on 2012-08-11T08:08:38.000+0000

Yet another typo please use '$value' instead of '$v' in my examples. Sorry for that.

Have you found an issue?

See the Overview section for more details.

Copyright

© 2006-2016 by Zend, a Rogue Wave Company. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.

Contacts