时间与日期
CodeIgniter 提供一个完全本地化、不可变的日期/时间类,该类基于 PHP 的 DateTimeImmutable 类构建,但利用 Intl
扩展的功能实现时区转换,并以不同区域设置正确显示输出。此类为 Time 类,位于 CodeIgniter\I18n 命名空间中。
备注
自 4.3.0 起,Time 类继承 DateTimeImmutable,若需要此类未提供的功能,
可直接在 DateTimeImmutable 类中查找。
备注
4.3.0 之前,Time 类继承 DateTime,部分继承方法会更改
当前对象状态。该问题已在 4.3.0 中修复。如需向后兼容,
可暂时使用已弃用的 TimeLegacy 类。
实例化
创建 Time 实例有多种方式。最简单的方式是直接像普通类一样实例化。
实例化时可传入表示所需时间的字符串。该字符串可为 PHP DateTimeImmutable 构造函数能解析的任意格式。详见 支持的日期和时间格式。
<?php
use CodeIgniter\I18n\Time;
$myTime = new Time('2024-01-01');
$myTime = new Time('2024-01-01 12:00:00');
$myTime = new Time('now');
$myTime = new Time('+3 week');
第二和第三个参数可分别传入时区和区域设置字符串。时区 可为 PHP DateTimeZone 类支持的所有值。区域设置可为 PHP Locale 类支持的所有值。如未提供区域设置或时区, 将使用应用默认值。
<?php
use CodeIgniter\I18n\Time;
$myTime = new Time('now', 'America/Chicago', 'en_US');
now()
Time 类提供多个辅助方法来实例化。首先是 now() 方法,
返回一个当前时间的新实例。第二和第三个参数可分别传入时区和区域设置字符串。如未提供时区或区域设置,
将使用应用默认值。
<?php
use CodeIgniter\I18n\Time;
$myTime = Time::now('America/Chicago', 'en_US');
parse()
此辅助方法为默认构造方法的静态版本。第一个参数为 DateTimeImmutable 构造函数可接受的字符串,第二个参数为时区,第三个参数为区域设置:
<?php
use CodeIgniter\I18n\Time;
$myTime = Time::parse('next Tuesday', 'America/Chicago', 'en_US');
today()
返回新实例,日期设为当前日期,时间设为午夜。第一和第二个参数可接受时区和区域设置字符串:
<?php
use CodeIgniter\I18n\Time;
$myTime = Time::today('America/Chicago', 'en_US');
yesterday()
返回新实例,日期设为昨天的日期,时间设为午夜。第一和第二个参数可接受时区和区域设置字符串:
<?php
use CodeIgniter\I18n\Time;
$myTime = Time::yesterday('America/Chicago', 'en_US');
tomorrow()
返回新实例,日期设为明天的日期,时间设为午夜。第一和第二个参数可接受时区和区域设置字符串:
<?php
use CodeIgniter\I18n\Time;
$myTime = Time::tomorrow('America/Chicago', 'en_US');
createFromDate()
分别传入 年、月、日,返回新实例。如省略任一参数, 将使用当前的年、月和日。第四和第五个参数可接受时区和区域设置字符串:
<?php
use CodeIgniter\I18n\Time;
$today = Time::createFromDate(); // Uses current year, month, and day
$anniversary = Time::createFromDate(2018); // Uses current month and day
$date = Time::createFromDate(2018, 3, 15, 'America/Chicago', 'en_US');
createFromTime()
与 createFromDate() 类似,仅处理 时、分、秒。日期部分使用
当前日期。第四和第五个参数可接受时区和区域设置字符串:
<?php
use CodeIgniter\I18n\Time;
$lunch = Time::createFromTime(11, 30); // 11:30 am today
$dinner = Time::createFromTime(18, 00, 00); // 6:00 pm today
$time = Time::createFromTime($hour, $minutes, $seconds, $timezone, $locale);
create()
结合上述两个方法,分别接受 年、月、日、时、分、秒 作为独立参数。任何未提供的值将使用当前日期和时间。第七和第八个参数可接受时区和区域设置字符串:
<?php
use CodeIgniter\I18n\Time;
$time = Time::create($year, $month, $day, $hour, $minutes, $seconds, $timezone, $locale);
createFromFormat()
此方法为 DateTimeImmutable 同名方法的替代版本。可同时设置时区,
并返回 Time 实例而非 DateTimeImmutable:
<?php
use CodeIgniter\I18n\Time;
$time = Time::createFromFormat('j-M-Y', '15-Feb-2009', 'America/Chicago');
createFromTimestamp()
此方法接受 UNIX 时间戳,以及可选的时区和区域设置, 创建新 Time 实例:
<?php
use CodeIgniter\I18n\Time;
$time = Time::createFromTimestamp(1501821586, 'America/Chicago', 'en_US');
如未显式传入时区,返回 UTC 时区的 Time 实例。
备注
建议始终传入两个参数调用 createFromTimestamp()
(即显式传入时区),除非默认使用 UTC 时区。
备注
4.4.6 至 4.6.0 之前的版本中,如未指定时区,此方法返回 默认时区的 Time 实例。
createFromInstance()
使用其他库提供的 DateTime 实例时,可使用方法将 DateTime 实例 转换为 Time 实例,并可选设置区域设置。时区将从传入的 DateTime 实例自动推断:
<?php
use CodeIgniter\I18n\Time;
$dt = new \DateTime('now');
$time = Time::createFromInstance($dt, 'en_US');
toDateTime()
此方法非实例化方法,而是与 instance 方法相反,将 Time 实例转换为 DateTime 实例。转换保留时区设置,但丢失区域设置,因为 DateTime 不支持区域设置:
<?php
use CodeIgniter\I18n\Time;
$datetime = Time::toDateTime();
显示值
Time 类继承自 DateTimeImmutable,因此可直接使用该类提供的所有输出方法,包括 format() 方法。
但 DateTimeImmutable 的方法不提供本地化输出。Time 类提供多个辅助方法
用于显示本地化值。
toLocalizedString()
此方法为 DateTimeImmutable format() 方法的本地化版本。不同于熟悉的格式化字符,
必须使用 IntlDateFormatter 类可接受的值。
完整值列表见 此处。
<?php
use CodeIgniter\I18n\Time;
// Locale: en
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toLocalizedString('MMM d, yyyy'); // March 9, 2016
// Locale: fa
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toLocalizedString('MMM d, yyyy'); // مارس ۹, ۲۰۱۶
toDateTimeString()
此为三个辅助方法中的第一个,无需记忆 IntlDateFormatter 的格式化值。
返回格式化为 Y-m-d H:i:s 的本地化字符串:
<?php
use CodeIgniter\I18n\Time;
// Locale: en
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toDateTimeString(); // 2016-03-09 12:00:00
// Locale: fa
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toDateTimeString(); // ۲۰۱۶-۰۳-۰۹ ۱۲:۰۰:۰۰
toDateString()
仅显示 Time 的本地化日期部分:
<?php
use CodeIgniter\I18n\Time;
// Locale: en
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toDateString(); // 2016-03-09
// Locale: fa
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toDateString(); // ۲۰۱۶-۰۳-۰۹
toTimeString()
仅显示值的本地化时间部分:
<?php
use CodeIgniter\I18n\Time;
// Locale: en
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toTimeString(); // 12:00:00
// Locale: fa
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->toTimeString(); // ۱۲:۰۰:۰۰
humanize()
此方法返回一个字符串,以人类可读格式显示当前日期/时间与实例之间的差异, 便于快速理解。可生成类似"3 小时前"、"1 个月后"等字符串:
<?php
use CodeIgniter\I18n\Time;
// Assume current time is: March 10, 2017 (America/Chicago)
$time = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
echo $time->humanize(); // 1 year ago
显示时间的具体规则如下:
时间差 |
结果 |
|---|---|
1 年 < $time < 2 年 |
in 1 year / 1 year ago |
1 个月 < $time < 1 年 |
in 6 months / 6 months ago |
7 天 < $time < 1 个月 |
in 3 weeks / 3 weeks ago |
今天 < $time < 7 天 |
in 4 days / 4 days ago |
$time == 昨天 / 明天 |
Tomorrow / Yesterday |
59 分钟 < $time < 1 天 |
in 2 hours / 2 hours ago |
现在 < $time < 1 小时 |
in 35 minutes / 35 minutes ago |
$time == 现在 |
Now |
结果字符串来自语言文件 system/Language/en/Time.php。 如需覆盖,创建 app/Language/{locale}/Time.php。
处理单个值
Time 对象提供多种方法,用于获取和设置现有实例的单个值,如年、月、小时等。 通过以下方法获取的所有值都会完全本地化,并遵守 创建 Time 实例时指定的区域设置。
以下所有 getX() 和 setX() 方法均可像类属性一样使用。例如,getYear() 可通过 $time->year 访问,以此类推。
Getter 方法
以下为基础 getter 方法:
<?php
use CodeIgniter\I18n\Time;
$time = Time::parse('August 12, 2016 4:15:23pm');
// The output may vary based on locale.
echo $time->getYear(); // '2016'
echo $time->getMonth(); // '8'
echo $time->getDay(); // '12'
echo $time->getHour(); // '16'
echo $time->getMinute(); // '15'
echo $time->getSecond(); // '23'
echo $time->year; // '2016'
echo $time->month; // '8'
echo $time->day; // '12'
echo $time->hour; // '16'
echo $time->minute; // '15'
echo $time->second; // '23'
此外,还提供多个方法获取关于日期的额外信息:
<?php
use CodeIgniter\I18n\Time;
$time = Time::parse('August 12, 2016 4:15:23pm');
// The output may vary based on locale.
echo $time->getDayOfWeek(); // '6'
echo $time->getDayOfYear(); // '225'
echo $time->getWeekOfMonth(); // '2'
echo $time->getWeekOfYear(); // '33'
echo $time->getTimestamp(); // 1471018523 - UNIX timestamp (locale independent)
echo $time->getQuarter(); // '3'
echo $time->dayOfWeek; // '6'
echo $time->dayOfYear; // '225'
echo $time->weekOfMonth; // '2'
echo $time->weekOfYear; // '33'
echo $time->timestamp; // 1471018523
echo $time->quarter; // '3'
getAge()
返回 Time 实例与当前时间之间的年龄(以年为单位)。适用于根据生日 计算年龄的场景:
<?php
use CodeIgniter\I18n\Time;
$time = Time::parse('5 years ago');
echo $time->getAge(); // 5
echo $time->age; // 5
getDST()
返回布尔值,表示 Time 实例当前是否处于夏令时:
<?php
use CodeIgniter\I18n\Time;
echo Time::createFromDate(2012, 1, 1)->getDst(); // false
echo Time::createFromDate(2012, 9, 1)->dst; // true
getLocal()
返回布尔值,表示 Time 实例是否与应用当前运行的时区相同:
<?php
use CodeIgniter\I18n\Time;
echo Time::now()->getLocal(); // true
echo Time::now('Europe/London')->local; // false
getUtc()
返回布尔值,表示 Time 实例是否处于 UTC 时间:
<?php
use CodeIgniter\I18n\Time;
echo Time::now('America/Chicago')->getUtc(); // false
echo Time::now('UTC')->utc; // true
getTimezone()
返回新 DateTimeZone 对象,时区设为 Time 实例的时区:
<?php
use CodeIgniter\I18n\Time;
$tz = Time::now()->getTimezone();
$tz = Time::now()->timezone;
echo $tz->getName();
echo $tz->getOffset();
getTimezoneName()
返回 Time 实例的完整 时区字符串:
<?php
use CodeIgniter\I18n\Time;
echo Time::now('America/Chicago')->getTimezoneName(); // America/Chicago
echo Time::now('Europe/London')->timezoneName; // Europe/London
Setter 方法
以下为基础 setter 方法。如设置的值超出范围,将抛出 InvalidArgumentExeption。
备注
所有 setter 均返回新的 Time 实例,原始实例保持不变。
备注
如值超出范围,所有 setter 均会抛出 InvalidArgumentException。
<?php
$time = $time->setYear(2017);
$time = $time->setMonth(4); // April
$time = $time->setMonth('April');
$time = $time->setMonth('Feb'); // February
$time = $time->setDay(25);
$time = $time->setHour(14); // 2:00 pm
$time = $time->setMinute(30);
$time = $time->setSecond(54);
setTimezone()
将时间从当前时区转换为新时区:
<?php
use CodeIgniter\I18n\Time;
$time = Time::parse('13 May 2020 10:00', 'America/Chicago');
$time2 = $time->setTimezone('Europe/London'); // Returns new instance converted to new timezone
echo $time->getTimezoneName(); // American/Chicago
echo $time2->getTimezoneName(); // Europe/London
echo $time->toDateTimeString(); // 2020-05-13 10:00:00
echo $time2->toDateTimeString(); // 2020-05-13 18:00:00
setTimestamp()
返回新实例,日期设为新时间戳:
<?php
use CodeIgniter\I18n\Time;
// The Application Timezone is "America/Chicago".
$time = Time::parse('May 10, 2017');
$time2 = $time->setTimestamp(strtotime('April 1, 2017'));
echo $time->toDateTimeString(); // 2017-05-10 00:00:00
echo $time2->toDateTimeString(); // 2017-04-01 00:00:00
备注
4.6.0 之前的版本中,由于 bug,此方法可能返回错误的 日期/时间。详见 升级指南。
修改值
以下方法支持通过增加或减少值来修改当前 Time 的日期。不会 修改现有 Time 实例,而是返回新实例。
<?php
$time = $time->addSeconds(23);
$time = $time->addMinutes(15);
$time = $time->addHours(12);
$time = $time->addDays(21);
$time = $time->addMonths(14);
$time = $time->addCalendarMonths(2);
$time = $time->addYears(5);
$time = $time->subSeconds(23);
$time = $time->subMinutes(15);
$time = $time->subHours(12);
$time = $time->subDays(21);
$time = $time->subMonths(14);
$time = $time->subCalendarMonths(2);
$time = $time->subYears(5);
addCalendarMonths() / subCalendarMonths()
通过增加或减少完整的日历月份来修改当前 Time。若需确保重复发生的日期不跳过任何日历月份,这些方法将非常有用。下表对比了初始日期为 2025-01-31 时,addMonths() 与 addCalendarMonths() 的差异。
$months |
addMonths() |
addCalendarMonths() |
|---|---|---|
1 |
2025-03-03 |
2025-02-28 |
2 |
2025-03-31 |
2025-03-31 |
3 |
2025-05-01 |
2025-04-30 |
4 |
2025-05-31 |
2025-05-31 |
5 |
2025-07-01 |
2025-06-30 |
6 |
2025-07-31 |
2025-07-31 |
比较两个时间
以下方法允许比较两个 Time 实例。所有比较均先转换为 UTC 后进行,以确保不同时区能正确响应。
equals()
判断传入的日期/时间是否等于当前实例。此处的"等于"表示两者表示同一 时刻,不要求处于相同时区,因为两个时间均转换为 UTC 后 进行比较:
<?php
use CodeIgniter\I18n\Time;
$time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago');
$time2 = Time::parse('January 11, 2017 03:50:00', 'Europe/London');
$time1->equals($time2); // true
被测试的值可为 Time 实例、DateTime 实例或完整的日期时间 字符串(格式需能被新 DateTime 实例解析)。第一个参数传入字符串时,可传入 时区字符串作为第二个参数。如未提供时区,将使用系统默认值:
<?php
$time1->equals('January 11, 2017 03:50:00', 'Europe/London'); // true
sameAs()
此方法与 equals() 类似,但仅当日期、时间与时区
全部相同时才返回 true:
<?php
use CodeIgniter\I18n\Time;
$time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago');
$time2 = Time::parse('January 11, 2017 03:50:00', 'Europe/London');
$time1->sameAs($time2); // false
$time2->sameAs('January 10, 2017 21:50:00', 'America/Chicago'); // true
isBefore()
检查传入的时间是否在当前实例之前。比较基于 两个时间的 UTC 版本进行:
<?php
use CodeIgniter\I18n\Time;
$time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago');
$time2 = Time::parse('January 11, 2017 03:50:00', 'America/Chicago');
$time1->isBefore($time2); // true
$time2->isBefore($time1); // false
被测试的值可为 Time 实例、DateTime 实例或完整的日期时间 字符串(格式需能被新 DateTime 实例解析)。第一个参数传入字符串时,可传入 时区字符串作为第二个参数。如未提供时区,将使用系统默认值:
<?php
$time1->isBefore('March 15, 2013', 'America/Chicago'); // false
isAfter()
与 isBefore() 工作方式相同,检查时间是否在传入时间之后:
<?php
use CodeIgniter\I18n\Time;
$time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago');
$time2 = Time::parse('January 11, 2017 03:50:00', 'America/Chicago');
$time1->isAfter($time2); // false
$time2->isAfter($time1); // true
isPast()
Added in version 4.7.0.
用于判断当前实例的时间相对于“现在”是否为过去。 返回布尔值 true/false:
.. literalinclude:: time/043.php
isFuture()
Added in version 4.7.0.
用于判断当前实例的时间相对于“现在”是否为未来。 返回布尔值 true/false:
.. literalinclude:: time/044.php
查看差异
直接比较两个 Time,应使用 difference() 方法,返回 CodeIgniter\I18n\TimeDifference
实例。
第一个参数可为 Time 实例、DateTime 实例或日期/时间字符串。如 第一个参数传入字符串,第二个参数可为时区字符串:
<?php
use CodeIgniter\I18n\Time;
$time = Time::parse('March 10, 2017', 'America/Chicago');
$diff = $time->difference(Time::now());
$diff = $time->difference(new \DateTime('July 4, 1975', 'America/Chicago'));
$diff = $time->difference('July 4, 1975 13:32:05', 'America/Chicago');
获取 TimeDifference 实例后,可使用多种方法获取两个时间 之间的差异信息。如果相对于原始时间为过去则返回负值,为未来则返回正值:
<?php
use CodeIgniter\I18n\Time;
$current = Time::parse('March 10, 2017', 'America/Chicago');
$test = Time::parse('March 10, 2010', 'America/Chicago');
$diff = $current->difference($test);
echo $diff->getYears(); // -7
echo $diff->getMonths(); // -84
echo $diff->getWeeks(); // -365
echo $diff->getDays(); // -2557
echo $diff->getHours(); // -61368
echo $diff->getMinutes(); // -3682080
echo $diff->getSeconds(); // -220924800
备注
4.4.7 之前的版本中,Time 类在比较前总是先将时区转换为 UTC。 如果日期因夏令时(DST)导致时长并非 24 小时, 此类转换可能会导致非预期结果。
4.4.7 起,比较相同时区的日期/时间时, 直接进行比较,不转换为 UTC。
<?php use CodeIgniter\I18n\Time; // 31 Mar 2024 - Daylight Saving Time Starts $current = Time::parse('2024-03-31', 'Europe/Madrid'); $test = Time::parse('2024-04-01', 'Europe/Madrid'); $diff = $current->difference($test); echo $diff->getDays(); // 0 in v4.4.6 or before // 1 in v4.4.7 or later
可使用 getX() 方法,或像访问属性一样访问计算值:
<?php
echo $diff->years; // -7
echo $diff->months; // -84
echo $diff->weeks; // -365
echo $diff->days; // -2557
echo $diff->hours; // -61368
echo $diff->minutes; // -3682080
echo $diff->seconds; // -220924800
humanize()
与 Time 的 humanize() 方法类似,返回人类可读格式的字符串,
显示两个时间之间的差异,便于快速理解。可生成类似"3 小时前"、
"1 个月后"等字符串。最大区别在于处理非常近的日期时:
<?php
use CodeIgniter\I18n\Time;
$current = Time::parse('March 10, 2017', 'America/Chicago');
$test = Time::parse('March 9, 2016 12:00:00', 'America/Chicago');
$diff = $current->difference($test);
echo $diff->humanize(); // 1 year ago
显示时间的具体规则如下:
时间差 |
结果 |
|---|---|
1 年 < $time < 2 年 |
in 1 year / 1 year ago |
1 个月 < $time < 1 年 |
in 6 months / 6 months ago |
7 天 < $time < 1 个月 |
in 3 weeks / 3 weeks ago |
今天 < $time < 7 天 |
in 4 days / 4 days ago |
1 小时 < $time < 1 天 |
in 8 hours / 8 hours ago |
1 分钟 < $time < 1 小时 |
in 35 minutes / 35 minutes ago |
$time < 1 分钟 |
Now |
结果字符串来自语言文件 system/Language/en/Time.php。 如需覆盖,创建 app/Language/{locale}/Time.php。