澄清一下,g/G 是不带前导 0 的 12/24 小时制时间,h/H 是带前导零的 12/24 小时制时间,如下所述
https://php.net/manual/en/datetime.format.php(PHP 5 >= 5.5.0, PHP 7, PHP 8)
DateTimeImmutable::createFromFormat -- date_create_immutable_from_format — 根据指定的格式解析时间字符串
面向对象风格
$format, string $datetime, ?DateTimeZone $timezone = null): DateTimeImmutable|false过程化风格
$format, string $datetime, ?DateTimeZone $timezone = null): DateTimeImmutable|false返回一个新的 DateTimeImmutable 对象,该对象表示由 datetime 字符串指定的日期和时间,该字符串以给定的 format 格式化。
format传递的 string 应采用的格式。请参阅下面的格式选项。在大多数情况下,可以使用与 date() 相同的字母。
所有字段都使用当前日期/时间初始化。在大多数情况下,您希望将其重置为“零”(Unix 纪元,1970-01-01 00:00:00 UTC)。您可以通过在 format 中将 ! 字符作为第一个字符或 | 作为最后一个字符来实现。有关更多信息,请参阅下面每个字符的文档。
格式从左到右解析,这意味着在某些情况下,格式字符出现的顺序会影响结果。在 z(一年中的某一天)的情况下,需要已经解析了一年,例如通过 Y 或 y 字符。
用于解析数字的字母允许使用各种范围的值,超出逻辑范围。例如,d(一个月中的某一天)接受 00 到 99 范围内的值。唯一的约束是数字的数量。当给出超出范围的值时,将使用日期/时间解析器的溢出机制。下面的示例显示了一些此类行为。
这也意味着为格式字母解析的数据是贪婪的,并且将读取其格式允许的最多位数字。然后,这可能也意味着 datetime 字符串中不再有足够的字符供后续的格式字符使用。此页面上的一个示例也说明了此问题。
format 字符 |
描述 | 示例可解析值 |
|---|---|---|
| 日 | --- | --- |
d 和 j |
一个月中的某一天,两位数,带或不带前导零 |
01 到 31 或 1 到 31。(两位数大于一个月中的天数是可以接受的,在这种情况下,它们将使月份溢出。例如,在 1 月使用 33 表示 2 月 2 日) |
D 和 l |
一天的文本表示 |
Mon 到 Sun 或 Sunday 到 Saturday。如果给定的日期名称与属于解析的(或默认的)日期的日期名称不同,则会发生溢出到具有给定日期名称的下一个日期。请参阅下面的示例以了解说明。 |
S |
一个月中的某一天的英文序数后缀,2 个字符。处理时会忽略它。 |
st、nd、rd 或 th。 |
z |
一年中的某一天(从 0 开始);必须在 Y 或 y 之前。 |
0 到 365。(三位数大于一年中的数字是可以接受的,在这种情况下,它们将使年份溢出。例如,在 2022 年使用 366 表示 2023 年 1 月 2 日) |
| 月 | --- | --- |
F 和 M |
一个月的文本表示,例如 January 或 Sept |
January 到 December 或 Jan 到 Dec |
m 和 n |
一个月的数字表示,带或不带前导零 |
01 到 12 或 1 到 12。(两位数大于 12 是可以接受的,在这种情况下,它们将使年份溢出。例如,使用 13 表示下一年的 1 月) |
| 年 | --- | --- |
X 和 x |
年份的完整数字表示,最多 19 位数字,可选地以 + 或 - 为前缀 |
示例:0055、787、1999、-2003、+10191 |
Y |
年份的完整数字表示,最多 4 位数字 | 示例:0055、787、1999、2003 |
y |
年份的两位数表示(假设在 1970-2069 年(含)范围内) | 示例:99 或 03(分别解释为 1999 和 2003) |
| 时间 | --- | --- |
a 和 A |
上午和下午 | am 或 pm |
g 和 h |
小时的 12 小时格式,带或不带前导零 |
从 1 到 12 或 01 到 12(大于 12 的两位数字会被接受,在这种情况下,它们会导致日期溢出。例如,使用 14 表示下一个 AM/PM 周期的 02) |
G 和 H |
小时的 24 小时制格式,可以带或不带前导零 |
从 0 到 23 或 00 到 23(大于 24 的两位数字会被接受,在这种情况下,它们会导致日期溢出。例如,使用 26 表示下一天的 02:00) |
i |
带前导零的分钟 |
从 00 到 59。(大于 59 的两位数字会被接受,在这种情况下,它们会导致小时溢出。例如,使用 66 表示下一小时的 :06) |
s |
带前导零的秒数 |
从 00 到 59(大于 59 的两位数字会被接受,在这种情况下,它们会导致分钟溢出。例如,使用 90 表示下一分钟的 :30) |
v |
毫秒级的小数部分(最多三位数字) | 例如:12(0.12 秒),345(0.345 秒) |
u |
微秒级的小数部分(最多六位数字) | 例如:45(0.45 秒),654321(0.654321 秒) |
| 时区 | --- | --- |
e、O、p、P 和 T |
时区标识符,或与 UTC 的小时差,或带冒号分隔的小时和分钟的与 UTC 的差值,或时区缩写 | 例如:UTC、GMT、Atlantic/Azores 或 +0200 或 +02:00 或 EST、MDT |
| 完整日期/时间 | --- | --- |
U |
自 Unix 纪元(1970 年 1 月 1 日 00:00:00 GMT)以来的秒数 | 例如:1292177455 |
| 空格和分隔符 | --- | --- |
(空格) |
零个或多个空格、制表符、不间断空格 (U+A0) 或窄不间断空格 (U+202F) 字符 | 例如:"\t"、" " |
# |
以下分隔符之一:;、:、/、.、,、-、( 或 ) |
例如:/ |
;、:、/、.、,、-、( 或 ) |
指定的字符。 | 例如:- |
? |
一个随机字节 | 例如:^(请注意,对于 UTF-8 字符,您可能需要多个 ?。在这种情况下,使用 * 可能是您想要的) |
* |
直到下一个分隔符或数字的随机字节 | 例如:在 Y-*-d 中使用 *,字符串为 2009-aWord-08,将匹配 aWord |
! |
将所有字段(年份、月份、日期、小时、分钟、秒、小数部分和时区信息)重置为类似于零的值(小时、分钟、秒和小数部分为 0,月份和日期为 1,年份为 1970,时区信息为 UTC) |
如果没有 !,,所有字段将设置为当前日期和时间。 |
| |
如果尚未解析,则将所有字段(年份、月份、日期、小时、分钟、秒、小数部分和时区信息)重置为类似于零的值 | Y-m-d| 将年份、月份和日期设置为要解析的字符串中找到的信息,并将小时、分钟和秒设置为 0。 |
+ |
如果存在此格式说明符,则字符串中的尾随数据不会导致错误,而是导致警告 | 使用 DateTimeImmutable::getLastErrors() 找出是否存在尾随数据。 |
格式字符串中无法识别的字符会导致解析失败,并将错误消息附加到返回的结构中。您可以使用 DateTimeImmutable::getLastErrors() 查询错误消息。
要在 format 中包含文字字符,必须使用反斜杠 (\) 对其进行转义。
如果 format 不包含字符 !,则 format 中未指定的生成日期/时间的部分将设置为当前系统时间。
如果 format 包含字符 !,则 format 中未提供的生成日期/时间的部分以及 ! 左侧的值将设置为 Unix 纪元中的相应值。
如果解析了任何时间字符,则所有其他与时间相关的字段都设置为“0”,除非也解析了。
Unix 纪元是 1970-01-01 00:00:00 UTC。
日期时间表示时间的字符串。
时区一个表示所需时区的 DateTimeZone 对象。
如果省略了 timezone 或为 null 并且 datetime 不包含时区,则将使用当前时区。
注意:
当
datetime参数包含 UNIX 时间戳(例如946684800)或指定时区(例如2010-01-28T15:00:00+02:00)时,将忽略timezone参数和当前时区。
返回一个新的 DateTimeImmutable 实例或在失败时返回 false。
当 datetime 包含空字节时,此方法会抛出 ValueError。
| 版本 | 描述 |
|---|---|
| 8.2.9 | (空格)说明符现在还支持不间断空格 (U+A0) 和窄不间断空格 (U+202F) 字符。 |
| 8.2.0 | 已添加 X 和 x format 说明符。 |
| 8.0.21, 8.1.8, 8.2.0 | 现在当将空字节传递到 datetime 中时会抛出 ValueError,之前会静默忽略。 |
| 7.3.0 | 已添加 v format 说明符。 |
示例 #1 DateTimeImmutable::createFromFormat() 示例
面向对象风格
<?php
$date = DateTimeImmutable::createFromFormat('j-M-Y', '15-Feb-2009');
echo $date->format('Y-m-d');
?>示例 #2 使用预定义的格式常量与 DateTimeImmutable::createFromFormat()
面向对象风格
<?php
$date = DateTimeImmutable::createFromFormat(DateTimeInterface::ISO8601, '2004-02-12T15:19:21+00:00');
$date = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC3339_EXTENDED, '2013-10-14T09:00:00.000+02:00');
?>本示例中使用的 格式化常量 由一串字符组成,用于 格式化 DateTimeImmutable 对象。在大多数情况下,这些字母与上面“参数”部分中定义的日期/时间信息的相同元素匹配,但它们往往更宽松。
示例 #3 DateTimeImmutable::createFromFormat() 的复杂性
<?php
echo '当前时间: ' . date('Y-m-d H:i:s') . "\n";
$format = 'Y-m-d';
$date = DateTimeImmutable::createFromFormat($format, '2009-02-15');
echo "格式: $format; " . $date->format('Y-m-d H:i:s') . "\n";
$format = 'Y-m-d H:i:s';
$date = DateTimeImmutable::createFromFormat($format, '2009-02-15 15:16:17');
echo "格式: $format; " . $date->format('Y-m-d H:i:s') . "\n";
$format = 'Y-m-!d H:i:s';
$date = DateTimeImmutable::createFromFormat($format, '2009-02-15 15:16:17');
echo "格式: $format; " . $date->format('Y-m-d H:i:s') . "\n";
$format = '!d';
$date = DateTimeImmutable::createFromFormat($format, '15');
echo "格式: $format; " . $date->format('Y-m-d H:i:s') . "\n";
$format = 'i';
$date = DateTimeImmutable::createFromFormat($format, '15');
echo "格式: $format; " . $date->format('Y-m-d H:i:s') . "\n";
?>以上示例将输出类似以下内容
Current time: 2022-06-02 15:50:46 Format: Y-m-d; 2009-02-15 15:50:46 Format: Y-m-d H:i:s; 2009-02-15 15:16:17 Format: Y-m-!d H:i:s; 1970-01-15 15:16:17 Format: !d; 1970-01-15 00:00:00 Format: i; 2022-06-02 00:15:00
示例 #4 包含字面字符的格式字符串
<?php
echo DateTimeImmutable::createFromFormat('H\h i\m s\s','23h 15m 03s')->format('H:i:s');
?>以上示例将输出类似以下内容
23:15:03
示例 #5 溢出行为
<?php
echo DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2021-17-35 16:60:97')->format(DateTimeImmutable::RFC2822);
?>以上示例将输出类似以下内容
Sat, 04 Jun 2022 17:01:37 +0000
尽管结果看起来很奇怪,但它是正确的,因为发生了以下溢出
97 秒溢出到 1 分钟,剩下 37 秒。
61 分钟溢出到 1 小时,剩下 1 分钟。
35 天溢出到 1 个月,剩下 4 天。剩余的天数取决于月份,因为每个月的天数不相同。
18 个月溢出到 1 年,剩下 6 个月。
示例 #6 星期名称溢出行为
<?php
$d = DateTime::createFromFormat(DateTimeInterface::RFC1123, 'Mon, 3 Aug 2020 25:00:00 +0000');
echo $d->format(DateTime::RFC1123), "\n";
?>以上示例将输出类似以下内容
Mon, 10 Aug 2020 01:00:00 +0000
尽管结果看起来很奇怪,但它是正确的,因为发生了以下溢出
3 Aug 2020 25:00:00 溢出到 (Tue) 4 Aug 2020 01:00。
Mon 被应用,这将日期提前到 Mon, 10 Aug 2020 01:00:00。诸如 Mon 之类的相对关键字的解释在关于相对格式的部分中进行了解释。
为了检测日期中的溢出,您可以使用 DateTimeImmutable::getLastErrors(),如果发生溢出,它将包含警告。
示例 #7 检测溢出日期
<?php
$d = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2021-17-35 16:60:97');
echo $d->format(DateTimeImmutable::RFC2822), "\n\n";
var_dump(DateTimeImmutable::GetLastErrors());
?>以上示例将输出类似以下内容
Sat, 04 Jun 2022 17:01:37 +0000
array(4) {
'warning_count' =>
int(2)
'warnings' =>
array(1) {
[19] =>
string(27) "The parsed date was invalid"
}
'error_count' =>
int(0)
'errors' =>
array(0) {
}
}
示例 #8 贪婪解析行为
<?php
print_r(date_parse_from_format('Gis', '60101'));
?>以上示例将输出类似以下内容
Array
(
[year] =>
[month] =>
[day] =>
[hour] => 60
[minute] => 10
[second] => 0
[fraction] => 0
[warning_count] => 1
[warnings] => Array
(
[5] => The parsed time was invalid
)
[error_count] => 1
[errors] => Array
(
[4] => A two digit second could not be found
)
[is_localtime] =>
)
G 格式用于解析 24 小时制的小时,带或不带前导零。这需要解析 1 或 2 位数字。因为后面还有两位数字,所以它贪婪地将其读取为 60。
接下来的 i 和 s 格式字符都需要两位数字。这意味着 10 被作为分钟 (i) 传递,并且没有足够的数字剩余来解析为秒 (s)。
errors 数组指示此问题。
此外,60 小时超出了 0-24 的范围,这导致 warnings 数组包含时间无效的警告。
澄清一下,g/G 是不带前导 0 的 12/24 小时制时间,h/H 是带前导零的 12/24 小时制时间,如下所述
https://php.net/manual/en/datetime.format.php由于描述和示例在时区行方面并不完全匹配,因此我想明确每个字符输出的格式。
`e` 输出时区标识符,例如 `America/New_York` 或 `Asia/Gaza`
`O` 输出与 UTC 的时差(以小时为单位),例如 `-0500` 或 `+0200`
`P` 输出与 UTC 的时差,在小时和分钟之间用冒号分隔,例如 `-05:00` 或 `+02:00`
`T` 输出时区缩写,例如 `EST` 或 `EET`如果您对这种方法为您进行的各种转换和修复不满意,或者只是想检查日期是否确实与输入相同
```
$datetime = \DateTimeImmutable::createFromFormat('Y-m-d G:i:s', $userDateTimeInput);
if ($datetime && $datetime->format('Y-m-d G:i:s') === $userDateTimeInput) {
// $datetime 不为假,并且我们从用户那里得到了正确格式的正确日期
}
```