PHP date_modify() 函数

date_modify() 函数是 DateTime::modify() 的过程化别名,用于使用相对时间字符串修改 DateTime 对象。这是PHP中最灵活、最强大的日期时间修改函数之一。

提示: date_modify() 支持丰富的相对时间格式,可以轻松地增加或减少日期时间,处理月末日期等复杂场景。

语法

DateTime|false date_modify ( DateTime $object , string $modify )

参数说明

参数 描述 必需
$object DateTime 对象,表示要修改的日期时间
$modify 相对时间字符串,描述如何修改日期时间

返回值

  • DateTime 对象 - 修改成功时返回
  • false - 修改失败时返回
  • 原始 DateTime 对象也会被修改
  • 支持链式调用

支持的相对时间格式

类型 格式 示例 描述
基本单位 ±N unit +1 day, -3 months 加减天、月、年、时、分、秒
±N units +2 weeks, -1 year 加减周、年等复数单位
next/last unit next Monday, last month 下一个/上一个时间单位
复合格式 ±N unit ±M unit +1 month +15 days 多个单位的组合
first/last day of first day of next month 月份的第一天/最后一天
星期相关 next Thursday, last Friday 特定星期几的计算
特殊格式 ±N weekday +1 weekday, -3 weekdays 工作日(跳过周末)
时间设置 14:30:00, tomorrow 09:00 直接设置时间
绝对日期 2024-03-15, 15 March 2024 直接设置为指定日期

示例代码

示例 1:基本用法

<?php
// 创建 DateTime 对象
$date = date_create('2024-03-15 14:30:45');

echo "原始日期: " . date_format($date, 'Y-m-d H:i:s') . "<br><br>";

// 增加时间
date_modify($date, '+1 day');
echo "+1 day: " . date_format($date, 'Y-m-d H:i:s') . "<br>";

date_modify($date, '+2 weeks');
echo "+2 weeks: " . date_format($date, 'Y-m-d H:i:s') . "<br>";

date_modify($date, '+3 months 5 days');
echo "+3 months 5 days: " . date_format($date, 'Y-m-d H:i:s') . "<br>";

date_modify($date, '+1 year -2 months');
echo "+1 year -2 months: " . date_format($date, 'Y-m-d H:i:s') . "<br><br>";

// 减少时间
$date2 = date_create('2024-03-15');
date_modify($date2, '-10 days');
echo "-10 days: " . date_format($date2, 'Y-m-d') . "<br>";

date_modify($date2, '2 days ago');
echo "2 days ago: " . date_format($date2, 'Y-m-d') . "<br>";

// 链式调用
$date3 = date_create('2024-03-15');
echo "链式调用: " .
     date_format(date_modify($date3, '+1 month'), 'Y-m-d') . "<br>";
?>

示例 2:处理月末日期

<?php
// date_modify() 智能处理月末日期
echo "月末日期处理示例:<br><br>";

// 1月31日加1个月
$date1 = date_create('2024-01-31');
date_modify($date1, '+1 month');
echo "2024-01-31 +1 month = " . date_format($date1, 'Y-m-d') . "<br>";

// 2月28日(非闰年)加1个月
$date2 = date_create('2023-02-28');
date_modify($date2, '+1 month');
echo "2023-02-28 +1 month = " . date_format($date2, 'Y-m-d') . "<br>";

// 2月29日(闰年)加1年
$date3 = date_create('2024-02-29');
date_modify($date3, '+1 year');
echo "2024-02-29 +1 year = " . date_format($date3, 'Y-m-d') . "<br>";

// 使用"first/last day of"
$date4 = date_create('2024-03-15');
date_modify($date4, 'first day of this month');
echo "2024-03-15 first day of this month = " . date_format($date4, 'Y-m-d') . "<br>";

date_modify($date4, 'last day of next month');
echo "然后 last day of next month = " . date_format($date4, 'Y-m-d') . "<br><br>";

// 复杂的月末计算
$dates = [
    '2024-01-31',
    '2024-02-28',
    '2024-02-29',
    '2024-03-31',
    '2024-04-30',
];

echo "月末加1个月测试:<br>";
foreach ($dates as $dateStr) {
    $date = date_create($dateStr);
    date_modify($date, '+1 month');
    echo "{$dateStr} +1 month = " . date_format($date, 'Y-m-d') . "<br>";
}
?>

示例 3:星期和相对日期

<?php
// 星期和相对日期处理
$date = date_create('2024-03-15'); // 星期五

echo "原始日期: " . date_format($date, 'Y-m-d l') . "<br><br>";

echo "星期相关操作:<br>";

// 下一个特定星期几
date_modify($date, 'next Monday');
echo "next Monday: " . date_format($date, 'Y-m-d l') . "<br>";

date_modify($date, 'last Friday');
echo "last Friday: " . date_format($date, 'Y-m-d l') . "<br>";

date_modify($date, 'next week');
echo "next week: " . date_format($date, 'Y-m-d l') . "<br>";

date_modify($date, 'previous Thursday');
echo "previous Thursday: " . date_format($date, 'Y-m-d l') . "<br><br>";

// 工作日计算(跳过周末)
echo "工作日计算:<br>";
$workDate = date_create('2024-03-15'); // 星期五
for ($i = 1; $i <= 5; $i++) {
    $current = clone $workDate;
    date_modify($current, "+{$i} weekday");
    echo "+{$i} weekday: " . date_format($current, 'Y-m-d l') . "<br>";
}

echo "<br>特殊相对日期:<br>";
$date2 = date_create('2024-03-15');
$operations = [
    'tomorrow',
    'yesterday',
    'next month',
    'last year',
    'next year',
    '3 days ago',
    'first day of January',
    'last day of February',
    'third Sunday of March',
];

foreach ($operations as $op) {
    $testDate = date_create('2024-03-15');
    date_modify($testDate, $op);
    echo "{$op}: " . date_format($testDate, 'Y-m-d l') . "<br>";
}
?>

示例 4:实用工具函数

<?php
/**
 * 实用日期工具函数集
 */
class DateTool {

    /**
     * 计算两个日期之间的工作日数
     */
    public static function getBusinessDays($start, $end) {
        $startDate = date_create($start);
        $endDate = date_create($end);

        if ($startDate > $endDate) {
            // 交换日期
            $temp = $startDate;
            $startDate = $endDate;
            $endDate = $temp;
        }

        $businessDays = 0;
        $current = clone $startDate;

        while ($current <= $endDate) {
            $dayOfWeek = date_format($current, 'N'); // 1-7 (1=Monday, 7=Sunday)
            if ($dayOfWeek <= 5) {
                $businessDays++;
            }
            date_modify($current, '+1 day');
        }

        return $businessDays;
    }

    /**
     * 添加工作日
     */
    public static function addBusinessDays($date, $days) {
        $dateObj = date_create($date);
        $added = 0;

        while ($added < $days) {
            date_modify($dateObj, '+1 day');
            $dayOfWeek = date_format($dateObj, 'N');
            if ($dayOfWeek <= 5) {
                $added++;
            }
        }

        return date_format($dateObj, 'Y-m-d');
    }

    /**
     * 获取下一个工作日的日期
     */
    public static function nextBusinessDay($date) {
        $dateObj = date_create($date);

        do {
            date_modify($dateObj, '+1 day');
            $dayOfWeek = date_format($dateObj, 'N');
        } while ($dayOfWeek > 5); // 跳过周末

        return date_format($dateObj, 'Y-m-d');
    }

    /**
     * 计算项目的截止日期
     */
    public static function calculateDeadline($startDate, $durationDays, $includeWeekends = false) {
        $date = date_create($startDate);

        if ($includeWeekends) {
            date_modify($date, "+{$durationDays} days");
        } else {
            // 只计算工作日
            $date = date_create(self::addBusinessDays($startDate, $durationDays));
        }

        return date_format($date, 'Y-m-d');
    }

    /**
     * 生成日期范围
     */
    public static function generateDateRange($start, $end, $step = '+1 day', $format = 'Y-m-d') {
        $dates = [];
        $current = date_create($start);
        $endDate = date_create($end);

        while ($current <= $endDate) {
            $dates[] = date_format($current, $format);
            date_modify($current, $step);
        }

        return $dates;
    }

    /**
     * 获取某月的所有工作日
     */
    public static function getMonthBusinessDays($year, $month) {
        $firstDay = date_create("{$year}-{$month}-01");
        $lastDay = date_create("{$year}-{$month}-" . date_format($firstDay, 't'));

        $businessDays = [];
        $current = clone $firstDay;

        while ($current <= $lastDay) {
            $dayOfWeek = date_format($current, 'N');
            if ($dayOfWeek <= 5) {
                $businessDays[] = date_format($current, 'Y-m-d l');
            }
            date_modify($current, '+1 day');
        }

        return $businessDays;
    }
}

echo "日期工具函数示例:<br><br>";

// 测试工作日计算
$startDate = '2024-03-01';
$endDate = '2024-03-15';
$businessDays = DateTool::getBusinessDays($startDate, $endDate);
echo "{$startDate} 到 {$endDate} 的工作日数: {$businessDays}<br><br>";

// 测试添加工作日
$testDate = '2024-03-15';
$addedDate = DateTool::addBusinessDays($testDate, 5);
echo "{$testDate} 加5个工作日: {$addedDate}<br><br>";

// 测试下一个工作日
$nextBusiness = DateTool::nextBusinessDay('2024-03-15'); // 星期五
echo "2024-03-15 的下一个工作日: {$nextBusiness}<br><br>";

// 测试项目截止日期
$deadline = DateTool::calculateDeadline('2024-03-15', 10, false);
echo "项目开始: 2024-03-15, 10个工作日后的截止日期: {$deadline}<br><br>";

// 测试日期范围
$dateRange = DateTool::generateDateRange('2024-03-10', '2024-03-15', '+2 days');
echo "2024-03-10 到 2024-03-15 每2天的日期: " . implode(', ', $dateRange) . "<br><br>";

// 测试某月工作日
echo "2024年3月的工作日:<br>";
$marchBusinessDays = DateTool::getMonthBusinessDays(2024, 3);
foreach (array_slice($marchBusinessDays, 0, 5) as $day) {
    echo $day . "<br>";
}
echo "... (共" . count($marchBusinessDays) . "个工作日)";
?>

示例 5:错误处理与边界情况

<?php
// 错误处理与边界情况
function safeDateModify($date, $modifyString) {
    // 如果是字符串,创建DateTime对象
    if (is_string($date)) {
        $date = date_create($date);
    }

    if ($date === false) {
        return "错误: 无法创建DateTime对象";
    }

    $originalDate = clone $date;
    $result = date_modify($date, $modifyString);

    if ($result === false) {
        return [
            'success' => false,
            'error' => "无法执行修改: '{$modifyString}'",
            'original' => date_format($originalDate, 'Y-m-d H:i:s'),
            'result' => null,
        ];
    }

    return [
        'success' => true,
        'error' => null,
        'original' => date_format($originalDate, 'Y-m-d H:i:s'),
        'result' => date_format($date, 'Y-m-d H:i:s'),
        'modified' => $date,
    ];
}

echo "错误处理与边界情况测试:<br><br>";

$testCases = [
    ['date' => '2024-03-15', 'modify' => '+1 day', 'desc' => '正常修改'],
    ['date' => '2024-03-15', 'modify' => 'invalid string', 'desc' => '无效字符串'],
    ['date' => '2024-02-30', 'modify' => '+1 day', 'desc' => '无效初始日期'],
    ['date' => '2024-03-15', 'modify' => 'next someday', 'desc' => '无效星期几'],
    ['date' => '2024-03-15', 'modify' => '+999 years', 'desc' => '超大时间跨度'],
    ['date' => '2024-03-15', 'modify' => 'first day of', 'desc' => '不完整表达式'],
    ['date' => '2024-03-15', 'modify' => '', 'desc' => '空字符串'],
    ['date' => '2024-03-15', 'modify' => '25:00:00', 'desc' => '无效时间'],
];

foreach ($testCases as $test) {
    echo "测试: {$test['desc']}<br>";
    echo "输入: date='{$test['date']}', modify='{$test['modify']}'<br>";

    $result = safeDateModify($test['date'], $test['modify']);

    if ($result['success']) {
        echo "✅ 成功: {$result['original']} → {$result['result']}<br>";
    } else {
        echo "❌ 失败: {$result['error']}<br>";
    }
    echo "<br>";
}

// 边界值测试
echo "<strong>边界值测试:</strong><br><br>";

// 测试最大值
$date = date_create('9999-12-31');
date_modify($date, '+1 day');
echo "9999-12-31 +1 day: " . (date_format($date, 'Y-m-d') ?: '超出范围') . "<br>";

// 测试最小值
$date = date_create('0001-01-01');
date_modify($date, '-1 day');
echo "0001-01-01 -1 day: " . (date_format($date, 'Y-m-d') ?: '超出范围') . "<br>";

// 测试闰秒(PHP通常不处理闰秒)
$date = date_create('2024-06-30 23:59:60');
echo "闰秒测试: " . ($date ? date_format($date, 'Y-m-d H:i:s') : '无效时间') . "<br>";

// 测试时区转换
echo "<br><strong>时区转换测试:</strong><br>";
$date = date_create('2024-03-15 12:00:00', new DateTimeZone('UTC'));
echo "UTC时间: " . date_format($date, 'Y-m-d H:i:s e') . "<br>";

date_modify($date, '+1 hour');
echo "UTC +1 hour: " . date_format($date, 'Y-m-d H:i:s e') . "<br>";

// 修改时区
$date->setTimezone(new DateTimeZone('Asia/Shanghai'));
echo "转换到上海时区: " . date_format($date, 'Y-m-d H:i:s e') . "<br>";

date_modify($date, '+2 hours');
echo "上海 +2 hours: " . date_format($date, 'Y-m-d H:i:s e') . "<br>";
?>

支持的相对时间单位

单位 示例 描述
second, seconds +30 seconds, -1 second
minute, minutes +15 minutes, -5 minute 分钟
hour, hours +2 hours, -1 hour 小时
day, days +3 days, -1 day
week, weeks +2 weeks, -1 week
month, months +6 months, -1 month
year, years +1 year, -2 years
weekday, weekdays +1 weekday, -3 weekdays 工作日(跳过周末)
fortnight +1 fortnight 两周

注意事项

重要提示:
  • date_modify() 会修改原始 DateTime 对象
  • 对于无效的修改字符串,函数返回 false 但不会抛出异常(PHP 8.0+会抛出异常)
  • 月末日期处理是智能的:1月31日+1个月=2月28/29日
  • 相对时间字符串不区分大小写,但建议使用小写保持一致性
  • date_modify() 会保持时间的完整性,不会因为日期改变而丢失时间部分
  • 在PHP 8.0+中,无效的修改字符串会抛出异常而不是返回false
  • 时区会影响日期的计算结果,特别是夏令时转换期间

PHP版本差异

PHP版本 行为 说明
PHP 5.2+ 返回false 修改失败时返回false
PHP 8.0+ 抛出异常 无效修改字符串抛出DateMalformedStringException
PHP 5.3+ 支持更多格式 增加了first/last day of等格式
PHP 7.0+ 错误处理改进 错误信息更加详细

常见问题

函数 参数类型 灵活性 适用场景
date_modify() 相对时间字符串 高,支持复杂表达式 用户输入、动态计算
date_add() DateInterval对象 中,需要创建对象 精确时间间隔、程序化计算
<?php
// date_modify() 示例
$date1 = date_create('2024-03-15');
date_modify($date1, '+2 weeks 3 days');
echo "date_modify: " . date_format($date1, 'Y-m-d') . "<br>";

// date_add() 示例
$date2 = date_create('2024-03-15');
$interval = new DateInterval('P2W3D');
date_add($date2, $interval);
echo "date_add: " . date_format($date2, 'Y-m-d') . "<br>";

// 两者结果相同
?>

date_modify() 会智能处理月末日期:

<?php
$dates = [
    '2024-01-31',
    '2024-02-28',
    '2024-02-29',
    '2024-03-31',
    '2024-04-30',
];

foreach ($dates as $dateStr) {
    $date = date_create($dateStr);
    date_modify($date, 'next month');
    echo "{$dateStr} next month = " . date_format($date, 'Y-m-d') . "<br>";
}

// 如果需要总是得到月末,可以使用"last day of next month"
echo "<br>使用'last day of next month':<br>";
foreach ($dates as $dateStr) {
    $date = date_create($dateStr);
    date_modify($date, 'last day of next month');
    echo "{$dateStr} last day of next month = " . date_format($date, 'Y-m-d') . "<br>";
}
?>

性能优化建议

date_modify() 性能考虑
  1. 重用DateTime对象:避免重复创建DateTime对象
  2. 批量操作:对于大量日期修改,考虑批量处理
  3. 使用合适的方法:简单的时间加减使用date_add()/date_sub()可能更快
  4. 避免复杂字符串解析:复杂的相对时间字符串解析开销较大
  5. 缓存结果:对于重复的日期计算,缓存结果
  6. 预验证输入:在处理用户输入前验证相对时间字符串的有效性

相关函数

函数 描述
DateTime::modify() 面向对象的日期修改方法
date_add() 向DateTime对象添加时间间隔
date_sub() 从DateTime对象减去时间间隔
date_date_set() 设置DateTime对象的日期部分
date_time_set() 设置DateTime对象的时间部分
strtotime() 将英文文本日期时间解析为Unix时间戳
date_interval_create_from_date_string() 从相对时间字符串创建DateInterval对象