时间和日期

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

备注

由于 Time 类扩展了 DateTimeImmutable,如果你需要的功能该类没有提供,你很可能可以在 DateTimeImmutable 类中找到它们。

备注

在 v4.3.0 之前,Time 类扩展了 DateTime,并且一些继承的方法改变了当前对象状态。这个 bug 在 v4.3.0 中修复了。如果你需要旧的 Time 类用于向后兼容,你可以暂时使用已弃用的 TimeLegacy 类。

实例化

有几种方法可以创建新的 Time 实例。第一种就是像任何其他类一样简单地创建一个新实例。

当你以这种方式执行时,你可以传递一个表示所需时间的字符串。这可以是 PHP 的 strtotime() 函数可以解析的任何字符串:

当你以这种方式执行时,你可以传入一个表示所需时间的字符串。这个字符串可以是 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(),只关心 hoursminutesseconds。使用当前日期作为 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');

备注

由于一个 bug,在 v4.4.6 之前的版本中,当你没有指定时区时,该方法返回的是 UTC 时区的 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()

仅显示 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 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 访问,以此类推。

获取器

存在以下基本获取器:

<?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 实例是否当前正在观察夏令时返回 boolean 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 实例与应用程序当前运行的时区相同,则返回 boolean true:

<?php

use CodeIgniter\I18n\Time;

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

getUtc()

如果 Time 实例处于 UTC 时间,则返回 boolean 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

设置器

存在以下基本设置器。如果设置的值超出范围,将抛出 InvalidArgumentExeption

备注

所有设置器都将返回一个新的 Time 实例,保留原始实例不变。

备注

如果值超出范围,所有设置器都会抛出 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;

$time  = Time::parse('May 10, 2017', 'America/Chicago');
$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

修改值

以下方法允许你通过添加或减去当前 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