5

Say I have this object instance of DateInterval:

$obj=new DateInterval("P1Y12D");

Now I can do few pretty things with that $obj instance, but say I want to get out that "P1Y12D" string from the object, is it straight possible without the need to override the class?

I do not find a method for this, maybe you do.

Steve Konves
  • 2,648
  • 3
  • 25
  • 44
Niki Romagnoli
  • 1,406
  • 1
  • 21
  • 26

4 Answers4

9

Here's my version of @Yaslaw's code.

It is improved according to a current PHP community and PSR requirements. I've also tried to make it more readable and straight-forward.

/**
 * @param \DateInterval $interval
 *
 * @return string
 */
function dateIntervalToString(\DateInterval $interval) {

    // Reading all non-zero date parts.
    $date = array_filter(array(
        'Y' => $interval->y,
        'M' => $interval->m,
        'D' => $interval->d
    ));

    // Reading all non-zero time parts.
    $time = array_filter(array(
        'H' => $interval->h,
        'M' => $interval->i,
        'S' => $interval->s
    ));

    $specString = 'P';

    // Adding each part to the spec-string.
    foreach ($date as $key => $value) {
        $specString .= $value . $key;
    }
    if (count($time) > 0) {
        $specString .= 'T';
        foreach ($time as $key => $value) {
            $specString .= $value . $key;
        }
    }

    return $specString;
}

And here's the extension to the initial \DateInterval class:

class CustomDateInterval extends \DateInterval
{
    public function __toString()
    {
        // Reading all non-zero date parts.
        $date = array_filter(array(
            'Y' => $this->y,
            'M' => $this->m,
            'D' => $this->d
        ));

        // Reading all non-zero time parts.
        $time = array_filter(array(
            'H' => $this->h,
            'M' => $this->i,
            'S' => $this->s
        ));

        $specString = 'P';

        // Adding each part to the spec-string.
        foreach ($date as $key => $value) {
            $specString .= $value . $key;
        }
        if (count($time) > 0) {
            $specString .= 'T';
            foreach ($time as $key => $value) {
                $specString .= $value . $key;
            }
        }

        return $specString;
    }
}

It can be used like this:

$interval = new CustomDateInterval('P1Y2M3DT4H5M6S');

// Prints "P1Y2M3DT4H5M6S".
print $interval . PHP_EOL;

I hope it will help someone, cheers!

Community
  • 1
  • 1
Slava Fomin II
  • 26,865
  • 29
  • 124
  • 202
5

You can work with the format() function:

echo $obj->format('P%yY%mM%dDT%hH%iM%sS');

Or a better readable Version (without Zero-Values in the String):

function getSpecString(DateInterval $delta){
    //Read all date-parts there are not 0
    $date = array_filter(array('Y' => $delta->y, 'M' => $delta->m, 'D' => $delta->d));
    //Read all time-parts there are not 0
    $time = array_filter(array('H' => $delta->h, 'M' => $delta->i, 'S' => $delta->s));

    //Convert each part to spec-Strings
    foreach($date as $key => &$value) $value = $value.$key;
    foreach($time as $key => &$value) $value = $value.$key;

    //Create date spec-string
    $spec = 'P' . implode('', $date);            
    //add time spec-string
    if(count($time)>0) $spec .= 'T' . implode('', $time);
    return $spec;        
}   
Yaslaw
  • 51
  • 2
2

I'm not a C guru but in the source code of the constructor function the value does not seem to be stored at all:

/* {{{ proto DateInterval::__construct([string interval_spec])
   Creates new DateInterval object.
*/
PHP_METHOD(DateInterval, __construct)
{
    char *interval_string = NULL;
    int   interval_string_length;
    php_interval_obj *diobj;
    timelib_rel_time *reltime;
    zend_error_handling error_handling;

    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &interval_string, &interval_string_length) == SUCCESS) {
        if (date_interval_initialize(&reltime, interval_string, interval_string_length TSRMLS_CC) == SUCCESS) {
            diobj = zend_object_store_get_object(getThis() TSRMLS_CC);
            diobj->diff = reltime;
            diobj->initialized = 1;
        } else {
            ZVAL_NULL(getThis());
        }
    }
    zend_restore_error_handling(&error_handling TSRMLS_CC);
}
/* }}} */

Neither seem to do the date_interval_initialize() function:

static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, int format_length TSRMLS_DC)
{
    timelib_time     *b = NULL, *e = NULL;
    timelib_rel_time *p = NULL;
    int               r = 0;
    int               retval = 0;
    struct timelib_error_container *errors;

    timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);

    if (errors->error_count > 0) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format);
        retval = FAILURE;
    } else {
        if(p) {
            *rt = p;
            retval = SUCCESS;
        } else {
            if(b && e) {
                timelib_update_ts(b, NULL);
                timelib_update_ts(e, NULL);
                *rt = timelib_diff(b, e);
                retval = SUCCESS;
            } else {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse interval (%s)", format);
                retval = FAILURE;
            }
        }
    }
    timelib_error_container_dtor(errors);
    return retval;
}
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
0

There is no built in function to do that, you have to create one by yourself. If you do not want to override DateInterval, try to use static methods :

class DateIntervalUtils{
    public static function getSpecString(DateInterval $i){
         $stat ="P";
         foreach($i as $key=>$value){
            if($key !=="days"){
               if($key=="h"){
                  $stat.="T";
               }
               $stat.=$value.upper($key);
             }
          }
          return $stat;
      }
  }

If you want to add functions (comparison etc.) you can add to it.

artragis
  • 3,677
  • 1
  • 18
  • 30
  • I don't like this implementation. It's iterating over an object that can have any number of additional fields in the future besides "days" and it already has them in PHP 5.4. Static classes is also a bad idea. And there is no such function as `upper` in PHP. – Slava Fomin II Aug 18 '14 at 20:28