# 如何使用 DateTime 类(处理转换、格式、差异和时区)



每个人都必须在编程的某个时刻处理日期/时间格式、时区、奇怪的时间格式等。PHP几乎没有方法来处理这些问题,其中最值得注意的是PHP内置DateTime类。这是因为DateTime类充当所有这些问题的多合一解决方案。

但是,DateTime类有一个缺陷:如果您还不熟悉它,使用它可能会令人困惑。

这篇文章是为了帮助那些想了解更多关于DateTime课程和/或发现自己问以下问题之一的人:

  1. 如何将字符串转换为可修改的日期/时间?
  2. 如何格式化日期/时间字符串?
  3. 我如何获得 2 次之间的差异?
  4. 如何考虑时区?

请注意:

这篇文章不会讨论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');

相关内容

  • 没有找到相关文章

最新更新