PHP date_create_immutable()函数

date_create_immutable() 函数创建一个新的不可变 DateTime 对象,返回 DateTimeImmutable 对象。与普通的 DateTime 对象不同,DateTimeImmutable 对象是不可变的,任何对它的修改都会返回一个新的对象,而原对象保持不变。

这个函数是 new DateTimeImmutable() 构造函数的过程化别名,推荐在函数式编程中使用。

语法

date_create_immutable([string $datetime = "now" [, DateTimeZone $timezone = null]]) : DateTimeImmutable|false

参数说明

参数 默认值 描述
datetime "now"

可选。 日期/时间字符串,任何能被 strtotime() 解析的格式。

例如:"2023-12-25 14:30:00", "next Monday", "+1 week" 等。

timezone null

可选。 DateTimeZone 对象,表示时区。

如果为 null,则使用当前默认时区。

返回值

成功时返回一个新的 DateTimeImmutable 对象,失败时返回 false

示例

示例 1:基本用法

<?php
// 创建当前时间的不可变对象
$date = date_create_immutable();

if ($date !== false) {
    echo "当前时间: " . $date->format('Y-m-d H:i:s') . "<br>";
    echo "时区: " . $date->getTimezone()->getName() . "<br>";
    echo "对象类型: " . get_class($date);
}

// 输出类似:
// 当前时间: 2023-08-15 10:30:45
// 时区: Asia/Shanghai
// 对象类型: DateTimeImmutable
?>

示例 2:创建指定时间的不可变对象

<?php
// 创建指定时间的不可变对象
$date1 = date_create_immutable('2023-12-25 14:30:00');
$date2 = date_create_immutable('next Monday');
$date3 = date_create_immutable('+1 week 2 days');

echo "指定时间: " . $date1->format('Y-m-d H:i:s') . "<br>";
echo "下周一: " . $date2->format('Y-m-d') . "<br>";
echo "1周2天后: " . $date3->format('Y-m-d') . "<br>";

// 使用相对时间
$date4 = date_create_immutable('first day of next month');
echo "下个月第一天: " . $date4->format('Y-m-d') . "<br>";

// 输出类似:
// 指定时间: 2023-12-25 14:30:00
// 下周一: 2023-08-21
// 1周2天后: 2023-08-24
// 下个月第一天: 2023-09-01
?>

示例 3:使用时区

<?php
// 使用时区创建不可变对象
$timezone_ny = new DateTimeZone('America/New_York');
$timezone_tokyo = new DateTimeZone('Asia/Tokyo');

$date_ny = date_create_immutable('2023-12-25 12:00:00', $timezone_ny);
$date_tokyo = date_create_immutable('2023-12-25 12:00:00', $timezone_tokyo);

echo "纽约时间: " . $date_ny->format('Y-m-d H:i:s T') . "<br>";
echo "东京时间: " . $date_tokyo->format('Y-m-d H:i:s T') . "<br><br>";

// 比较时间戳(应该相同,因为指定了不同的时区)
echo "时间戳比较:<br>";
echo "纽约时间戳: " . $date_ny->getTimestamp() . "<br>";
echo "东京时间戳: " . $date_tokyo->getTimestamp() . "<br>";
echo "是否相同: " . ($date_ny->getTimestamp() === $date_tokyo->getTimestamp() ? '是' : '否');

// 输出类似:
// 纽约时间: 2023-12-25 12:00:00 EST
// 东京时间: 2023-12-25 12:00:00 JST
//
// 时间戳比较:
// 纽约时间戳: 1703520000
// 东京时间戳: 1703520000
// 是否相同: 是
?>

示例 4:不可变性演示(与可变对象对比)

<?php
// 不可变对象与可变对象的对比
echo "<h4>可变 DateTime 对象:</h4>";
$mutable_date = date_create('2023-01-01');
echo "原始日期: " . $mutable_date->format('Y-m-d') . "<br>";

// 修改可变对象
$modified_mutable = $mutable_date->modify('+1 month');
echo "修改后日期: " . $mutable_date->format('Y-m-d') . "<br>";
echo "返回的新对象: " . ($modified_mutable === $mutable_date ? '是同一个对象' : '新对象') . "<br><br>";

echo "<h4>不可变 DateTimeImmutable 对象:</h4>";
$immutable_date = date_create_immutable('2023-01-01');
echo "原始日期: " . $immutable_date->format('Y-m-d') . "<br>";

// 尝试修改不可变对象
$modified_immutable = $immutable_date->modify('+1 month');
echo "原对象日期: " . $immutable_date->format('Y-m-d') . "<br>";
echo "修改后返回的新对象日期: " . $modified_immutable->format('Y-m-d') . "<br>";
echo "原对象与新对象是否相同: " . ($immutable_date === $modified_immutable ? '是' : '否') . "<br>";

// 输出类似:
// 可变 DateTime 对象:
// 原始日期: 2023-01-01
// 修改后日期: 2023-02-01
// 返回的新对象: 是同一个对象
//
// 不可变 DateTimeImmutable 对象:
// 原始日期: 2023-01-01
// 原对象日期: 2023-01-01
// 修改后返回的新对象日期: 2023-02-01
// 原对象与新对象是否相同: 否
?>

示例 5:日期运算和比较

<?php
// 日期运算和比较
$start_date = date_create_immutable('2023-01-01');

// 创建新的不可变对象进行运算
$one_month_later = $start_date->modify('+1 month');
$two_months_later = $one_month_later->modify('+1 month');
$one_year_later = $start_date->modify('+1 year');

echo "开始日期: " . $start_date->format('Y-m-d') . "<br>";
echo "一个月后: " . $one_month_later->format('Y-m-d') . "<br>";
echo "两个月后: " . $two_months_later->format('Y-m-d') . "<br>";
echo "一年后: " . $one_year_later->format('Y-m-d') . "<br><br>";

// 日期比较
echo "日期比较:<br>";
echo "开始日期 < 一个月后: " . ($start_date < $one_month_later ? '是' : '否') . "<br>";
echo "一个月后 < 两个月后: " . ($one_month_later < $two_months_later ? '是' : '否') . "<br>";
echo "一年后 > 开始日期: " . ($one_year_later > $start_date ? '是' : '否') . "<br><br>";

// 日期差异
$diff = $start_date->diff($one_year_later);
echo "日期差异: " . $diff->format('%y 年 %m 月 %d 天') . "<br>";

// 输出类似:
// 开始日期: 2023-01-01
// 一个月后: 2023-02-01
// 两个月后: 2023-03-01
// 一年后: 2024-01-01
//
// 日期比较:
// 开始日期 < 一个月后: 是
// 一个月后 < 两个月后: 是
// 一年后 > 开始日期: 是
//
// 日期差异: 1 年 0 月 0 天
?>

示例 6:错误处理

<?php
// 错误处理
function safeDateCreateImmutable($datetime, $timezone = null) {
    $date = date_create_immutable($datetime, $timezone);

    if ($date === false) {
        return "错误: 无法解析日期时间字符串 '{$datetime}'";
    }

    return $date;
}

// 测试各种情况
$test_cases = [
    '2023-12-25 14:30:00',
    'invalid date string',
    'next Monday',
    '2023-13-45', // 无效日期
    '',
    '2023-02-28 +1 day' // 有效
];

foreach ($test_cases as $datetime) {
    $result = safeDateCreateImmutable($datetime);

    if (is_string($result)) {
        echo "'{$datetime}': {$result}<br>";
    } else {
        echo "'{$datetime}': 成功创建 - " . $result->format('Y-m-d H:i:s') . "<br>";
    }
}

// 输出类似:
// '2023-12-25 14:30:00': 成功创建 - 2023-12-25 14:30:00
// 'invalid date string': 错误: 无法解析日期时间字符串 'invalid date string'
// 'next Monday': 成功创建 - 2023-08-21 00:00:00
// '2023-13-45': 错误: 无法解析日期时间字符串 '2023-13-45'
// '': 成功创建 - 2023-08-15 10:30:45
// '2023-02-28 +1 day': 成功创建 - 2023-03-01 00:00:00
?>

示例 7:与 date_create() 的比较

<?php
// 比较 date_create_immutable() 和 date_create()
echo "<h4>date_create_immutable() 与 date_create() 比较:</h4>";

// 创建可变对象
$mutable = date_create('2023-01-01');
echo "可变对象类型: " . get_class($mutable) . "<br>";

// 创建不可变对象
$immutable = date_create_immutable('2023-01-01');
echo "不可变对象类型: " . get_class($immutable) . "<br><br>";

// 测试修改操作
echo "<h5>修改操作对比:</h5>";

// 修改可变对象
$mutable_original = $mutable->format('Y-m-d');
$mutable_modified = $mutable->modify('+1 day');
$mutable_after = $mutable->format('Y-m-d');

echo "可变对象 - 修改前: {$mutable_original}<br>";
echo "可变对象 - 修改后: {$mutable_after}<br>";
echo "可变对象 - 是否相同对象: " . ($mutable === $mutable_modified ? '是' : '否') . "<br><br>";

// 修改不可变对象
$immutable_original = $immutable->format('Y-m-d');
$immutable_modified = $immutable->modify('+1 day');
$immutable_after = $immutable->format('Y-m-d');

echo "不可变对象 - 修改前: {$immutable_original}<br>";
echo "不可变对象 - 原对象修改后: {$immutable_after}<br>";
echo "不可变对象 - 新对象日期: " . $immutable_modified->format('Y-m-d') . "<br>";
echo "不可变对象 - 是否相同对象: " . ($immutable === $immutable_modified ? '是' : '否') . "<br><br>";

// 序列化测试
echo "<h5>序列化对比:</h5>";
$serialized_mutable = serialize($mutable);
$serialized_immutable = serialize($immutable);

echo "可变对象序列化长度: " . strlen($serialized_mutable) . "<br>";
echo "不可变对象序列化长度: " . strlen($serialized_immutable) . "<br>";

// 输出类似:
// date_create_immutable() 与 date_create() 比较:
// 可变对象类型: DateTime
// 不可变对象类型: DateTimeImmutable
//
// 修改操作对比:
// 可变对象 - 修改前: 2023-01-01
// 可变对象 - 修改后: 2023-01-02
// 可变对象 - 是否相同对象: 是
//
// 不可变对象 - 修改前: 2023-01-01
// 不可变对象 - 原对象修改后: 2023-01-01
// 不可变对象 - 新对象日期: 2023-01-02
// 不可变对象 - 是否相同对象: 否
//
// 序列化对比:
// 可变对象序列化长度: 79
// 不可变对象序列化长度: 90
?>

示例 8:在实际应用中使用不可变对象

<?php
// 在实际应用中使用不可变对象
class EventScheduler {
    private $events = [];

    public function addEvent($name, $start_date) {
        // 确保使用不可变对象
        if (!$start_date instanceof DateTimeImmutable) {
            $start_date = date_create_immutable($start_date->format('Y-m-d H:i:s'), $start_date->getTimezone());
        }

        $this->events[] = [
            'name' => $name,
            'start' => $start_date,
            'end' => $start_date->modify('+2 hours') // 返回新对象,不影响原对象
        ];

        return $this;
    }

    public function getEventsAfter($date) {
        $result = [];
        foreach ($this->events as $event) {
            if ($event['start'] > $date) {
                $result[] = $event;
            }
        }
        return $result;
    }

    public function displayEvents() {
        foreach ($this->events as $event) {
            echo "活动: " . $event['name'] . "<br>";
            echo "开始: " . $event['start']->format('Y-m-d H:i:s') . "<br>";
            echo "结束: " . $event['end']->format('Y-m-d H:i:s') . "<br><br>";
        }
    }
}

// 使用示例
$scheduler = new EventScheduler();

// 添加活动
$scheduler->addEvent('会议', date_create_immutable('2023-12-25 09:00:00'))
         ->addEvent('培训', date_create_immutable('2023-12-26 14:00:00'))
         ->addEvent('派对', date_create_immutable('2023-12-31 20:00:00'));

echo "<h4>所有活动:</h4>";
$scheduler->displayEvents();

// 获取特定日期之后的活动
$cutoff_date = date_create_immutable('2023-12-26 00:00:00');
$future_events = $scheduler->getEventsAfter($cutoff_date);

echo "<h4>2023-12-26 之后的活动:</h4>";
if (empty($future_events)) {
    echo "没有找到活动";
} else {
    foreach ($future_events as $event) {
        echo "活动: " . $event['name'] . " (" . $event['start']->format('Y-m-d') . ")<br>";
    }
}

// 输出类似:
// 所有活动:
// 活动: 会议
// 开始: 2023-12-25 09:00:00
// 结束: 2023-12-25 11:00:00
//
// 活动: 培训
// 开始: 2023-12-26 14:00:00
// 结束: 2023-12-26 16:00:00
//
// 活动: 派对
// 开始: 2023-12-31 20:00:00
// 结束: 2024-01-01 22:00:00
//
// 2023-12-26 之后的活动:
// 活动: 派对 (2023-12-31)
?>

DateTime 与 DateTimeImmutable 对比

特性 DateTime(可变) DateTimeImmutable(不可变)
创建函数 date_create()new DateTime() date_create_immutable()new DateTimeImmutable()
修改行为 修改原对象,返回自身($this) 返回新对象,原对象不变
线程安全 不安全(可能被意外修改) 安全(不可修改)
函数式编程 不适用 适用
内存使用 较低(原地修改) 较高(创建新对象)
调试难度 较难(值可能变化) 较易(值不变)
推荐场景 简单脚本,性能敏感场景 复杂应用,多线程,函数式编程

常用方法

方法 描述 是否返回新对象
modify() 修改日期时间
add() 添加时间间隔
sub() 减去时间间隔
setDate() 设置日期
setTime() 设置时间
setTimestamp() 设置时间戳
setTimezone() 设置时区
format() 格式化日期时间 否(返回字符串)
getTimestamp() 获取时间戳 否(返回整数)
getTimezone() 获取时区 否(返回DateTimeZone)
diff() 计算时间差 否(返回DateInterval)

注意事项

  • DateTimeImmutable 对象不可变,所有修改操作都返回新对象
  • 不可变对象在函数式编程和多线程环境中更安全
  • 如果不需要修改原始日期,推荐使用不可变对象
  • 不可变对象会占用更多内存,因为每次修改都创建新对象
  • DateTime 和 DateTimeImmutable 可以相互转换
  • 在 PHP 7.2+ 中,date_create_immutable() 总是可用

最佳实践

场景 建议
需要保留原始日期 使用 DateTimeImmutable
函数式编程 使用 DateTimeImmutable
性能敏感场景 使用 DateTime(如果不需要不可变性)
API 设计 接受和返回 DateTimeImmutable
数据库操作 使用 DateTimeImmutable 避免意外修改
缓存日期对象 使用 DateTimeImmutable 保证值不变

相关函数

  • strtotime() - 将英文文本日期时间解析为 Unix 时间戳
  • date() - 格式化本地时间/日期
  • time() - 返回当前的 Unix 时间戳