处理日期和时间是那些可能让程序员非常沮丧的事情之一。同时,它们是软件开发的基础,从元数据和事物排序方式到基于时间的触发器以及介于两者之间的许多内容都使用它们。
日期和时间也容易出错。处理不当,它们可能会让最终用户和程序员都感到困惑。
这是一份专门针对 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 | 包括英语后缀。 | st , nd , rd , th (例如 1st , 2nd , 3rd , 4th ) |
星期几 | ||
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::FULL
和 IntlDateFormatter::SHORT
之外,其他流行的格式还有 IntlDateFormatter::NONE
、IntlDateFormatter::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
P
和 T
用于分离周期间隔和时间间隔。以下是我们穿越到未来的方式
<?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
变量位于过去。然后检查 y
、m
和 d
属性。
可以在 PHP 文档中找到 DateInterval
对象属性的完整列表。
您下一步去哪里?
现在您有了一张常见 PHP 食谱的小抄,当您发现自己正在处理日期和时间时,可以使用它。需要获取当前日期和时间吗?也许您需要以某种方式格式化日期,或者包含本地时区,或者比较日期。所有这些都在这里!
当然,还有更多关于日期和时间的 方法和函数 我们没有讨论 - 例如与日历相关的函数等等。一定要将 PHP 手册的 日期和时间 部分放在手边,以获得更多用例和示例。
天哪,我是过去 20 年的 PHP 程序员,但我从未听说过 DateInterval!!!
我认为我已经习惯了碳,而且我敢肯定他们会在内部使用这些功能!
感谢这篇文章!
Carbon 简化了日期和时间的处理,并提供了额外的功能。
对不起,只是对这行笑了一下:“您可能认为每个人都应该熟悉美式日期格式。”
我认为每个人都知道美国人对日期的格式很奇怪,但这并不意味着我们对细节不熟悉。
我希望 15 年前我就有这篇文章!我认为唯一缺少的是 ->modify()。例如 ->modify(‘+ 1 Day’)
令我惊讶的是,没有提到 strftime 在 Windows 上不起作用并且在 PHP 8.1 上已弃用。
感谢您的反馈!我是这篇文章的作者。我将尝试用这篇文章中的弃用函数替换。但由于现在是年底,我们可能需要等到 2022 年 1 月更新。