每个人都必须在编程的某个时刻处理日期/时间格式、时区、奇怪的时间格式等。PHP几乎没有方法来处理这些问题,其中最值得注意的是PHP内置DateTime
类。这是因为DateTime
类充当所有这些问题的多合一解决方案。
但是,DateTime
类有一个缺陷:如果您还不熟悉它,使用它可能会令人困惑。
这篇文章是为了帮助那些想了解更多关于DateTime
课程和/或发现自己问以下问题之一的人:
- 如何将字符串转换为可修改的日期/时间?
- 如何格式化日期/时间字符串?
- 我如何获得 2 次之间的差异?
- 如何考虑时区?
请注意:
这篇文章不会讨论date()
、time()
、strtotime()
或其任何相关功能的使用。这篇文章纯粹是为了解释 PHP 中DateTime
及其相关类的正确用法。虽然其中许多答案也可以通过date()
及其相关函数来实现,但DateTime
将所有这些功能包装到几个干净的类中;这可以使其作为一个整体更容易理解。
以下大部分信息都可以从 PHP 的DateTime
类文档的各个部分获得。但是,此答案的格式应回答大多数人有关DateTime
类的问题,并且应适用于其大多数用例。
如果您正在尝试使用日期/时间执行更高级的操作,例如创建DateTime
包装器、使用不可变的DateTime
实例或其他特定于您的应用程序需求的内容,我强烈建议您查看完整的日期和时间文档。
1. 如何将字符串转换为可修改的日期/时间?
编程中最难做的事情之一是尝试使最终用户输入可用。然而,当涉及到日期和时间时,DateTime
课程使这几乎是儿戏。
如何
DateTime
的构造函数使用了一个强大的解析器,它接受最广为人知的格式,包括相对格式。
$datetime = new DateTime($datetime_string);
从那里,您可以使用以下任一方法来修改时间:
$datetime->modify()
- 更改时间戳(适用于相对格式!$datetime->add()
- 向 DateTime 对象添加天数、月、年、小时、分钟和秒的数量$datetime->sub()
- 从 DateTime 对象中减去天数、月数、年数、小时数、分钟数和秒数$datetime->setDate()
- 设置日期$datetime->setISODate()
- 设置 ISO 日期$datetime->setTime()
- 设置时间$datetime->setTimestamp()
- 根据 Unix 时间戳设置日期和时间(非常适合处理跨时区的绝对时间!
要查看DateTime::__construct()
支持的格式的完整列表,请查看:支持的日期和时间格式。
示例 - 解释最终用户输入
假设您有允许用户说出他们想要预约的日期的表单,但此输入不是具有强制格式的日期选择器,而是纯文本输入。
典型的最终用户会在该输入中放入类似的东西,当被要求支持它时,典型的程序员会通过以下方式做出响应:
12/31/2000
- "确定">2000-12-31
- "当然">Today
- "嗯,我想我们可以支持吗?">Tomorrow
- "我想我们也应该支持这一点。wednesday next week
- "不。">
过了一会儿,你要么强制使用特定的格式(无论如何你都应该这样做),要么为你糟糕的表单设计而哭泣。但是,DateTime
允许所有这些都作为有效的输入,并完美地解释它们。
// 2000-12-31 00:00:00.000000
new DateTime('12/31/2000');
// 2000-12-31 00:00:00.000000
new DateTime('2000-12-31');
// 2000-12-31 00:00:00.000000
new DateTime('Today');
// 2001-01-01 00:00:00.000000
new DateTime('Tomorrow');
// 2001-01-03 00:00:00.000000
new DateTime('wednesday next week');
但是,像大多数事情一样,DateTime
类并不完美,并且不支持每种格式。这就是为什么您应该始终将try ... catch
块与DateTime
一起使用,并与最终用户确认您解释的日期是最终用户想要的。一个很好的例子是欧洲日期格式:
try {
new DateTime('31/12/2000');
} catch (Exception $e) {
echo $e->getMessage();
}
输出:
DateTime::__construct(): Failed to parse time string (31/12/2000) at position 0 (3): Unexpected character
示例 - 修改日期/时间
您可以使用$datetime->modify()
方法轻松调整任何日期/时间:
$datetime = new DateTime('2001-01-01');
// 2001-01-04 00:00:00.000000
$datetime->modify('+3 days');
// 2001-02-04 00:00:00.000000
$datetime->modify('+1 month');
// 2001-02-03 23:59:00.000000
$datetime->modify('-60 seconds');
// 2001-02-02 00:00:00.000000
$datetime->modify('yesterday');
// 2001-02-02 18:00:00.000000
$datetime->modify('6pm');
$datetime->modify()
方法是修改任何DateTime
实例的最简单方法。
但是,由于解析,它的效率有些低下。如果要修改 1000 个日期/时间并且需要更好的性能,请使用add()
、sub()
、setDate()
、setISODate()
、setTime()
和setTimestamp()
而不是modify()
。
$datetime = new DateTime('2001-01-01');
// 2001-06-01 00:00:00.000000
$datetime->setDate(2001, 6, 1);
// 2001-06-01 06:30:00.000000
$datetime->setTime(6, 30, 0);
// No sane person should ever do the below when they could just add 10,000
// seconds, but it's a good way to test how fast your system will handle
// updating DateTime.
$timestamp = $datetime->getTimestamp();
foreach (range(1, 10000) as $i) {
$timestamp++;
$datetime->setTimestamp($timestamp);
}
// 2001-06-01 09:16:40.000000
2. 如何格式化日期/时间字符串?
通常,您需要获取 1 个日期/时间字符串并将其格式化为另一个日期/时间字符串,甚至只是获取现有日期/时间并更新它。DateTime
类也使这变得简单。
如何
DateTime
具有方法format()
,该方法以格式化字符串的形式返回日期/时间。
$datetime = new DateTime;
$format = 'Y-m-d H:i:s';
echo $datetime->format($format);
我们只使用这些示例中可用的一小部分格式选项,因此我强烈建议您查看有关格式化日期/时间的文档以及预定义的 DateTime 常量。
注意:请注意,如果您尝试转义可能是 PHP 字符串转义字符的字符,您可能会得到意外的结果。
不正确的结果
// output: Da e 2000-12-31
echo $datetime->format("Date: Y-m-d").PHP_EOL;
正确的结果
// output: Date 2000-12-31
echo $datetime->format("Da\te: Y-m-d").PHP_EOL;
// output: Date 2000-12-31
echo $datetime->format('Date: Y-m-d').PHP_EOL;
例子
以下是您可能需要的一些常见格式:
SQL 日期/时间
// output: 2000-12-31
echo $datetime->format('Y-m-d').PHP_EOL;
// output: 23:59:59
echo $datetime->format('H:i:s').PHP_EOL;
// output: 2000-12-31 23:59:59
echo $datetime->format('Y-m-d H:i:s').PHP_EOL;
最终用户可读日期/时间
// output: 12/31/2000
echo $datetime->format('n/j/Y').PHP_EOL;
// output: 11:59pm
echo $datetime->format('g:ia').PHP_EOL;
// output: 12/31/2000 at 11:59pm
echo $datetime->format('n/j/Y at g:ia').PHP_EOL;
// output: Sunday the 31st of December 2000 at 11:59:59 PM
echo $datetime->format('l the jS of F Y at g:i:s A').PHP_EOL;
具有时区的日期/时间
date_default_timezone_set('America/New_York');
$datetime = new DateTime('2000-12-31 23:59:59');
// output: 2000-12-31 23:59:59 America/New_York
echo $datetime->format('Y-m-d H:i:s e').PHP_EOL;
// output: 2000-12-31 23:59:59 EST
echo $datetime->format('Y-m-d H:i:s T').PHP_EOL;
// output: 2000-12-31 23:59:59 -0500
echo $datetime->format('Y-m-d H:i:s O').PHP_EOL;
3. 我如何获得 2 次之间的差异?
通常需要知道 2 个日期/时间之间的时间差异。对于DateTime
实际上有3种不同的方法可以实现此目的,您想要使用哪一种取决于您的需求。
如何(附示例)
场景 1:您只需要知道$datetime1
是大于、小于还是等于$datetime2
在这种情况下,您可以简单地直接比较DateTime
的实例。
$datetime1 = new DateTime;
sleep(1);
$datetime2 = new DateTime;
var_dump($datetime1 > $datetime2); // FALSE
var_dump($datetime1 < $datetime2); // TRUE
var_dump($datetime1 == $datetime2); // FALSE
场景 2:您需要$datetime1
和$datetime2
之间的差异,以细分的年/月/日/等表示。
这在大多数情况下都有效,但是您从$datetime->diff()
返回的DateInterval
实例有自己的"陷阱",可能不适用于您的特定用例。
$datetime1 = new DateTime('2000-01-01 00:00:00.000000');
$datetime2 = new DateTime('2001-02-03 04:05:06.789012');
$diff = $datetime1->diff($datetime2);
// output: 1 Years, 1 Months, 2 Days, 4 Hours, 5 Minutes, 6 Seconds
echo $diff->format('%y Years, %m Months, %d Days, %h Hours, %i Minutes, %s Seconds');
场景 3:您需要以另一种方式表达$datetime1
和$datetime2
之间的差异。
这将在任何上下文中工作,但需要一些额外的代码。
$interval = 60 * 60 * 24; // 1 day in seconds
$datetime1 = new DateTime('2000-01-01');
$datetime2 = new DateTime;
$diff = $datetime2->getTimestamp() - $datetime1->getTimestamp();
// output: It has been 6956 days since 2000-01-01!
printf('It has been %s days since %s!', floor($diff / $interval), $datetime1->format('Y-m-d'));
4. 如何计算时区?
在处理编程中的时间时,到目前为止,最糟糕的部分是处理时区。幸运的是,这是DateTime
类优雅处理的其他内容。
如何
DateTime
的构造函数允许您在日期/时间字符串中指定源时区或作为第二个参数。之后,只需使用$datetime->setTimezone()
设置一个新的时区,DateTime
将负责其余的工作。
// These 2 lines are functionally identical
$datetime = new DateTime('2001-01-01 00:00:00', new DateTimeZone('UTC')); // recommended, may be faster
$datetime = new DateTime('2001-01-01 00:00:00 UTC');
$datetime->setTimezone(new DateTimeZone('EST'));
我建议查看 PHP 支持的时区的完整列表以及DateTimeZone
类的文档。
例
假设您想向最终用户显示客户支持热线在其时区打开的时间。使用DateTime
代码将如下所示:
$support_opens = new DateTime('08:00:00', new DateTimeZone('America/New_York'));
$customer_timezones = array('America/New_York', 'America/Chicago', 'America/Denver', 'America/Phoenix', 'America/Los_Angeles', 'America/Anchorage', 'America/Adak', 'Pacific/Honolulu');
echo "Today we open at the following times:".PHP_EOL;
foreach ($customer_timezones as $timezone) {
$support_opens->setTimezone(new DateTimeZone($timezone));
echo '* '.$support_opens->format('g:ia for the e').' time zone'.PHP_EOL;
}
输出:
Today we open at the following times:
* 8:00am for the America/New_York time zone
* 7:00am for the America/Chicago time zone
* 6:00am for the America/Denver time zone
* 6:00am for the America/Phoenix time zone
* 5:00am for the America/Los_Angeles time zone
* 4:00am for the America/Anchorage time zone
* 3:00am for the America/Adak time zone
* 3:00am for the Pacific/Honolulu time zone
注意:如果在日期/时间字符串中同时提供时区并作为第二个参数,则参数时区将被忽略。
$datetime = new DateTime('2001-01-01 00:00:00 EST', new DateTimeZone('UTC'));
// output: 2001-01-01 00:00:00 EST
echo $datetime1->format('Y-m-d H:i:s');