Negative DateInterval

前端 未结 7 2089
后悔当初
后悔当初 2020-12-17 09:59

I want to create a DatePeriod object with a negative DateInterval.

This creates a DatePeriod with the year increasing from today to 2016.

$this->S         


        
相关标签:
7条回答
  • 2020-12-17 10:33

    I had the same problem (and some other) and have created a class in order to be able to add and substact DateInterval. It supports also negative ISO8601 date interval ('P-2M1DT3H-56M21S' for example).

    Here the code (suggestions are welcome, I'm a very beginner in PHP):

    class MyDateInterval extends DateInterval
    {
        public function __construct($interval_spec)
        {
            $interval_spec = str_replace('+', '', $interval_spec);
            $pos = strpos($interval_spec, '-');
            if ($pos !== false) {
                // if at least 1 negative part
                $pattern = '/P(?P<ymd>(?P<years>-?\d+Y)?(?P<months>-?\d+M)?(?P<days>-?\d+D)?)?(?P<hms>T(?P<hours>-?\d+H)?(?P<minutes>-?\d+M)?(?P<seconds>-?\d+S)?)?/';
                $match = preg_match($pattern, $interval_spec, $matches);
                $group_names = array('years', 'months', 'days', 'hours', 'minutes', 'seconds');
                $negative_parts = array();
                $positive_parts = array();
                $all_negative = true;
                foreach ($matches as $k => $v) {
                    if (in_array($k, $group_names, true)) {
                        if (substr($v, 0, 1) == '-' and $v != '')
                            $negative_parts[$k] = $v;
                        if (substr($v, 0, 1) != '-' and $v != '')
                            $positive_parts[$k] = $v;
                    }
                }
                if (count($positive_parts) == 0) {
                    // only negative parts
                    $interval_spec = str_replace('-', '', $interval_spec);
                    parent::__construct($interval_spec);
                    $this->invert = 1;
                } else {
                    // the negative and positive parts are to be sliced
                    $negative_interval_spec = 'P';
                    $positive_interval_spec = 'P';
                    if ($matches['ymd'] != '') {
                        foreach ($matches as $k => $v) {
                            if (in_array($k, array_slice($group_names, 0, 3))) {
                                $negative_interval_spec .= $negative_parts[$k];
                                $positive_interval_spec .= $positive_parts[$k];
                            }
                        }
                    }
                    if ($matches['hms'] != '') {
                        $negative_ok = false;
                        $positive_ok = false;
                        foreach ($matches as $k => $v) {
                            if (in_array($k, array_slice($group_names, 3, 3))) {
                                if ($negative_parts[$k] != '' and ! $negative_ok) {
                                    $negative_interval_spec .= 'T';
                                    $negative_ok = true;
                                }
                                $negative_interval_spec .= $negative_parts[$k];
                                if ($positive_parts[$k] != '' and ! $positive_ok) {
                                    $positive_interval_spec .= 'T';
                                    $positive_ok = true;
                                }
                                $positive_interval_spec .= $positive_parts[$k];
                            }
                        }
                    }
                    $negative_interval_spec = str_replace('-', '', $negative_interval_spec);
                    $from = new DateTime('2013-01-01');
                    $to = new DateTime('2013-01-01');
                    $to = $to->add(new DateInterval($positive_interval_spec));
                    $to = $to->sub(new DateInterval($negative_interval_spec));
                    $diff = $from->diff($to);
                    parent::__construct($diff->format('P%yY%mM%dDT%hH%iM%sS'));
                    $this->invert = $diff->invert;
                }
            } else {
                // only positive parts
                parent::__construct($interval_spec);
            }
        }
    
        public static function fromDateInterval(DateInterval $from)
        {
            return new MyDateInterval($from->format('P%yY%mM%dDT%hH%iM%sS'));
        }
    
        public static function fromSeconds($from)
        {
            $invert = false;
            if ($from < 0)
                $invert = true;
            $from = abs($from);
    
            $years = floor($from / (365 * 30 * 24 * 60 * 60));
            $from = $from % (365 * 30 * 24 * 60 * 60);
    
            $months = floor($from / (30 * 24 * 60 * 60));
            $from = $from % (30 * 24 * 60 * 60);
    
            $days = floor($from / (24 * 60 * 60));
            $from = $from % (24 * 60 * 60);
    
            $hours = floor($from / (60 * 60));
            $from = $from % (60 * 60);
    
            $minutes = floor($from / 60);
            $seconds = floor($from % 60);
    
            if ($invert)
                return new MyDateInterval(sprintf("P-%dY-%dM-%dDT-%dH-%dM-%dS", $years, $months, $days, $hours, $minutes, $seconds));
            return new MyDateInterval(sprintf("P%dY%dM%dDT%dH%dM%dS", $years, $months, $days, $hours, $minutes, $seconds));
        }
    
        public function to_seconds()
        {
            $seconds = ($this->y * 365 * 24 * 60 * 60)
                        + ($this->m * 30 * 24 * 60 * 60)
                        + ($this->d * 24 * 60 * 60)
                        + ($this->h * 60 * 60)
                        + ($this->i * 60)
                        + $this->s;
            if ($this->invert == 1)
                return $seconds * -1;
            return $seconds;
        }
    
        public function to_hours()
        {
            $hours = round($this->to_seconds() / (60 * 60), 2);
            return $hours;
        }
    
        public function add($interval)
        {
            $sum = $this->to_seconds() + $interval->to_seconds();
            $new = MyDateInterval::fromSeconds($sum);
            foreach ($new as $k => $v) $this->$k = $v;
            return $this;
        }
    
        public function sub($interval)
        {
    
            $diff = $this->to_seconds() - $interval->to_seconds();
            $new = MyDateInterval::fromSeconds($diff);
            foreach ($new as $k => $v) $this->$k = $v;
            return $this;
        }
    
        public function recalculate()
        {
            $seconds = $this->to_seconds();
            $new = MyDateInterval::fromSeconds($seconds);
            foreach ($new as $k => $v) $this->$k = $v;
            return $this;
        }
    }
    
    0 讨论(0)
提交回复
热议问题