PHP date_isodate_set() 函数

date_isodate_set() 函数是 DateTime::setISODate() 的过程化别名,用于根据 ISO 8601 标准设置日期。它使用年、周数和星期几来设置 DateTime 对象的日期。

提示: ISO 8601 是国际标准化组织制定的日期和时间表示方法标准,广泛应用于商业和国际交流中。

语法

DateTime date_isodate_set ( DateTime $object , int $year , int $week [, int $day = 1 ] )

参数说明

参数 描述 必需 默认值
$object DateTime 对象,表示要修改的日期时间 -
$year ISO 8601 年份 -
$week ISO 周数(1-53) -
$day 星期几(1=周一,7=周日) 1

返回值

  • 返回修改后的 DateTime 对象
  • 原始 DateTime 对象也会被修改
  • 支持链式调用

ISO 8601 标准说明

ISO 8601 日期标准特点:

  1. 一周从星期一开始,星期日是一周的第七天
  2. 一年的第一周是包含该年第一个星期四的那一周
  3. 一年有52或53周
  4. 日期格式:YYYY-Www-D(如 2024-W11-1 表示2024年第11周星期一)
  5. 常用于欧洲国家、商业和国际交流

示例代码

示例 1:基本用法

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

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

// 设置为2024年第11周星期一(ISO周)
date_isodate_set($date, 2024, 11, 1);

echo "修改后日期: " . date_format($date, 'Y-m-d H:i:s') . "<br>";
echo "ISO格式: " . date_format($date, 'Y-\WW-N') . "<br>";
echo "详细: " . date_format($date, 'Y年 第W周 星期N') . "<br>";

/*
输出:
原始日期: 2024-03-15 14:30:00
修改后日期: 2024-03-11 14:30:00
ISO格式: 2024-W11-1
详细: 2024年 第11周 星期1
*/
?>

示例 2:不同的星期几

<?php
// 显示2024年第11周的所有日子
$year = 2024;
$week = 11;

echo "{$year}年第{$week}周的每一天:<br><br>";

for ($day = 1; $day <= 7; $day++) {
    $date = date_create();
    date_isodate_set($date, $year, $week, $day);

    $dayNames = [
        1 => '星期一',
        2 => '星期二',
        3 => '星期三',
        4 => '星期四',
        5 => '星期五',
        6 => '星期六',
        7 => '星期日'
    ];

    echo "星期{$day} ({$dayNames[$day]}): " .
         date_format($date, 'Y-m-d l') .
         " (ISO: " . date_format($date, 'Y-\WW-N') . ")<br>";
}

// 特殊示例:一年的最后一周
echo "<br>特殊示例 - 一年的最后一周:<br>";
$lastWeekDates = date_create();
date_isodate_set($lastWeekDates, 2024, 53, 1); // 2024年有53周
echo "2024年第53周星期一: " . date_format($lastWeekDates, 'Y-m-d') . "<br>";

// 验证:2025年第1周
$firstWeek2025 = date_create();
date_isodate_set($firstWeek2025, 2025, 1, 1);
echo "2025年第1周星期一: " . date_format($firstWeek2025, 'Y-m-d') . "<br>";
?>

示例 3:与普通日期函数对比

<?php
// 对比 ISO 周日期和普通日期设置
echo "ISO周日期与普通日期设置对比:<br><br>";

// 使用date_isodate_set设置ISO周日期
$date1 = date_create('2024-01-01');
date_isodate_set($date1, 2024, 11, 3); // 2024年第11周星期三
echo "ISO周设置 (2024-W11-3): " . date_format($date1, 'Y-m-d') . "<br>";

// 使用date_modify设置相同日期
$date2 = date_create('2024-01-01');
date_modify($date2, '2024-03-13');
echo "普通日期设置 (2024-03-13): " . date_format($date2, 'Y-m-d') . "<br><br>";

// 显示两种方法得到相同结果
echo "两个日期是否相同: " . ($date1 == $date2 ? '是' : '否') . "<br><br>";

// 计算周数差异
function getWeekDifference($startDate, $endDate) {
    $start = date_create($startDate);
    $end = date_create($endDate);

    $startWeek = date_format($start, 'W');
    $endWeek = date_format($end, 'W');
    $startYear = date_format($start, 'Y');
    $endYear = date_format($end, 'Y');

    if ($startYear == $endYear) {
        return $endWeek - $startWeek;
    } else {
        // 简化计算:实际应用需要考虑跨年
        return "跨年计算,需要更复杂的逻辑";
    }
}

// 测试周数计算
$dateA = '2024-03-01';
$dateB = '2024-03-31';
echo "从 {$dateA} 到 {$dateB} 的周数差: " . getWeekDifference($dateA, $dateB) . " 周";
?>

示例 4:实用场景 - 周报系统

<?php
// 实用场景:周报系统
class WeeklyReportSystem {

    /**
     * 获取指定年份和周数的日期范围
     */
    public static function getWeekDateRange($year, $week) {
        $startDate = date_create();
        date_isodate_set($startDate, $year, $week, 1); // 周一

        $endDate = clone $startDate;
        date_add($endDate, date_interval_create_from_date_string('6 days')); // 周日

        return [
            'year' => $year,
            'week' => $week,
            'start' => date_format($startDate, 'Y-m-d'),
            'end' => date_format($endDate, 'Y-m-d'),
            'start_formatted' => date_format($startDate, 'Y年m月d日 (l)'),
            'end_formatted' => date_format($endDate, 'Y年m月d日 (l)'),
            'iso_format' => date_format($startDate, 'Y-\WW'),
        ];
    }

    /**
     * 获取当前周信息
     */
    public static function getCurrentWeekInfo() {
        $now = date_create();
        $year = date_format($now, 'o'); // ISO年份
        $week = date_format($now, 'W'); // ISO周数
        $day = date_format($now, 'N'); // ISO星期几(1-7)

        return self::getWeekDateRange($year, $week);
    }

    /**
     * 生成周报文件名
     */
    public static function generateWeeklyReportName($year, $week, $department = '') {
        $range = self::getWeekDateRange($year, $week);
        $name = "周报_{$year}年第{$week}周_{$range['start']}-{$range['end']}";

        if ($department) {
            $name .= "_{$department}";
        }

        $name .= '.pdf';
        return $name;
    }

    /**
     * 获取未来N周的周信息
     */
    public static function getFutureWeeks($weeksAhead = 4) {
        $result = [];
        $current = self::getCurrentWeekInfo();

        for ($i = 0; $i < $weeksAhead; $i++) {
            $year = $current['year'];
            $week = $current['week'] + $i;

            // 处理跨年
            if ($week > 53) {
                // 简化处理:实际应检查该年是否有53周
                $week = $week - 53;
                $year++;
            }

            $result[] = self::getWeekDateRange($year, $week);
        }

        return $result;
    }
}

echo "周报系统演示:<br><br>";

// 当前周信息
$currentWeek = WeeklyReportSystem::getCurrentWeekInfo();
echo "<strong>当前周信息:</strong><br>";
echo "年份: {$currentWeek['year']}<br>";
echo "周数: {$currentWeek['week']}<br>";
echo "日期范围: {$currentWeek['start']} 至 {$currentWeek['end']}<br>";
echo "格式化: {$currentWeek['start_formatted']} - {$currentWeek['end_formatted']}<br>";
echo "ISO格式: {$currentWeek['iso_format']}<br><br>";

// 特定周信息
$specificWeek = WeeklyReportSystem::getWeekDateRange(2024, 11);
echo "<strong>2024年第11周信息:</strong><br>";
echo "日期范围: {$specificWeek['start']} 至 {$specificWeek['end']}<br><br>";

// 生成周报文件名
echo "<strong>周报文件名示例:</strong><br>";
echo WeeklyReportSystem::generateWeeklyReportName(2024, 11) . "<br>";
echo WeeklyReportSystem::generateWeeklyReportName(2024, 11, '技术部') . "<br><br>";

// 未来四周信息
echo "<strong>未来四周信息:</strong><br>";
$futureWeeks = WeeklyReportSystem::getFutureWeeks(4);
foreach ($futureWeeks as $weekInfo) {
    echo "第{$weekInfo['week']}周: {$weekInfo['start']} - {$weekInfo['end']}<br>";
}
?>

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

<?php
// 错误处理和边界情况示例
function safeSetIsoDate($date, $year, $week, $day = 1) {
    // 验证参数有效性
    if ($week < 1 || $week > 53) {
        return "错误: 周数必须在1-53之间";
    }

    if ($day < 1 || $day > 7) {
        return "错误: 星期几必须在1-7之间(1=周一,7=周日)";
    }

    // 检查该年是否有53周
    if ($week == 53) {
        $lastDayOfYear = date_create("{$year}-12-31");
        $lastWeekOfYear = date_format($lastDayOfYear, 'W');

        if ($lastWeekOfYear < 53) {
            return "错误: {$year}年只有{$lastWeekOfYear}周";
        }
    }

    // 执行设置
    try {
        $result = date_isodate_set($date, $year, $week, $day);
        if ($result === false) {
            return "错误: 设置ISO日期失败";
        }

        return [
            'success' => true,
            'date' => date_format($date, 'Y-m-d'),
            'iso_format' => date_format($date, 'Y-\WW-N'),
            'day_of_week' => date_format($date, 'l'),
            'message' => "设置成功",
        ];
    } catch (Exception $e) {
        return "异常: " . $e->getMessage();
    }
}

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

$testCases = [
    ['year' => 2024, 'week' => 11, 'day' => 3, 'desc' => '正常情况'],
    ['year' => 2024, 'week' => 53, 'day' => 1, 'desc' => '第53周(2024年有53周)'],
    ['year' => 2023, 'week' => 53, 'day' => 1, 'desc' => '第53周(2023年只有52周)'],
    ['year' => 2024, 'week' => 0, 'day' => 1, 'desc' => '周数过小'],
    ['year' => 2024, 'week' => 54, 'day' => 1, 'desc' => '周数过大'],
    ['year' => 2024, 'week' => 11, 'day' => 0, 'desc' => '星期几过小'],
    ['year' => 2024, 'week' => 11, 'day' => 8, 'desc' => '星期几过大'],
];

foreach ($testCases as $test) {
    $date = date_create('2024-01-01');
    $result = safeSetIsoDate($date, $test['year'], $test['week'], $test['day']);

    echo "测试: {$test['desc']} (Y={$test['year']}, W={$test['week']}, D={$test['day']})<br>";

    if (is_array($result) && $result['success']) {
        echo "✅ 成功: {$result['date']} ({$result['iso_format']}, {$result['day_of_week']})<br>";
    } else {
        echo "❌ 失败: {$result}<br>";
    }
    echo "<br>";
}

// 显示不同年份的周数情况
echo "<strong>不同年份的周数统计:</strong><br>";
$years = [2020, 2021, 2022, 2023, 2024, 2025];

foreach ($years as $year) {
    $lastDay = date_create("{$year}-12-31");
    $weekCount = date_format($lastDay, 'W');
    $isLeapYear = date('L', strtotime("{$year}-01-01")) ? '是' : '否';

    echo "{$year}年: {$weekCount}周 (闰年: {$isLeapYear})<br>";
}

echo "<br>注意:一年有53周的条件是该年的1月1日是星期四,或者闰年时1月1日是星期三。";
?>

ISO 8601 周计算规则

规则 描述 示例
一周开始 每周从星期一开始 星期一 = 1,星期日 = 7
第一周定义 包含该年第一个星期四的那一周 2024年第一周从1月1日开始
最后一周 包含该年最后一个星期四的那一周 2024年最后一周是第53周
周数范围 一年有52或53周 平年通常52周,闰年可能53周
跨年日期 年初的几天可能属于上一年的最后一周 2024年1月1日属于2024年第1周

注意事项

重要提示:
  • ISO周从星期一开始,星期日结束(与某些地区周日开始的习惯不同)
  • 一年中的第一周是包含该年第一个星期四的那一周
  • 一年可能有52或53周,取决于该年的第一天是星期几
  • date_isodate_set() 会修改原始 DateTime 对象
  • 星期参数的范围是1-7,1表示星期一,7表示星期日
  • 周数参数的范围是1-53,但并非所有年份都有53周
  • 时间部分(时、分、秒)保持不变

常见问题

方面 ISO 8601 周 普通周
每周开始 星期一 取决于地区(周日或周一)
第一周定义 包含当年第一个星期四的周 1月1日所在的周
周数编号 1-52或53 可能从0或1开始
国际标准
应用场景 商业、国际交流、欧洲 日常使用

一年有53周的条件:

  1. 普通年份(非闰年)的1月1日是星期四
  2. 闰年的1月1日是星期三或星期四

PHP代码判断:

<?php
function has53Weeks($year) {
    $lastDay = date_create("{$year}-12-31");
    $weekNumber = date_format($lastDay, 'W');
    return $weekNumber == 53;
}

$years = [2020, 2021, 2022, 2023, 2024, 2025];
foreach ($years as $year) {
    echo "{$year}年有" . (has53Weeks($year) ? '53' : '52') . "周<br>";
}
?>

ISO周相关格式化字符

字符 描述 示例
o ISO 8601 年份 2024
W ISO 8601 周数(带前导零) 01, 11, 52
N ISO 8601 星期几(1-7) 1(周一)到 7(周日)
Y-\WW-N 完整ISO周格式 2024-W11-3
Y-\WW ISO周格式(无星期几) 2024-W11

相关函数

函数 描述
DateTime::setISODate() 面向对象的ISO周日期设置方法
date_create() 创建DateTime对象
date_format() 格式化DateTime对象
date('W') 获取ISO周数
date('o') 获取ISO年份
date('N') 获取ISO星期几
strtotime() 解析英文文本日期时间