DateTime::add
date_add
(PHP 5 >= 5.3.0, PHP 7)
DateTime::add -- date_add — Добавляет заданное количество дней, месяцев, лет, часов, минут и секунд к объекту DateTime
Описание
Объектно-ориентированный стиль
Процедурный стиль
Прибавляет заданный объект DateInterval к объекту DateTime.
Список параметров
-
object
-
Только для процедурного стиля: Объект DateTime, возвращаемый date_create(). Функция изменяет этот объект.
-
interval
-
Объект класса DateInterval
Возвращаемые значения
Возвращает объект DateTime для применения в цепи методов или FALSE
в случае возникновения ошибки.
Примеры
Пример #1 Пример использования DateTime::add()
Объектно-ориентированный стиль
<?php
$date = new DateTime('2000-01-01');
$date->add(new DateInterval('P10D'));
echo $date->format('Y-m-d') . "\n";
?>
Процедурный стиль
<?php
$date = date_create('2000-01-01');
date_add($date, date_interval_create_from_date_string('10 days'));
echo date_format($date, 'Y-m-d');
?>
Результат выполнения данных примеров:
2000-01-11
Пример #2 Другие примеры с DateTime::add()
<?php
$date = new DateTime('2000-01-01');
$date->add(new DateInterval('PT10H30S'));
echo $date->format('Y-m-d H:i:s') . "\n";
$date = new DateTime('2000-01-01');
$date->add(new DateInterval('P7Y5M4DT4H3M2S'));
echo $date->format('Y-m-d H:i:s') . "\n";
?>
Результат выполнения данного примера:
2000-01-01 10:00:30 2007-06-05 04:03:02
Пример #3 Будьте внимательны при добавлении месяцев
<?php
$date = new DateTime('2000-12-31');
$interval = new DateInterval('P1M');
$date->add($interval);
echo $date->format('Y-m-d') . "\n";
$date->add($interval);
echo $date->format('Y-m-d') . "\n";
?>
Результат выполнения данного примера:
2001-01-31 2001-03-03
Примечания
При работе с PHP 5.2 в качестве альтернативы можно воспользоваться функцией DateTime::modify().
Смотрите также
- DateTime::sub() - Вычитает заданное количество дней, месяцев, лет, часов, минут и секунд из времени объекта DateTime
- DateTime::diff() - Возвращает разницу между двумя DateTime объектами
- DateTime::modify() - Изменение временной метки
- PHP Руководство
- Функции по категориям
- Индекс функций
- Справочник функций
- Расширения для работы с датой и временем
- Дата и Время
- DateTime::add
- Функция DateTime::__construct() - Конструктор класса DateTime
- Функция DateTime::createFromFormat() - Создает и возвращает экземпляр класса DateTime, соответствующий заданному формату
- Функция DateTime::getLastErrors() - Возвращает предупреждения и ошибки
- Функция DateTime::modify() - Изменение временной метки
- Функция DateTime::__set_state() - Обработчик __set_state
- Функция DateTime::setDate() - Установка даты
- Функция DateTime::setISODate() - Установка ISO даты
- Функция DateTime::setTime() - Установка времени
- Функция DateTime::setTimestamp() - Устанавливает дату и время, основываясь на метке времени Unix
- Функция DateTime::setTimezone() - Установка временной зоны для объекта класса DateTime
- DateTime::sub
Коментарии
adding 15 min to a datetime
<?php
$initDate = new DateTime("2010/08/24");
$initDate->add(new DateInterval("PT15M"));
echo $initDate->format("Y/m/d m:i:s");//result: 2010/08/24 08:15:00
?>
period:
P1Y2M3DT1H2M3S
period time:
PT1H2M3S
Note that the add() and sub() methods will modify the value of the object you're calling the method on! This is very untypical for a method that returns a value of its own type. You could misunderstand it that the method would return a new instance with the modified value, but in fact it modifies itself! This is undocumented here. (Only a side note on procedural style mentions it, but it obviously does not apply to object oriented style.)
If you need add() and sub() that don't modify object values, you can create new methods like this:
<?php
class DateTimeEnhanced extends DateTime {
public function returnAdd(DateInterval $interval)
{
$dt = clone $this;
$dt->add($interval);
return $dt;
}
public function returnSub(DateInterval $interval)
{
$dt = clone $this;
$dt->sub($interval);
return $dt;
}
}
$interval = DateInterval::createfromdatestring('+1 day');
$dt = new DateTimeEnhanced; # initialize new object
echo $dt->format(DateTime::W3C) . "\n"; # 2013-09-12T15:01:44+02:00
$dt->add($interval); # this modifies the object values
echo $dt->format(DateTime::W3C) . "\n"; # 2013-09-13T15:01:44+02:00
$dtNew = $dt->returnAdd($interval); # this returns the new modified object and doesn't change original object
echo $dt->format(DateTime::W3C) . "\n"; # 2013-09-13T15:01:44+02:00
echo $dtNew->format(DateTime::W3C) . "\n"; # 2013-09-14T15:01:44+02:00
Be careful that the internal timer to your DateTime object can be changed drastically when adding even 1 second, during the switch from DST to normal.
Consider the following:
<?php
$ts = 1383458399; /* 2013-11-03 01:59:59 in Eastern Saving Time */
$dst = DateTime::createFromFormat('U',$ts, new DateTimeZone('GMT')); /* timezone is ignored for a unix timestamp, but if we don't put it, php throws warnings */
$dst->setTimeZone(new DateTimeZone('EST5EDT')); /* a timezone effectuating the change */
$second = new DateInterval('PT1S'); /* one second */
echo $ts . "\t" . $dst->format("U\tY-m-d H:i:s T") . "\n";
$dst->add($second);
$ts++;
echo $ts . "\t" . $dst->format("U\tY-m-d H:i:s T") . "\n";
/* results:
1383458399 1383458399 2013-11-03 01:59:59 EDT
1383458400 1383462000 2013-11-03 02:00:00 EST
noticed how the second column went from 1383458399 to 1383462000 even though only 1 second was added?
*/
?>
Here is a solution to adding months when you want 2014-10-31 to become 2014-11-30 instead of 2014-12-01.
<?php
/**
* Class MyDateTime
*
* Extends DateTime to include a sensible addMonth method.
*
* This class provides a method that will increment the month, and
* if the day is greater than the last day in the new month, it
* changes the day to the last day of that month. For example,
* If you add one month to 2014-10-31 using DateTime::add, the
* result is 2014-12-01. Using MyDateTime::addMonth the result is
* 2014-11-30.
*/
class MyDateTime extends DateTime
{
public function addMonth($num = 1)
{
$date = $this->format('Y-n-j');
list($y, $m, $d) = explode('-', $date);
$m += $num;
while ($m > 12)
{
$m -= 12;
$y++;
}
$last_day = date('t', strtotime("$y-$m-1"));
if ($d > $last_day)
{
$d = $last_day;
}
$this->setDate($y, $m, $d);
}
}
?>
If you're using PHP >= 5.5, instead of using "glavic at gmail dot com"'s DateTimeEnhanced class, use the built in DateTimeImmutable type. When you call DateTimeImmutable::add() it will return a new object, rather than modifying the original
Another simple solution to adding a month but not autocorrecting days to the next month is this.
(Also works for substracting months)
$dt = new DateTime("2016-01-31");
$oldDay = $dt->format("d");
$dt->add(new DateInterval("P1M")); // 2016-03-02
$newDay = $dt->format("d");
if($oldDay != $newDay) {
// Check if the day is changed, if so we skipped to the next month.
// Substract days to go back to the last day of previous month.
$dt->sub(new DateInterval("P" . $newDay . "D"));
}
echo $dt->format("Y-m-d"); // 2016-02-29
Hope this helps someone.
Be careful when using this function, I may have happened upon a bug in PHP7.
My code is as follows
//get date from post or else fill with today's date
if (isset($_POST["from"]))
{
$from = date_create($_POST["from"]);
}else{
$from = date_create(date("Y-m-d"));
}
//get date from post if there isn't one just take the same date as what is in the $from variable and add one day to it
if (isset($_POST["to"]))
{
$to = date_create($_POST["to"]);
}else{
$to = $from;
date_modify($to, '+1 day');
}
echo(date_format($from, 'Y-m-d') . " " . date_format($to, 'Y-m-d'));
The resultant output is
$from = 2015-12-11
$to = 2015-12-11
In actuality the result should be
$from = 2015-12-10
$to = 2015-12-11
For some reason the code above modifies the $from variable in the line date_modify($to, '+1 day'); even though it shouldn't as the $from variable isn't being modified.
to fix this i needed to change the code to
//get date from post or else fill with today's date
if (isset($_POST["from"]))
{
$from = date_create($_POST["from"]);
}else{
$from = date_create(date("Y-m-d"));
}
//get date from post if there isn't one just take the same date as what is in the $from variable and add one day to it
if (isset($_POST["to"]))
{
$to = date_create($_POST["to"]);
}else{
$to = date_create(date("Y-m-d"));
date_modify($to, '+1 day');
}
echo(date_format($from, 'Y-m-d') . " " . date_format($to, 'Y-m-d'));
This isn't strictly the code I wanted. Possible bug?
What you can do with this function/method is a great example of the philosophy: "just because you can do it doesn't mean you should". I'm talking about two issues: (1) the number of days in the month which varies from months 1-12 as well as for month 2 which could be leap year (or not); and then issue (2): what if there is the need to specify a large quantity of an interval such that it needs to be re-characterized into broader-scoped intervals (i.e. 184 seconds ==> 3 minutes-4 seconds). Examples in notes elsewhere in the docs for this function illustrate both issues and their undesired effects so I won't focus on them further. But how did I decide to handle? I've gone with four "public" functions and a single "private" function, and without giving you a bunch of code to study, here are their summaries...
1. function adjustYear(int $yearsAdj){ //you can pass in +/- value and I adjust year value by that value but then I also call PHP's 'cal_days_in_month' function to ensure the day number I have in my date does not exceed days in the month for the new year/month combo--if it does, I adjust the day value downward.
2. function adjustMonth(int $monthsAdj){ //same notes as above apply; but also, I allow any number to be passed in for $monthsAdj. I use the 'int' function (int($monthsAdj/12)) and modulus % operator to determine how to adjust both year and month. And again, I use 'cal_days_in_month' function to tweak the day number as needed.
3. function addTime(int $days, int $hours, int $minutes, int $seconds){
// I use date_add and create a DateInterval object from the corresponding string spec (created from the args passed to this function). Note that months and years are excluded due to the bad side-effects already mentioned elsewhere.
4. function subtractTime(int $days, int $hours, int $minutes, int $seconds){
//notes for "addTime" also apply to this function but note that I like separate add and subtract functions because setting the DateInterval property flag to indicate add/subtract is not as intuitive for future coding.
5. function recharacterizeIntervals(int $days, int $hours, int $minutes, int $seconds){ // I convert excessively large quantities of any one interval into the next largest interval using the 'int' function and modulus (%) operator. I then use the result of this function when creating the string interval specification that gets passed when generating the DateInterval object for calling the date_add function (or object-method equivalent).
**Results/goals...
--any number of days/hours/minutes/seconds can be passed in to add/subtractTime and all of "Y/M/D/H/M/S" values get adjusted as you would expect.
--using adjustYear/Month lets you pass +/- values and only "Y/M" values get modified without having undesirable effects on day values.
--a call to the "recharacterize" function helps ensure proper and desired values are in the intervals prior to calling date_add to let it do its work.
If you use fraction of seconds, you may have surprises. It only occurs when the sum of the floating point parts results in exactly 1 second (0.5 + 0.5 ou 0.3 + 0.7, for example). See these cases at intervals slightly bigger than 1 second:
<?php
$objDataHora = date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT0S");
$objIntervalo->f = 0.600;
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>
$strDataHora is correct: "2017-12-31T23:59:59.900"
<?php
$objDataHora = date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT0S");
$objIntervalo->f = 0.800;
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>
$strDataHora is correct: "2018-01-01T00:00:00.100"
But...
<?php
$objDataHora = date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT0S");
$objIntervalo->f = 0.700;
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>
$strDataHora has "2017-12-31T23:59:59.1000"
To resolve, add 1 second to the interval and f property must be negative (-1.0 plus original value):
<?php
$objDataHora = date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT1S");
$objIntervalo->f = -0.300; // = -1.0 + 0.700
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>
$strDataHora is correct: "2018-01-01T00:00:00.000"
Remark, that calculations on date are not defined as bijective operations. The Summertime is integrated by mixing two concepts. You should test it beforehead.
Datetime will correct a date after each summation, if a date (29.2.2021 => 1.3.2021) or a datetime (29.3.2020 2:30 am (Europe/Berlin) => 29.3.2020 3:30 or 29.3.2020 1:30)
Example
<?php
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20761M');
$expectEaster->sub($interval);
echo('recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n" );
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20760M');
$expectEaster->sub($interval);
echo('recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20701M');
$expectEaster->sub($interval);
echo('recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20700M');
$expectEaster->sub($interval);
echo('recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n");
// Result
// recalc 2020-03-29 00:59:00 // reduce the missing hour before you calcuclate the datetime
// easter 2020-04-12 11:00:00 // recalcultate the date and remove the missing hour
// recalc 2020-03-29 03:00:00 //because 2020-03-29 3:00:00 [it means 2020-03-29 2:00:00] does not exist add 60 min)
// easter 2020-04-12 13:00:00
// recalc 2020-03-29 03:59:00 // -(12*60+(11+2)*1440+21*60) = -(20701 min) = = 29.3.2020 2:59(not exist => no-equivalent add of one hour) => 29.3.2020 3:59
// easter 2020-04-12 13:00:00 // Recalc add 60 minutes, because the hour does not exist.)
// recalc 2020-03-29 03:00:00 // -(12*60+(11+2)*1440+21*60 min)= -(20700 min) = 29.3.2020 3:00
// easter 2020-04-12 12:00:00 // +(12*60+(11+2)*1440+21*60 min)= +(20700 min) = 29.3.2020
$TodaySQL = substr(date(DATE_ISO8601 ),0,10)
$LastYearSQL = date('Y.m.d',strtotime("-1 years"))
$NextMonthEndSQL = date('Y.m.d',strtotime("+1 months"))
// handy little SQL date formats
//Today
2021-03-24
//Last year
2020.03.24
//Next month
2021.04.24