时间和日期

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

use CodeIgniter\I18n\Time;

$myTime = new Time('+3 week');
$myTime = new Time('now');

你可以在第二个和第三个参数中分别传递表示时区和语言环境的字符串。时区可以是 PHP 的 DateTimeZone 类支持的任何时区。语言环境可以是 PHP 的 Locale 类支持的任何语言环境。如果没有提供语言环境或时区,将使用应用程序默认值。

<?php

$myTime = new Time('now', 'America/Chicago', 'en_US');

now()

Time 类有几个帮助方法来实例化该类。第一个是 now() 方法,它返回一个设置为当前时间的新实例。你可以在第二个和第三个参数中分别传递表示时区和语言环境的字符串。如果没有提供语言环境或时区,将使用应用程序默认值。

<?php

$myTime = Time::now('America/Chicago', 'en_US');

parse()

这个辅助方法是默认构造函数的静态版本。它以 DateTimeImmutable 构造函数可以接受的字符串作为第一个参数,时区作为第二个参数,语言环境作为第三个参数:

<?php

$myTime = Time::parse('next Tuesday', 'America/Chicago', 'en_US');

today()

返回一个新的实例,日期设置为当前日期,时间设置为午夜。它在第一个和第二个参数中接受时区和语言环境的字符串:

<?php

$myTime = Time::today('America/Chicago', 'en_US');

yesterday()

返回一个新的实例,日期设置为昨天的日期,时间设置为午夜。它在第一个和第二个参数中接受时区和语言环境的字符串:

<?php

$myTime = Time::yesterday('America/Chicago', 'en_US');

tomorrow()

返回一个新的实例,日期设置为明天的日期,时间设置为午夜。它在第一个和第二个参数中接受时区和语言环境的字符串:

<?php

$myTime = Time::tomorrow('America/Chicago', 'en_US');

createFromDate()

给定独立的 yearmonthday 输入,将返回一个新的实例。如果这些参数中的任何一个未提供,它将使用当前值来填充它。在第四个和第五个参数中接受时区和语言环境的字符串:

<?php

$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

$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()

前两个方法的组合,以 yearmonthdayhourminutesseconds 作为单独的参数。未提供的值将使用当前日期和时间来确定。在第四个和第五个参数中接受时区和语言环境的字符串:

<?php

$time = Time::create($year, $month, $day, $hour, $minutes, $seconds, $timezone, $locale);

createFromFormat()

这是 DateTimeImmutable 同名方法的替代方法。这允许同时设置时区,并返回一个 Time 实例,而不是 DateTimeImmutable:

<?php

$time = Time::createFromFormat('j-M-Y', '15-Feb-2009', 'America/Chicago');

createFromTimestamp()

该方法获取一个 UNIX 时间戳和可选的时区和语言环境来创建一个新的 Time 实例:

<?php

$time = Time::createFromTimestamp(1501821586, 'America/Chicago', 'en_US');

createFromInstance()

当使用提供 DateTime 实例的其他库时,你可以使用此方法将其转换为 Time 实例,可选设置语言环境。时区将自动从传入的 DateTime 实例中确定:

<?php

$dt   = new DateTime('now');
$time = Time::createFromInstance($dt, 'en_US');

toDateTime()

虽然不是一个实例化方法,但此方法与 instance 方法相反,允许你将 Time 实例转换为 DateTime 实例。这会保留时区设置,但会丢失语言环境,因为 DateTime 不知道语言环境:

<?php

$datetime = Time::toDateTime();

显示值

由于 Time 类扩展了 DateTimeImmutable,因此你可以获得它提供的所有输出方法,包括 format() 方法。 然而,DateTimeImmutable 方法不提供本地化的结果。然而,Time 类确实提供了一些帮助方法来显示值的本地化版本。

toLocalizedString()

这是 DateTimeImmutable 的 format() 方法的本地化版本。但是,与你可能熟悉的值不同,你必须使用 IntlDateFormatter 类可以接受的值。 可以在 这里 找到值的完整列表。

<?php

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

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

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

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

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

显示的确切时间如下确定:

时间差

结果

$time > 1年 且 < 2年

1年内/1年前

$time > 1月 且 < 1年

6个月内/6个月前

$time > 7天 且 < 1月

3周内/3周前

$time > 今天 且 < 7天

4天内/4天前

$time == 明天/昨天

明天/昨天

$time > 59分钟 且 < 1天

2小时内/2小时前

$time > 现在 且 < 1小时

35分钟内/35分钟前

$time == 现在

现在

确切使用的语言通过语言文件 Time.php 控制。

使用单个值

Time 对象提供了许多方法来获取和设置现有实例的各个项,如年、月、小时等。通过以下方法检索到的所有值都将完全本地化,并尊重创建 Time 实例时使用的语言环境。

以下所有 getX()setX() 方法也可以当作类属性使用。所以,对像 getYear() 这样的方法调用也可以通过 $time->year 访问,以此类推。

获取器

存在以下基本获取器:

<?php

$time = Time::parse('August 12, 2016 4:15:23pm');

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

$time = Time::parse('August 12, 2016 4:15:23pm');

echo $time->getDayOfWeek();   // 6 - but may vary based on locale's starting day of the week
echo $time->getDayOfYear();   // 225
echo $time->getWeekOfMonth(); // 2
echo $time->getWeekOfYear();  // 33
echo $time->getTimestamp();   // 1471018523 - UNIX timestamp
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

$time = Time::parse('5 years ago');

echo $time->getAge(); // 5
echo $time->age;      // 5

getDST()

根据 Time 实例是否当前正在观察夏令时返回 boolean true/false:

<?php

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

getLocal()

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

<?php

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

getUtc()

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

<?php

echo Time::now('America/Chicago')->getUtc(); // false
echo Time::now('UTC')->utc;                  // true

getTimezone()

返回一个新的 DateTimeZone 对象,将时区设置为 Time 实例的时区:

<?php

$tz = Time::now()->getTimezone();
$tz = Time::now()->timezone;

echo $tz->getName();
echo $tz->getOffset();

getTimezoneName()

返回 Time 实例的完整 时区字符串:

<?php

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

$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

$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

$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

$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

$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

$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

$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

$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

你可以使用 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

$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

显示的确切时间如下确定:

时间差

结果

$time > 1年 且 < 2年

1年内/1年前

$time > 1月 且 < 1年

6个月内/6个月前

$time > 7天 且 < 1月

3周内/3周前

$time > 今天 且 < 7天

4天内/4天前

$time > 1小时 且 < 1天

8小时内/8小时前

$time > 1分钟 且 < 1小时

35分钟内/35分钟前

$time < 1分钟

现在

确切使用的语言通过语言文件 Time.php 控制。