时间与日期

CodeIgniter 提供了一个完全本地化、不可变的日期/时间类,该类基于 PHP 的 DateTimeImmutable 类构建,但利用 Intl 扩展的功能来转换时区时间,并针对不同区域设置正确显示输出。这个类就是 Time 类,位于 CodeIgniter\I18n 命名空间。

备注

由于 Time 类继承自 DateTimeImmutable,如果你需要本类未提供的功能,可以在 DateTimeImmutable 类本身中找到这些功能。

备注

在 v4.3.0 之前,Time 类继承自 DateTime,某些继承的方法会改变当前对象状态。此问题已在 v4.3.0 中修复。如果需要向后兼容旧的 Time 类,可以暂时使用已弃用的 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(),但仅关注 小时分钟。使用当前日期作为 Time 实例的日期部分。第四个和第五个参数接受时区和区域设置的字符串:

<?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 实例。

备注

我们建议始终使用 2 个参数调用 createFromTimestamp() (即显式传递时区),除非使用 UTC 作为默认时区。

备注

在 v4.4.6 到 v4.6.0 之前,当未指定时区时,此方法返回具有默认时区的 Time 实例。

createFromInstance()

当处理其他提供 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()

仅显示值的本地化日期部分:

<?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 hours ago”、”in 1 month” 等字符串:

<?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 实例当前是否处于夏令时返回布尔值 true/false:

<?php

use CodeIgniter\I18n\Time;

echo Time::createFromDate(2012, 1, 1)->getDst(); // false
echo Time::createFromDate(2012, 9, 1)->dst;      // true

getLocal()

如果 Time 实例与时区与应用程序当前运行时区相同,则返回布尔值 true:

<?php

use CodeIgniter\I18n\Time;

echo Time::now()->getLocal();           // true
echo Time::now('Europe/London')->local; // false

getUtc()

如果 Time 实例处于 UTC 时间,则返回布尔值 true:

<?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

备注

在 v4.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->addYears(5);

$time = $time->subSeconds(23);
$time = $time->subMinutes(15);
$time = $time->subHours(12);
$time = $time->subDays(21);
$time = $time->subMonths(14);
$time = $time->subYears(5);

比较两个时间

以下方法允许你将一个 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

查看差异

要直接比较两个 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

备注

在 v4.4.7 之前,Time 始终在比较前将时区转换为 UTC。当包含因夏令时 (DST) 导致天数不同于 24 小时的情况时,可能导致意外结果。

从 v4.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 hours ago”、”in 1 month” 等字符串。主要区别在于处理最近日期的方式:

<?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