Dates7 min readExamples run on PHP 8.5.10

date_date_set()

Learn how date_date_set() replaces the year, month and day of a DateTime object, how overflow values roll over, and which mistakes to avoid.

Signature

date_date_set(DateTime $object, int $year, int $month, int $day): DateTime
Available since PHP 5.2.07.48.08.18.28.38.48.5All versions →

Purpose

date_date_set() is the procedural alias of DateTime::setDate(). It replaces the year, month and day of an existing DateTime object and leaves the time of day alone. Use it when you already hold a date-time value and only the calendar part should change, for example moving an appointment to another day while keeping its start time.

The two spellings do the same thing. date_date_set($date, 2025, 3, 9) behaves like $date->setDate(2025, 3, 9). Choose one style and stay consistent within a codebase.

Signature

Parameters

  • $object: the DateTime to modify. The object is changed in place. It must be a DateTime, not a DateTimeImmutable.
  • $year: the new year as an integer.
  • $month: the new month, normally 1 to 12.
  • $day: the new day of the month, normally 1 to 31 depending on the month.

None of the parameters has a default, so all four are required.

Return value and failure cases

The function returns the same DateTime object that you passed in, now modified. Because it is the same instance, you can chain further calls on the result or ignore the return value entirely.

The arguments are not rejected for being out of range. Values that do not form a real date are normalized instead. The most common failure is a type problem. Passing a DateTimeImmutable as $object, or a non-integer value that cannot be coerced to int, raises a TypeError.

Example 1: change the date, keep the time

The time portion and the timezone of the object are untouched.

Example
<?php
$date = new DateTime('2024-01-15 10:30:00', new DateTimeZone('UTC'));
date_date_set($date, 2025, 3, 9);
echo $date->format('Y-m-d H:i:s'), "\n";
Output
2025-03-09 10:30:00

Example 2: overflow is normalized

Out-of-range months and days roll forward or backward, much like date arithmetic. February 31 in a leap year lands on March 2. Month 13 becomes January of the next year. Day 0 means the last day of the previous month.

Example
<?php
$date = new DateTime('2024-01-31 08:00:00', new DateTimeZone('UTC'));
$results = [];

date_date_set($date, 2024, 2, 31);
$results[] = $date->format('Y-m-d');

date_date_set($date, 2024, 13, 1);
$results[] = $date->format('Y-m-d');

date_date_set($date, 2024, 3, 0);
$results[] = $date->format('Y-m-d');

echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
["2024-03-02","2025-01-01","2024-02-29"]

Example 3: the same object is returned

The return value is not a copy. Both variables point to one object, so a change through either is visible through both.

Example
<?php
$a = new DateTime('2024-05-10 12:00:00', new DateTimeZone('UTC'));
$b = date_date_set($a, 2030, 1, 2);
echo json_encode([$a === $b, $a->format('Y-m-d')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
[true,"2030-01-02"]

Edge cases

  • Overflow is silent. A day such as 31 in a 30-day month does not raise an error. It moves into the next month. If you need strict validation, check the input first, for example with checkdate().
  • Time is preserved. If you also need to change the hour, minute or second, call date_time_set() or DateTime::setTime() separately.
  • Order of setting matters in your own logic. Because all three parts are set together, you avoid the trap of changing the month first and having the old day overflow. That is a good reason to prefer one date_date_set() call over several separate modifications.
  • Immutable objects. DateTimeImmutable has its own setDate() method that returns a new object. The procedural function is only for DateTime.

Common mistakes

  1. Assuming a copy is returned. Code such as $moved = date_date_set($original, ...) modifies $original too. Clone first with clone $original if the original must stay unchanged.
  2. Passing a DateTimeImmutable. This fails with a TypeError. Call the object method setDate() and use the returned value instead.
  3. Expecting validation. Impossible dates are normalized, not rejected.
  4. Mixing up argument order. The order is year, month, day, not day, month, year.
  5. Expecting the time to reset. Only the date changes. A value of 10:30:00 stays 10:30:00.
  • DateTime::setDate(): the method form of this function.
  • date_time_set(): change hour, minute and second.
  • date_isodate_set(): set the date using ISO week numbering.
  • date_modify(): apply a relative change such as +1 month.
  • checkdate(): validate a Gregorian date before using it.
  • date_create(): create a DateTime object procedurally.

Recap

date_date_set() changes the year, month and day of a DateTime in place and returns that same object. It keeps the time and timezone, normalizes overflowing values, and requires a mutable DateTime instance. Validate user input yourself when the date must be real, and clone the object when you need to keep the original.

Tested examples

Change the date while keeping the time
<?php
$date = new DateTime('2024-01-15 10:30:00', new DateTimeZone('UTC'));
date_date_set($date, 2025, 3, 9);
echo $date->format('Y-m-d H:i:s'), "\n";
Output
2025-03-09 10:30:00
Overflowing month and day values are normalized
<?php
$date = new DateTime('2024-01-31 08:00:00', new DateTimeZone('UTC'));
$results = [];

date_date_set($date, 2024, 2, 31);
$results[] = $date->format('Y-m-d');

date_date_set($date, 2024, 13, 1);
$results[] = $date->format('Y-m-d');

date_date_set($date, 2024, 3, 0);
$results[] = $date->format('Y-m-d');

echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
["2024-03-02","2025-01-01","2024-02-29"]
The same object is returned
<?php
$a = new DateTime('2024-05-10 12:00:00', new DateTimeZone('UTC'));
$b = date_date_set($a, 2030, 1, 2);
echo json_encode([$a === $b, $a->format('Y-m-d')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
[true,"2030-01-02"]