php 负日期间隔

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6350778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 00:07:06  来源:igfitidea点击:

Negative DateInterval

php

提问by user798584

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

我想创建一个具有负 DateInterval 的 DatePeriod 对象。

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

这将创建一个 DatePeriod,年份从今天增加到 2016 年。

$this->Set('dates', new DatePeriod(new DateTime(), new DateInterval('P1Y'), new DateTime('2016-06-06')));

I want to start at 2016, and using a negative DateInterval move towards todays year

我想从 2016 年开始,并使用负 DateInterval 移动到今天的年份

Something like this might illustrate my desire

这样的事情可能说明了我的愿望

$this->Set('dates', new DatePeriod(new DateTime('2016-06-06'), new DateInterval('-P1Y'), new DateTime()));

I just can't find any extended info on either DatePeriod or DateInterval on how to do this. All i find is that DateInterval can be inverted.

我只是找不到有关如何执行此操作的 DatePeriod 或 DateInterval 的任何扩展信息。我发现的只是 DateInterval 可以反转。

回答by hijarian

According to comment by kevinpeno at 17-Mar-2011 07:47 on php.net's page about DateInterval::__construct(), you cannot directly create negative DateIntervals through the constructor:

根据kevinpeno 在 17-Mar-2011 07:47 在 php.net 页面上关于 DateInterval::__construct() 的评论,您不能通过构造函数直接创建负 DateIntervals:

new DateInterval('-P1Y'); // Exception "Unknown or bad format (-P1Y)"

Instead of this you are required to create a positive interval and explicitly set it's invertproperty to 1:

取而代之的是,您需要创建一个正间隔并将其invert属性显式设置为1

$di = new DateInterval('P1Y');
$di->invert = 1; // Proper negative date interval

Just checked the above code by myself, it's working exactly in this way.

刚刚自己检查了上面的代码,它就是这样工作的。

回答by Andrew Curioso

This took a little digging. The only way I was able to get a negative DateIntervalwas by doing this:

这需要一点挖掘。我能够得到否定的唯一方法DateInterval是这样做:

$interval = DateInterval::createFromDateString('-1 day');

However, there is a catch. DatePeriodseems to not work for negative intervals. If you set the start date to be before the end date then it doesn't contain any dates at all and if you flip so that the start date is after the end date then it looks backwards indefinitely.

但是,有一个问题。DatePeriod似乎不适用于负间隔。如果您将开始日期设置为结束日期之前,那么它根本不包含任何日期,如果您翻转开始日期是在结束日期之后,那么它会无限期地向后看。

You're probably going to have to restructure your code to loop through the dates using DateTime::subwith a positive DateIntervalor DateTime::addwith the negative one.

您可能将不得不重新构建代码以使用DateTime::sub正数DateIntervalDateTime::add负数循环遍历日期。

回答by Ramadhan

You can used sub http://php.net/manual/en/datetime.sub.php

您可以使用 sub http://php.net/manual/en/datetime.sub.php

Here is example

这是例子

$startDate = new \DateTime('2018-01-08 13:54:06');
$startDate->sub(new \DateInterval('P1D'));

回答by KingCrunch

I tried it myself and it isn't possible with DatePeriodalone, but I think that makes sense: It just reflects the periods, that usually doesn't have any specific order and therefore cannot get reordered (it can be treated as a set).

我自己尝试过,DatePeriod单独使用是不可能的,但我认为这是有道理的:它只是反映了时间段,通常没有任何特定顺序,因此无法重新排序(可以将其视为一组)。

The only way to retrieve the dates and sort it in reversed order, as far as I can see, is something like this

据我所知,检索日期并按相反顺序对其进行排序的唯一方法是这样的

$result = array();
forech ($dateperiod as $date) {
  array_push ($result, $data);
}

Update

更新

$date = new DateTime('2016-06-06');
$i = new DateInterval('P1Y');
$now = new DateTime;
while ($date >= $now) {
  echo $date->format('c') . PHP_EOL;
  $date = $date->sub($i);
}

回答by Michel

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

我遇到了同样的问题(和其他一些问题)并创建了一个类以便能够添加和减少 DateInterval。它还支持负 ISO8601 日期间隔(例如“P-2M1DT3H-56M21S”)。

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

这里的代码(欢迎提出建议,我是 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;
    }
}

回答by khany

This extract worked for me:

这个摘录对我有用:

    $iDate = $endDate;
    while($iDate >= $startDate) {
        $dates[] = new DateTime($iDate->format('Y-m-d'));
        $iDate->sub(new DateInterval("P1D"));
    }

回答by Antony Nguyen

EDIT: note that this was inspired by khany's code above.

编辑:请注意,这是受到上面 khany 代码的启发。

Here's a fully working script for my use-case, which is to display year+month strings from the current month going back N number of months. It should work with days or years as intervals and is tested with PHP version 5.3.3.

这是我的用例的完整工作脚本,它显示从当前月份开始回溯 N 个月的年份 + 月份字符串。它应该以天或年为间隔工作,并使用 PHP 5.3.3 版进行测试。

<?php

date_default_timezone_set('America/Los_Angeles');

$monthsBack=16;

$monthDateList = array();
$previousMonthDate = new DateTime();
for($monthInterval = 0; $monthInterval < $monthsBack; $monthInterval++) {
    array_push($monthDateList, $previousMonthDate->format('Ym'));
    $previousMonthDate->sub(new DateInterval("P1M"));
}

print_r($monthDateList) . "\n";

?>

The output is:

输出是:

Array
(
    [0] => 201705
    [1] => 201704
    [2] => 201703
    [3] => 201702
    [4] => 201701
    [5] => 201612
    [6] => 201611
    [7] => 201610
    [8] => 201609
    [9] => 201608
    [10] => 201607
    [11] => 201606
    [12] => 201605
    [13] => 201604
    [14] => 201603
    [15] => 201602
)