PHP 日期和时间食谱

Arjuna Sky Kok - 2021 年 11 月 18 日

处理日期和时间是那些可能让程序员非常沮丧的事情之一。同时,它们是软件开发的基础,从元数据和事物排序方式到基于时间的触发器以及介于两者之间的许多内容都使用它们。

日期和时间也容易出错。处理不当,它们可能会让最终用户和程序员都感到困惑。

这是一份专门针对 PHP 编程语言处理日期和时间的快速指南。它旨在作为您最常见的需求(例如格式化和调整日期)的参考。它很简单,但它很可能涵盖您 80% 的需求。

目录

这项研究得到了 Frontend Masters,CSS-Tricks 的官方学习合作伙伴的支持。

需要前端开发培训吗?

Frontend Masters 是获取它的最佳场所。他们提供有关所有最重要的前端技术的课程。有兴趣成为全栈工程师吗?这是您最好的选择


获取当前日期和时间

需要知道的一件事是,日期和时间可以用三种形式表示:时间戳(即 纪元时间)、DateTime 对象和字符串。

首先,获取当前日期和时间的食谱

<?php

$now = new DateTime();
var_dump($now);

// object(DateTime)#1 (3) {
//   ["date"]=>
//   string(26) "2021-10-13 22:25:11.790490"
//   ["timezone_type"]=>
//   int(3)
//   ["timezone"]=>
//   string(12) "Asia/Jakarta"
// }

这提供了一个 DateTime 对象,可用于创建日期和时间字符串

<?php

$now = new DateTime();
echo $now->format("Y-m-d"); // 2021-10-13
echo $now->format("Y-m-d h:i:s A"); // 2021-10-13 10:10:31 PM

直观地,您知道 Y 指的是年份,m 指的是月份,d 指的是月份中的日期,等等。参数的完整列表可以在 PHP 手册中找到,但我会在这里提供一些最常见的参数以供参考。

月份中的日期
d月份中的日期。有两个数字,并有前导零01 – 31
j月份中的日期,没有前导零1 – 31
S包括英语后缀。stndrdth (例如 1st2nd3rd4th)
星期几
D星期几的简写文本表示,三个字母Sun – Sat
l星期几的完整文本表示。Sunday – Saturday
F月份的完整文本表示,例如一月或三月January – December
M月份的简写文本表示,三个字母Jan – Dec
m月份的数字表示,并有前导零01 – 12
n月份的数字表示,没有前导零1 – 12
Y年份的完整数字表示,4 位数字例如 1999 或 2003
y年份的两位数字表示例如 99 或 03
时间
A上午和下午大写AM 或 PM
g没有前导零的小时 12 小时制格式1 – 12
h有前导零的小时 12 小时制格式01 – 12
i有前导零的分钟00 – 59
s有前导零的秒00 – 59

DateTime 对象可以转换为时间戳

<?php

$now = new DateTime();
echo $now->getTimestamp(); // 1634139081

但是我们也可以在不构造 DateTime 对象的情况下获取当前时间戳

<?php

echo time(); // 1634139081

构造特定时间的 DateTime 对象

如果我们想为特定时间(例如 2011 年 7 月 14 日)构造一个 DateTime,我们可以将格式化的字符串日期传递给构造函数

<?php

$date = new DateTime("2011-07-14");
var_dump($date);

// object(DateTime)#1 (3) {
//   ["date"]=>
//   string(26) "2011-07-14 00:00:00.000000"
//   ["timezone_type"]=>
//   int(3)
//   ["timezone"]=>
//   string(12) "Asia/Jakarta"
// }

构造函数还接受其他格式

<?php

$date = new DateTime("14-07-2011");
var_dump($date);

// object(DateTime)#1 (3) {
//   ["date"]=>
//   string(26) "2011-07-14 00:00:00.000000"
//   ["timezone_type"]=>
//   int(3)
//   ["timezone"]=>
//   string(12) "Asia/Jakarta"
// }

但要小心使用模棱两可的格式,例如:

<?php

$date = new DateTime("07/14/2011");
var_dump($date);

// object(DateTime)#1 (3) {
//   ["date"]=>
//   string(26) "2011-07-14 00:00:00.000000"
//   ["timezone_type"]=>
//   int(3)
//   ["timezone"]=>
//   string(12) "Asia/Jakarta"
// }

您可能认为每个人都应该熟悉美国日期格式。但并非所有人都是这样,并且可能会被不同地解释。PostgreSQL 不是。

CREATE TABLE IF NOT EXISTS public.datetime_demo
(
  created_at date
);

insert into datetime_demo (created_at) values ('07/12/2011');

select created_at from datetime_demo; /* 2011-12-07 */

您可能认为这将返回 2011 年 7 月 12 日,但实际上它是 2011 年 12 月 7 日。更好的方法是使用显式格式

<?php

$date = DateTime::createFromFormat('m/d/y', "10/08/21");
var_dump($date);

//object(DateTime)#2 (3) {
//  ["date"]=>
//  string(26) "2021-10-08 16:00:47.000000"
//  ["timezone_type"]=>
//  int(3)
//  ["timezone"]=>
//  string(12) "Asia/Jakarta"
//}

如果我们想从时间戳构造一个 DateTime 对象,该怎么办?

<?php

$date = new DateTime();
$date->setTimestamp(1634142890);
var_dump($date);

//object(DateTime)#1 (3) {
//  ["date"]=>
//  string(26) "2021-10-13 23:34:50.000000"
//  ["timezone_type"]=>
//  int(3)
//  ["timezone"]=>
//  string(12) "Asia/Jakarta"
// }

如果我们想将时间戳对象转换为格式化的日期字符串,则不必创建 DateTime 对象

<?php

echo date("Y-m-d h:i A", time()); // 2021-10-14 04:10 PM

时区

我们可以创建一个包含时区信息的 DateTime 对象,例如,如果我们处理的是太平洋标准时间、东部夏令时等。

<?php

$timezone = new DateTimeZone("America/New_York");
$date = new DateTime("2021-10-13 05:00", $timezone);
var_dump($date);

// object(DateTime)#1 (3) {
//   ["date"]=>
//   string(26) "2021-10-13 05:00:00.000000"
//   ["timezone_type"]=>
//   int(3)
//   ["timezone"]=>
//   string(16) "America/New_York"
// }

// Eastern Daylight Time, for example: New York
$date = new DateTime("2021-10-13 05:00 EDT");
var_dump($date);

// object(DateTime)#2 (3) {
//   ["date"]=>
//   string(26) "2021-10-13 05:00:00.000000"
//   ["timezone_type"]=>
//   int(2)
//   ["timezone"]=>
//   string(3) "EDT"
// }

$date = new DateTime("2021-10-13 05:00 -04:00");
var_dump($date);

// object(DateTime)#1 (3) {
//   ["date"]=>
//   string(26) "2021-10-13 05:00:00.000000"
//   ["timezone_type"]=>
//   int(1)
//   ["timezone"]=>
//   string(6) "-04:00"
// }

有三种方法可以创建包含时区信息的 DateTime 对象。timezone_type 接受每个方法的不同值。

但是,假设我们想将以纽约时区显示的日期和时间转换为以雅加达时区显示,该怎么办?

<?php

$newYorkTimeZone = new DateTimeZone("America/New_York");
$date = new DateTime("2021-11-11 05:00", $newYorkTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-11-11 05:00 AM
$jakartaTimeZone = new DateTimeZone("Asia/Jakarta");
$date->setTimeZone($jakartaTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-11-11 05:00 PM

当纽约时间凌晨 5:00 时,雅加达时间为下午 5:00,日期相同。2021 年 11 月 11 日,雅加达比纽约早 12 小时。但在一个月前,雅加达比纽约只早 11 个小时,如下所示

<?php

$newYorkTimeZone = new DateTimeZone("America/New_York");
$date = new DateTime("2021-10-11 05:00", $newYorkTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-10-11 05:00 AM
$jakartaTimeZone = new DateTimeZone("Asia/Jakarta");
$date->setTimeZone($jakartaTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-10-11 04:00 PM

PHP 自动为您处理夏令时。

本地化

这是在美国显示日期和时间的常见方法

<?php

$now = new DateTime();
echo $now->format("m/d/Y h:i A"); // 10/14/2021 03:00 PM

但法国人可能更喜欢更符合他们地区习惯的东西。C’est horrible,他们会抱怨。首先,除了美国以外,没有人把月份放在日期前面。其次,法国不使用上午或下午——他们使用 24 小时制格式(例如 14:00 而不是下午 2:00),就像军队一样。这就是您让法国人开心的方法。

<?php

$now = new DateTime();
echo $now->format("d/m/Y H:i"); // 14/10/2021 15:00

但这需要对特定国家或地区的深入了解。相反,我们可以本地化日期。要本地化日期,我们需要安装 PHP 的国际化支持。在 Ubuntu 中,我们可以执行此步骤

$ sudo apt-get install php-intl

要以法语显示日期和时间,我们可以使用 IntlDateFormatter

$locale = "fr_FR.UTF-8";
$formatter = new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::SHORT, "Asia/Singapore");
$date = new DateTime("2020-10-10 00:00 UTC");
echo $formatter->format($date); // samedi 10 octobre 2020 à 08:00

您将法语区域设置作为 IntlDateFormatter 的第一个参数传递。
第二个参数是日期的格式。第三个参数是时间的格式。显示日期和时间的时区位于第四个参数中。

除了 IntlDateFormatter::FULLIntlDateFormatter::SHORT 之外,其他流行的格式还有 IntlDateFormatter::NONEIntlDateFormatter::LONG
IntlDateFormatter::MEDIUM

如果在时间或第三个参数中使用 IntlDateFormatter::NONE,则意味着您不包含格式中的时间

$locale = "fr_FR.UTF-8";
$formatter = new IntlDateFormatter($locale, IntlDateFormatter::LONG, IntlDateFormatter::NONE, "Asia/Singapore");
$date = new DateTime("2020-10-10 00:00 UTC");
echo $formatter->format($date); // 10 octobre 2020

时间旅行

让我们穿越时间,回到过去和未来。首先,让我们熟悉 DateInterval

<?php

$interval = new DateInterval("P4M1W2DT2H5M");

// P 4M 1W 2D T 2H 5M
//
// P = Period interval (years, months, weeks, days)
// 4M = 4 months
// 1W = 1 week
// 2D = 2 days
//
// T = Time interval (hours, minutes, seconds)
// 2H = 2 hours
// 5M = 5 minutes

PT 用于分离周期间隔和时间间隔。以下是我们穿越到未来的方式

<?php

$date = new DateTime("2021-10-14");
$interval = new DateInterval("P2D"); // 2 days
$futureDate = $date->add($interval);
echo $futureDate->format("Y-m-d"); // 2021-10-16

以下是我们回到过去的方式

<?php

$date = new DateTime("2021-10-14 10:00");
$interval = new DateInterval("PT6H"); // 6 hours
$pastDate = $date->sub($interval);
echo $pastDate->format("Y-m-d H:i"); // 2021-10-14 04:00

如果我们想使用星期几的名称进行时间旅行,我们可以将 strtotime() 函数和 DateTime 对象的 setTimestamp() 方法结合起来

<?php

$nextTuesday = strtotime("next tuesday");
$date = new DateTime("2021-10-14");
$date->setTimestamp($nextTuesday);
echo $date->format("Y-m-d"); // 2021-10-19

查看 PHP 文档中 strtotime() 参数的完整列表 .

重复日期和时间

在日历应用程序中,设置定期重复的提醒(例如每两天或每周一次)是一个常见的功能。我们可以使用 DatePeriod 来表示一段时间

<?php

$start = new DateTime("2021-10-01");
$end = new DateTime("2021-11-01");
$interval = new DateInterval("P1W"); // 1 week
$range = new DatePeriod($start, $interval, $end);

// Starting from October 1st 2021 (inclusive), jump every 1 week
// until November 1st 2021 (exclusive)
foreach ($range as $date) {
  echo $date->format("Y-m-d") . "n";
}

// 2022-10-01
// 2022-10-08
// 2022-10-15
// 2022-10-22
// 2022-10-29

几天前?

您知道像 Twitter 这样的服务会显示某人发布了 X 分钟/小时/天/等之前吗?我们可以通过计算当前时间和该操作发生的时间之间经过的时间来做同样的事情。

<?php

$date = new DateTime("2022-10-30");

$date2 = new DateTime("2022-10-25");
$date3 = new DateTime("2022-10-10");
$date4 = new DateTime("2022-03-30");
$date5 = new DateTime("2020-03-30");

function get_period_ago($endDate, $startDate) {
  $dateInterval = $endDate->diff($startDate);

  if ($dateInterval->invert==1) {
    if ($dateInterval->y > 0) {
      return $dateInterval->y . " years agon";
    } if ($dateInterval->m > 0) {
      return $dateInterval->m . " months agon";
    } if ($dateInterval->d > 7) {
      return (int)($dateInterval->d / 7) . " weeks agon";
    } if ($dateInterval->d > 0) {
      return $dateInterval->d . " days agon";
    }
  }
}

echo get_period_ago($date, $date2); // 5 days ago
echo get_period_ago($date, $date3); // 2 weeks ago
echo get_period_ago($date, $date4); // 7 months ago
echo get_period_ago($date, $date5); // 2 years ago

diff() 方法获取 DateInterval 对象后,请确保通过检查 invert 属性来确保 $startDate 变量位于过去。然后检查 ymd 属性。

可以在 PHP 文档中找到 DateInterval 对象属性的完整列表。

您下一步去哪里?

现在您有了一张常见 PHP 食谱的小抄,当您发现自己正在处理日期和时间时,可以使用它。需要获取当前日期和时间吗?也许您需要以某种方式格式化日期,或者包含本地时区,或者比较日期。所有这些都在这里!

当然,还有更多关于日期和时间的 方法和函数 我们没有讨论 - 例如与日历相关的函数等等。一定要将 PHP 手册的 日期和时间 部分放在手边,以获得更多用例和示例。