时间与日期

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