PHP日期时间处理完全指南:从基础到高级应用
Orion K Lv6

PHP日期时间处理完全指南:从基础到高级应用

日期时间处理是Web开发中的常见需求,从简单的时间戳到复杂的时区转换,PHP提供了丰富的日期时间处理功能。本文将分享一些实用的日期时间处理技巧和最佳实践。

基础日期时间操作

1. 基本函数和类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<?php
// 基础日期时间操作
echo "=== 基础日期时间操作 ===\n";

// 获取当前时间戳
$timestamp = time();
echo "当前时间戳: $timestamp\n";

// 格式化时间戳
echo "格式化时间: " . date('Y-m-d H:i:s', $timestamp) . "\n";
echo "中文格式: " . date('Y年m月d日 H:i:s', $timestamp) . "\n";

// 常用日期格式
$formats = [
'Y-m-d' => '年-月-日',
'Y-m-d H:i:s' => '年-月-日 时:分:秒',
'Y/m/d' => '年/月/日',
'M d, Y' => '英文月 日, 年',
'l, F j, Y' => '星期, 月份 日, 年',
'c' => 'ISO 8601格式',
'r' => 'RFC 2822格式'
];

echo "\n常用日期格式:\n";
foreach ($formats as $format => $description) {
echo "$description ($format): " . date($format) . "\n";
}

// 解析日期字符串
$dateString = '2023-08-05 15:30:45';
$parsedTimestamp = strtotime($dateString);
echo "\n解析日期字符串 '$dateString': $parsedTimestamp\n";
echo "转换回格式: " . date('Y-m-d H:i:s', $parsedTimestamp) . "\n";

// 相对时间解析
$relativeFormats = [
'now',
'+1 day',
'-1 week',
'+2 months',
'next Monday',
'last Friday',
'first day of this month',
'last day of next month'
];

echo "\n相对时间解析:\n";
foreach ($relativeFormats as $format) {
$timestamp = strtotime($format);
echo "$format: " . date('Y-m-d H:i:s', $timestamp) . "\n";
}

// 日期时间工具类
class DateTimeHelper {

// 格式化时间差
public static function formatTimeDiff($timestamp1, $timestamp2 = null) {
$timestamp2 = $timestamp2 ?: time();
$diff = abs($timestamp2 - $timestamp1);

$units = [
'年' => 365 * 24 * 3600,
'月' => 30 * 24 * 3600,
'周' => 7 * 24 * 3600,
'天' => 24 * 3600,
'小时' => 3600,
'分钟' => 60,
'秒' => 1
];

foreach ($units as $unit => $seconds) {
if ($diff >= $seconds) {
$count = floor($diff / $seconds);
return $count . $unit . '前';
}
}

return '刚刚';
}

// 判断是否为工作日
public static function isWorkday($timestamp = null) {
$timestamp = $timestamp ?: time();
$dayOfWeek = date('N', $timestamp); // 1-7, 1为周一
return $dayOfWeek >= 1 && $dayOfWeek <= 5;
}

// 获取月份天数
public static function getDaysInMonth($year, $month) {
return date('t', mktime(0, 0, 0, $month, 1, $year));
}

// 获取季度
public static function getQuarter($timestamp = null) {
$timestamp = $timestamp ?: time();
$month = date('n', $timestamp);
return ceil($month / 3);
}

// 获取星座
public static function getZodiacSign($month, $day) {
$signs = [
['摩羯座', 1, 20], ['水瓶座', 2, 19], ['双鱼座', 3, 21],
['白羊座', 4, 20], ['金牛座', 5, 21], ['双子座', 6, 22],
['巨蟹座', 7, 23], ['狮子座', 8, 23], ['处女座', 9, 23],
['天秤座', 10, 24], ['天蝎座', 11, 23], ['射手座', 12, 22]
];

foreach ($signs as $i => $sign) {
if (($month == $sign[1] && $day >= $sign[2]) ||
($month == ($sign[1] % 12) + 1 && $day < $signs[($i + 1) % 12][2])) {
return $sign[0];
}
}

return '摩羯座';
}

// 计算年龄
public static function calculateAge($birthDate, $currentDate = null) {
$currentDate = $currentDate ?: date('Y-m-d');

$birth = new DateTime($birthDate);
$current = new DateTime($currentDate);

return $birth->diff($current)->y;
}
}

// 使用日期时间工具类
echo "\n=== 日期时间工具类示例 ===\n";

// 时间差格式化
$pastTime = strtotime('-2 hours -30 minutes');
echo "时间差: " . DateTimeHelper::formatTimeDiff($pastTime) . "\n";

// 工作日判断
echo "今天是工作日: " . (DateTimeHelper::isWorkday() ? '是' : '否') . "\n";

// 月份天数
echo "2023年2月天数: " . DateTimeHelper::getDaysInMonth(2023, 2) . "\n";
echo "2024年2月天数: " . DateTimeHelper::getDaysInMonth(2024, 2) . "\n";

// 季度
echo "当前季度: 第" . DateTimeHelper::getQuarter() . "季度\n";

// 星座
echo "8月5日星座: " . DateTimeHelper::getZodiacSign(8, 5) . "\n";

// 年龄计算
echo "1990-05-15出生年龄: " . DateTimeHelper::calculateAge('1990-05-15') . "岁\n";
?>

2. DateTime类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
<?php
// DateTime类详细使用
echo "=== DateTime类使用 ===\n";

// 创建DateTime对象
$now = new DateTime();
echo "当前时间: " . $now->format('Y-m-d H:i:s') . "\n";

// 从字符串创建
$specificDate = new DateTime('2023-08-05 15:30:45');
echo "指定时间: " . $specificDate->format('Y-m-d H:i:s') . "\n";

// 从格式创建
$customFormat = DateTime::createFromFormat('d/m/Y H:i', '05/08/2023 15:30');
echo "自定义格式: " . $customFormat->format('Y-m-d H:i:s') . "\n";

// 时间修改
$modifiedDate = clone $now;
$modifiedDate->add(new DateInterval('P1Y2M3DT4H5M6S')); // 1年2月3天4小时5分6秒
echo "增加时间后: " . $modifiedDate->format('Y-m-d H:i:s') . "\n";

$modifiedDate->sub(new DateInterval('P6M')); // 减去6个月
echo "减去6个月后: " . $modifiedDate->format('Y-m-d H:i:s') . "\n";

// 设置特定日期
$setDate = clone $now;
$setDate->setDate(2023, 12, 25);
$setDate->setTime(18, 30, 0);
echo "设置圣诞节: " . $setDate->format('Y-m-d H:i:s') . "\n";

// DateTime工具类
class DateTimeManager {
private $dateTime;

public function __construct($dateTime = null) {
if ($dateTime instanceof DateTime) {
$this->dateTime = clone $dateTime;
} elseif (is_string($dateTime)) {
$this->dateTime = new DateTime($dateTime);
} else {
$this->dateTime = new DateTime();
}
}

// 链式操作
public function addYears($years) {
$this->dateTime->add(new DateInterval("P{$years}Y"));
return $this;
}

public function addMonths($months) {
$this->dateTime->add(new DateInterval("P{$months}M"));
return $this;
}

public function addDays($days) {
$this->dateTime->add(new DateInterval("P{$days}D"));
return $this;
}

public function addHours($hours) {
$this->dateTime->add(new DateInterval("PT{$hours}H"));
return $this;
}

public function subYears($years) {
$this->dateTime->sub(new DateInterval("P{$years}Y"));
return $this;
}

public function subMonths($months) {
$this->dateTime->sub(new DateInterval("P{$months}M"));
return $this;
}

public function subDays($days) {
$this->dateTime->sub(new DateInterval("P{$days}D"));
return $this;
}

// 获取月初
public function startOfMonth() {
$this->dateTime->setDate(
$this->dateTime->format('Y'),
$this->dateTime->format('n'),
1
)->setTime(0, 0, 0);
return $this;
}

// 获取月末
public function endOfMonth() {
$this->dateTime->setDate(
$this->dateTime->format('Y'),
$this->dateTime->format('n'),
$this->dateTime->format('t')
)->setTime(23, 59, 59);
return $this;
}

// 获取周一
public function startOfWeek() {
$dayOfWeek = $this->dateTime->format('N');
if ($dayOfWeek != 1) {
$this->dateTime->sub(new DateInterval('P' . ($dayOfWeek - 1) . 'D'));
}
$this->dateTime->setTime(0, 0, 0);
return $this;
}

// 获取周日
public function endOfWeek() {
$dayOfWeek = $this->dateTime->format('N');
if ($dayOfWeek != 7) {
$this->dateTime->add(new DateInterval('P' . (7 - $dayOfWeek) . 'D'));
}
$this->dateTime->setTime(23, 59, 59);
return $this;
}

// 判断是否为今天
public function isToday() {
$today = new DateTime();
return $this->dateTime->format('Y-m-d') === $today->format('Y-m-d');
}

// 判断是否为昨天
public function isYesterday() {
$yesterday = new DateTime('-1 day');
return $this->dateTime->format('Y-m-d') === $yesterday->format('Y-m-d');
}

// 判断是否为明天
public function isTomorrow() {
$tomorrow = new DateTime('+1 day');
return $this->dateTime->format('Y-m-d') === $tomorrow->format('Y-m-d');
}

// 判断是否为周末
public function isWeekend() {
$dayOfWeek = $this->dateTime->format('N');
return $dayOfWeek >= 6;
}

// 获取友好时间格式
public function toFriendlyString() {
$now = new DateTime();
$diff = $now->getTimestamp() - $this->dateTime->getTimestamp();

if ($diff < 60) {
return '刚刚';
} elseif ($diff < 3600) {
return floor($diff / 60) . '分钟前';
} elseif ($diff < 86400) {
return floor($diff / 3600) . '小时前';
} elseif ($diff < 604800) {
return floor($diff / 86400) . '天前';
} else {
return $this->dateTime->format('Y-m-d');
}
}

// 格式化输出
public function format($format = 'Y-m-d H:i:s') {
return $this->dateTime->format($format);
}

// 获取DateTime对象
public function getDateTime() {
return clone $this->dateTime;
}

// 比较日期
public function isBefore(DateTime $date) {
return $this->dateTime < $date;
}

public function isAfter(DateTime $date) {
return $this->dateTime > $date;
}

public function equals(DateTime $date) {
return $this->dateTime == $date;
}
}

// 使用DateTime管理器
echo "\n=== DateTime管理器示例 ===\n";

$dtm = new DateTimeManager();
echo "当前时间: " . $dtm->format() . "\n";

// 链式操作
$future = new DateTimeManager();
$future->addYears(1)->addMonths(2)->addDays(15);
echo "1年2月15天后: " . $future->format() . "\n";

// 月初月末
$monthStart = new DateTimeManager();
$monthStart->startOfMonth();
echo "本月开始: " . $monthStart->format() . "\n";

$monthEnd = new DateTimeManager();
$monthEnd->endOfMonth();
echo "本月结束: " . $monthEnd->format() . "\n";

// 周开始结束
$weekStart = new DateTimeManager();
$weekStart->startOfWeek();
echo "本周开始: " . $weekStart->format() . "\n";

$weekEnd = new DateTimeManager();
$weekEnd->endOfWeek();
echo "本周结束: " . $weekEnd->format() . "\n";

// 判断方法
$testDate = new DateTimeManager();
echo "是否为今天: " . ($testDate->isToday() ? '是' : '否') . "\n";
echo "是否为周末: " . ($testDate->isWeekend() ? '是' : '否') . "\n";

// 友好时间
$pastTime = new DateTimeManager('-2 hours');
echo "友好时间: " . $pastTime->toFriendlyString() . "\n";
?>

3. 时区处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php
// 时区处理
echo "=== 时区处理 ===\n";

// 获取默认时区
echo "默认时区: " . date_default_timezone_get() . "\n";

// 设置时区
date_default_timezone_set('Asia/Shanghai');
echo "设置后时区: " . date_default_timezone_get() . "\n";

// 不同时区的时间
$timezones = [
'Asia/Shanghai' => '上海',
'America/New_York' => '纽约',
'Europe/London' => '伦敦',
'Asia/Tokyo' => '东京',
'Australia/Sydney' => '悉尼'
];

echo "\n不同时区当前时间:\n";
foreach ($timezones as $timezone => $city) {
$dt = new DateTime('now', new DateTimeZone($timezone));
echo "$city ($timezone): " . $dt->format('Y-m-d H:i:s T') . "\n";
}

// 时区转换类
class TimezoneConverter {

// 转换时区
public static function convert($dateTime, $fromTimezone, $toTimezone) {
if (is_string($dateTime)) {
$dt = new DateTime($dateTime, new DateTimeZone($fromTimezone));
} else {
$dt = clone $dateTime;
$dt->setTimezone(new DateTimeZone($fromTimezone));
}

$dt->setTimezone(new DateTimeZone($toTimezone));
return $dt;
}

// 获取时区偏移
public static function getTimezoneOffset($timezone, $datetime = null) {
$datetime = $datetime ?: new DateTime();
$tz = new DateTimeZone($timezone);
return $tz->getOffset($datetime);
}

// 获取时区列表
public static function getTimezoneList($region = null) {
$timezones = DateTimeZone::listIdentifiers();

if ($region) {
$regionConstant = constant('DateTimeZone::' . strtoupper($region));
$timezones = DateTimeZone::listIdentifiers($regionConstant);
}

return $timezones;
}

// 格式化时区偏移
public static function formatOffset($offset) {
$hours = intval($offset / 3600);
$minutes = abs(($offset % 3600) / 60);

return sprintf('%+03d:%02d', $hours, $minutes);
}

// 获取时区信息
public static function getTimezoneInfo($timezone) {
$tz = new DateTimeZone($timezone);
$now = new DateTime('now', $tz);

return [
'timezone' => $timezone,
'offset' => $tz->getOffset($now),
'offset_formatted' => self::formatOffset($tz->getOffset($now)),
'current_time' => $now->format('Y-m-d H:i:s T'),
'is_dst' => $now->format('I') == '1'
];
}

// 批量转换时区
public static function convertMultiple($dateTime, $fromTimezone, $toTimezones) {
$results = [];

foreach ($toTimezones as $timezone) {
$converted = self::convert($dateTime, $fromTimezone, $timezone);
$results[$timezone] = $converted->format('Y-m-d H:i:s T');
}

return $results;
}
}

// 使用时区转换器
echo "\n=== 时区转换器示例 ===\n";

// 时区转换
$shanghaiTime = '2023-08-05 15:30:00';
$newYorkTime = TimezoneConverter::convert($shanghaiTime, 'Asia/Shanghai', 'America/New_York');
echo "上海时间 $shanghaiTime 转换为纽约时间: " . $newYorkTime->format('Y-m-d H:i:s T') . "\n";

// 获取时区偏移
$offset = TimezoneConverter::getTimezoneOffset('Asia/Shanghai');
echo "上海时区偏移: " . TimezoneConverter::formatOffset($offset) . "\n";

// 获取时区信息
$timezoneInfo = TimezoneConverter::getTimezoneInfo('Europe/London');
echo "\n伦敦时区信息:\n";
foreach ($timezoneInfo as $key => $value) {
echo "- $key: $value\n";
}

// 批量转换
$targetTimezones = ['America/New_York', 'Europe/London', 'Asia/Tokyo'];
$conversions = TimezoneConverter::convertMultiple($shanghaiTime, 'Asia/Shanghai', $targetTimezones);

echo "\n批量时区转换 (从上海时间 $shanghaiTime):\n";
foreach ($conversions as $timezone => $time) {
echo "- $timezone: $time\n";
}

// 获取亚洲时区列表
$asiaTimezones = TimezoneConverter::getTimezoneList('asia');
echo "\n亚洲时区数量: " . count($asiaTimezones) . "\n";
echo "前5个亚洲时区: " . implode(', ', array_slice($asiaTimezones, 0, 5)) . "\n";
?>

高级日期时间应用

1. 日期范围和周期处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<?php
// 日期范围和周期处理
echo "=== 日期范围和周期处理 ===\n";

class DateRangeManager {
private $startDate;
private $endDate;

public function __construct($startDate, $endDate) {
$this->startDate = new DateTime($startDate);
$this->endDate = new DateTime($endDate);

if ($this->startDate > $this->endDate) {
throw new InvalidArgumentException('开始日期不能大于结束日期');
}
}

// 获取日期范围内的所有日期
public function getAllDates($format = 'Y-m-d') {
$dates = [];
$current = clone $this->startDate;

while ($current <= $this->endDate) {
$dates[] = $current->format($format);
$current->add(new DateInterval('P1D'));
}

return $dates;
}

// 获取工作日
public function getWorkdays($format = 'Y-m-d') {
$workdays = [];
$current = clone $this->startDate;

while ($current <= $this->endDate) {
$dayOfWeek = $current->format('N');
if ($dayOfWeek >= 1 && $dayOfWeek <= 5) {
$workdays[] = $current->format($format);
}
$current->add(new DateInterval('P1D'));
}

return $workdays;
}

// 获取周末
public function getWeekends($format = 'Y-m-d') {
$weekends = [];
$current = clone $this->startDate;

while ($current <= $this->endDate) {
$dayOfWeek = $current->format('N');
if ($dayOfWeek >= 6) {
$weekends[] = $current->format($format);
}
$current->add(new DateInterval('P1D'));
}

return $weekends;
}

// 获取特定星期几的日期
public function getDayOfWeek($dayOfWeek, $format = 'Y-m-d') {
$dates = [];
$current = clone $this->startDate;

// 找到第一个指定星期几
while ($current->format('N') != $dayOfWeek && $current <= $this->endDate) {
$current->add(new DateInterval('P1D'));
}

// 收集所有指定星期几
while ($current <= $this->endDate) {
$dates[] = $current->format($format);
$current->add(new DateInterval('P7D'));
}

return $dates;
}

// 获取月份列表
public function getMonths($format = 'Y-m') {
$months = [];
$current = clone $this->startDate;
$current->setDate($current->format('Y'), $current->format('n'), 1);

while ($current <= $this->endDate) {
$months[] = $current->format($format);
$current->add(new DateInterval('P1M'));
}

return $months;
}

// 计算天数差
public function getDaysDiff() {
return $this->startDate->diff($this->endDate)->days;
}

// 判断日期是否在范围内
public function contains($date) {
$checkDate = new DateTime($date);
return $checkDate >= $this->startDate && $checkDate <= $this->endDate;
}

// 获取范围统计
public function getStatistics() {
$allDates = $this->getAllDates();
$workdays = $this->getWorkdays();
$weekends = $this->getWeekends();

return [
'total_days' => count($allDates),
'workdays' => count($workdays),
'weekends' => count($weekends),
'weeks' => ceil(count($allDates) / 7),
'months' => count($this->getMonths()),
'start_date' => $this->startDate->format('Y-m-d'),
'end_date' => $this->endDate->format('Y-m-d')
];
}
}

// 使用日期范围管理器
echo "=== 日期范围管理器示例 ===\n";

$range = new DateRangeManager('2023-08-01', '2023-08-31');

// 获取统计信息
$stats = $range->getStatistics();
echo "8月份统计信息:\n";
foreach ($stats as $key => $value) {
$labels = [
'total_days' => '总天数',
'workdays' => '工作日',
'weekends' => '周末天数',
'weeks' => '周数',
'months' => '月数',
'start_date' => '开始日期',
'end_date' => '结束日期'
];
echo "- {$labels[$key]}: $value\n";
}

// 获取所有周一
$mondays = $range->getDayOfWeek(1);
echo "\n8月份所有周一: " . implode(', ', $mondays) . "\n";

// 获取工作日
$workdays = $range->getWorkdays();
echo "8月份工作日数量: " . count($workdays) . "\n";
echo "前5个工作日: " . implode(', ', array_slice($workdays, 0, 5)) . "\n";

// 判断日期是否在范围内
echo "\n日期范围检查:\n";
echo "2023-08-15 在范围内: " . ($range->contains('2023-08-15') ? '是' : '否') . "\n";
echo "2023-09-01 在范围内: " . ($range->contains('2023-09-01') ? '是' : '否') . "\n";
?>

2. 定时任务和周期计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
<?php
// 定时任务和周期计算
echo "=== 定时任务和周期计算 ===\n";

class CronScheduler {

// 解析Cron表达式
public static function parseCronExpression($expression) {
$parts = explode(' ', trim($expression));

if (count($parts) !== 5) {
throw new InvalidArgumentException('Cron表达式必须包含5个部分');
}

return [
'minute' => $parts[0],
'hour' => $parts[1],
'day' => $parts[2],
'month' => $parts[3],
'weekday' => $parts[4]
];
}

// 计算下次执行时间
public static function getNextRunTime($cronExpression, $fromTime = null) {
$fromTime = $fromTime ?: new DateTime();
$cron = self::parseCronExpression($cronExpression);

// 简化版本,只处理基本情况
$nextRun = clone $fromTime;
$nextRun->add(new DateInterval('PT1M')); // 从下一分钟开始

// 这里应该有完整的Cron解析逻辑
// 为了演示,我们返回一个简单的计算结果

return $nextRun;
}

// 生成常用的Cron表达式
public static function generateCronExpression($type, $params = []) {
switch ($type) {
case 'every_minute':
return '* * * * *';
case 'every_hour':
$minute = $params['minute'] ?? 0;
return "$minute * * * *";
case 'daily':
$hour = $params['hour'] ?? 0;
$minute = $params['minute'] ?? 0;
return "$minute $hour * * *";
case 'weekly':
$hour = $params['hour'] ?? 0;
$minute = $params['minute'] ?? 0;
$weekday = $params['weekday'] ?? 0;
return "$minute $hour * * $weekday";
case 'monthly':
$hour = $params['hour'] ?? 0;
$minute = $params['minute'] ?? 0;
$day = $params['day'] ?? 1;
return "$minute $hour $day * *";
default:
throw new InvalidArgumentException('不支持的类型');
}
}

// 获取Cron表达式描述
public static function describeCronExpression($expression) {
$descriptions = [
'* * * * *' => '每分钟执行',
'0 * * * *' => '每小时执行',
'0 0 * * *' => '每天午夜执行',
'0 0 * * 0' => '每周日午夜执行',
'0 0 1 * *' => '每月1号午夜执行',
'0 0 1 1 *' => '每年1月1号午夜执行'
];

if (isset($descriptions[$expression])) {
return $descriptions[$expression];
}

$cron = self::parseCronExpression($expression);
$desc = [];

// 分钟
if ($cron['minute'] === '*') {
$desc[] = '每分钟';
} else {
$desc[] = "第{$cron['minute']}分钟";
}

// 小时
if ($cron['hour'] === '*') {
$desc[] = '每小时';
} else {
$desc[] = "{$cron['hour']}点";
}

// 日期
if ($cron['day'] !== '*') {
$desc[] = "{$cron['day']}号";
}

// 月份
if ($cron['month'] !== '*') {
$desc[] = "{$cron['month']}月";
}

// 星期
if ($cron['weekday'] !== '*') {
$weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
$desc[] = $weekdays[$cron['weekday']] ?? "星期{$cron['weekday']}";
}

return implode(' ', $desc) . ' 执行';
}
}

// 周期任务管理器
class RecurringTaskManager {
private $tasks = [];

// 添加任务
public function addTask($name, $cronExpression, $callback) {
$this->tasks[$name] = [
'cron' => $cronExpression,
'callback' => $callback,
'last_run' => null,
'next_run' => CronScheduler::getNextRunTime($cronExpression),
'run_count' => 0
];
}

// 执行到期任务
public function runDueTasks() {
$now = new DateTime();
$executedTasks = [];

foreach ($this->tasks as $name => &$task) {
if ($task['next_run'] <= $now) {
// 执行任务
if (is_callable($task['callback'])) {
call_user_func($task['callback']);
}

// 更新任务信息
$task['last_run'] = clone $now;
$task['next_run'] = CronScheduler::getNextRunTime($task['cron'], $now);
$task['run_count']++;

$executedTasks[] = $name;
}
}

return $executedTasks;
}

// 获取任务状态
public function getTaskStatus($name = null) {
if ($name) {
return $this->tasks[$name] ?? null;
}

return $this->tasks;
}

// 移除任务
public function removeTask($name) {
unset($this->tasks[$name]);
}

// 获取下次执行时间
public function getNextExecutionTime() {
$nextTimes = [];

foreach ($this->tasks as $name => $task) {
$nextTimes[$name] = $task['next_run'];
}

if (empty($nextTimes)) {
return null;
}

return min($nextTimes);
}
}

// 使用定时任务调度器
echo "=== 定时任务调度器示例 ===\n";

// 生成Cron表达式
$cronExpressions = [
'every_minute' => CronScheduler::generateCronExpression('every_minute'),
'daily_9am' => CronScheduler::generateCronExpression('daily', ['hour' => 9, 'minute' => 0]),
'weekly_monday' => CronScheduler::generateCronExpression('weekly', ['hour' => 8, 'minute' => 30, 'weekday' => 1]),
'monthly_1st' => CronScheduler::generateCronExpression('monthly', ['hour' => 0, 'minute' => 0, 'day' => 1])
];

echo "生成的Cron表达式:\n";
foreach ($cronExpressions as $type => $expression) {
$description = CronScheduler::describeCronExpression($expression);
echo "- $type: $expression ($description)\n";
}

// 创建任务管理器
$taskManager = new RecurringTaskManager();

// 添加任务
$taskManager->addTask('backup', '0 2 * * *', function() {
echo "执行数据库备份任务\n";
});

$taskManager->addTask('cleanup', '0 3 * * 0', function() {
echo "执行日志清理任务\n";
});

$taskManager->addTask('report', '0 9 1 * *', function() {
echo "生成月度报告\n";
});

// 获取任务状态
echo "\n任务状态:\n";
$tasks = $taskManager->getTaskStatus();
foreach ($tasks as $name => $task) {
echo "- $name: 下次执行 " . $task['next_run']->format('Y-m-d H:i:s') .
", 已执行 {$task['run_count']} 次\n";
}

// 获取下次执行时间
$nextExecution = $taskManager->getNextExecutionTime();
if ($nextExecution) {
echo "\n最近的任务执行时间: " . $nextExecution->format('Y-m-d H:i:s') . "\n";
}
?>

3. 日历和节假日处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
<?php
// 日历和节假日处理
echo "=== 日历和节假日处理 ===\n";

class CalendarManager {
private $holidays = [];

public function __construct() {
$this->initializeHolidays();
}

// 初始化节假日
private function initializeHolidays() {
// 固定节假日
$this->holidays = [
'01-01' => '元旦',
'02-14' => '情人节',
'03-08' => '妇女节',
'03-12' => '植树节',
'04-01' => '愚人节',
'05-01' => '劳动节',
'05-04' => '青年节',
'06-01' => '儿童节',
'07-01' => '建党节',
'08-01' => '建军节',
'09-10' => '教师节',
'10-01' => '国庆节',
'12-25' => '圣诞节'
];
}

// 添加节假日
public function addHoliday($date, $name) {
$key = date('m-d', strtotime($date));
$this->holidays[$key] = $name;
}

// 检查是否为节假日
public function isHoliday($date) {
$key = date('m-d', strtotime($date));
return isset($this->holidays[$key]);
}

// 获取节假日名称
public function getHolidayName($date) {
$key = date('m-d', strtotime($date));
return $this->holidays[$key] ?? null;
}

// 获取月份日历
public function getMonthCalendar($year, $month) {
$firstDay = new DateTime("$year-$month-01");
$lastDay = clone $firstDay;
$lastDay->setDate($year, $month, $firstDay->format('t'));

$calendar = [];
$current = clone $firstDay;

// 填充月初空白
$startWeekday = $current->format('N') % 7; // 0=周日, 1=周一...
for ($i = 0; $i < $startWeekday; $i++) {
$calendar[] = null;
}

// 填充月份天数
while ($current <= $lastDay) {
$dateStr = $current->format('Y-m-d');
$calendar[] = [
'date' => $dateStr,
'day' => $current->format('j'),
'is_weekend' => $current->format('N') >= 6,
'is_holiday' => $this->isHoliday($dateStr),
'holiday_name' => $this->getHolidayName($dateStr),
'is_today' => $dateStr === date('Y-m-d')
];
$current->add(new DateInterval('P1D'));
}

return [
'year' => $year,
'month' => $month,
'month_name' => $firstDay->format('F'),
'days' => $calendar,
'weeks' => array_chunk($calendar, 7)
];
}

// 获取年份所有节假日
public function getYearHolidays($year) {
$yearHolidays = [];

foreach ($this->holidays as $dateKey => $name) {
$fullDate = "$year-$dateKey";
$yearHolidays[] = [
'date' => $fullDate,
'name' => $name,
'weekday' => date('l', strtotime($fullDate))
];
}

return $yearHolidays;
}

// 计算工作日(排除周末和节假日)
public function calculateWorkdays($startDate, $endDate) {
$start = new DateTime($startDate);
$end = new DateTime($endDate);
$workdays = 0;

$current = clone $start;
while ($current <= $end) {
$dateStr = $current->format('Y-m-d');
$dayOfWeek = $current->format('N');

// 不是周末且不是节假日
if ($dayOfWeek < 6 && !$this->isHoliday($dateStr)) {
$workdays++;
}

$current->add(new DateInterval('P1D'));
}

return $workdays;
}

// 获取下一个工作日
public function getNextWorkday($date = null) {
$current = new DateTime($date ?: 'now');
$current->add(new DateInterval('P1D'));

while (true) {
$dateStr = $current->format('Y-m-d');
$dayOfWeek = $current->format('N');

if ($dayOfWeek < 6 && !$this->isHoliday($dateStr)) {
return $current;
}

$current->add(new DateInterval('P1D'));
}
}

// 农历转换(简化版)
public function getLunarDate($date) {
// 这里应该有完整的农历转换算法
// 为了演示,返回模拟数据
return [
'lunar_year' => '癸卯年',
'lunar_month' => '六月',
'lunar_day' => '十八',
'zodiac' => '兔',
'solar_term' => '立秋'
];
}
}

// 使用日历管理器
echo "=== 日历管理器示例 ===\n";

$calendar = new CalendarManager();

// 检查节假日
$testDates = ['2023-01-01', '2023-05-01', '2023-10-01', '2023-08-05'];
echo "节假日检查:\n";
foreach ($testDates as $date) {
$isHoliday = $calendar->isHoliday($date);
$holidayName = $calendar->getHolidayName($date);
echo "- $date: " . ($isHoliday ? "节假日 ($holidayName)" : "普通日期") . "\n";
}

// 计算工作日
$workdays = $calendar->calculateWorkdays('2023-08-01', '2023-08-31');
echo "\n2023年8月工作日数量: $workdays 天\n";

// 获取下一个工作日
$nextWorkday = $calendar->getNextWorkday('2023-08-05');
echo "2023-08-05的下一个工作日: " . $nextWorkday->format('Y-m-d') . "\n";

// 获取月份日历
$monthCalendar = $calendar->getMonthCalendar(2023, 8);
echo "\n2023年8月日历:\n";
echo "年份: {$monthCalendar['year']}, 月份: {$monthCalendar['month']} ({$monthCalendar['month_name']})\n";

echo "日历布局:\n";
echo "日 一 二 三 四 五 六\n";
foreach ($monthCalendar['weeks'] as $week) {
foreach ($week as $day) {
if ($day === null) {
echo " ";
} else {
$marker = '';
if ($day['is_today']) $marker .= '*';
if ($day['is_holiday']) $marker .= 'H';
if ($day['is_weekend']) $marker .= 'W';

printf("%2d%s", $day['day'], $marker ? "($marker)" : " ");
}
}
echo "\n";
}

echo "\n图例: * = 今天, H = 节假日, W = 周末\n";

// 获取年度节假日
$yearHolidays = $calendar->getYearHolidays(2023);
echo "\n2023年节假日列表:\n";
foreach (array_slice($yearHolidays, 0, 5) as $holiday) {
echo "- {$holiday['date']} ({$holiday['weekday']}): {$holiday['name']}\n";
}

// 农历信息
$lunarInfo = $calendar->getLunarDate('2023-08-05');
echo "\n2023-08-05 农历信息:\n";
foreach ($lunarInfo as $key => $value) {
$labels = [
'lunar_year' => '农历年',
'lunar_month' => '农历月',
'lunar_day' => '农历日',
'zodiac' => '生肖',
'solar_term' => '节气'
];
echo "- {$labels[$key]}: $value\n";
}
?>

性能优化和最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<?php
// 日期时间性能优化
echo "=== 性能优化和最佳实践 ===\n";

class DateTimeOptimizer {

// 缓存时区对象
private static $timezoneCache = [];

// 获取缓存的时区对象
public static function getTimezone($timezone) {
if (!isset(self::$timezoneCache[$timezone])) {
self::$timezoneCache[$timezone] = new DateTimeZone($timezone);
}

return self::$timezoneCache[$timezone];
}

// 批量日期格式化
public static function formatDates($dates, $format = 'Y-m-d H:i:s') {
$formatted = [];

foreach ($dates as $date) {
if ($date instanceof DateTime) {
$formatted[] = $date->format($format);
} elseif (is_numeric($date)) {
$formatted[] = date($format, $date);
} else {
$formatted[] = date($format, strtotime($date));
}
}

return $formatted;
}

// 性能测试
public static function benchmarkDateOperations($iterations = 1000) {
$results = [];

// 测试 DateTime vs date()
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$dt = new DateTime();
$formatted = $dt->format('Y-m-d H:i:s');
}
$results['DateTime'] = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$formatted = date('Y-m-d H:i:s');
}
$results['date()'] = microtime(true) - $start;

// 测试时区转换
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$dt = new DateTime('now', new DateTimeZone('Asia/Shanghai'));
}
$results['new_timezone'] = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$dt = new DateTime('now', self::getTimezone('Asia/Shanghai'));
}
$results['cached_timezone'] = microtime(true) - $start;

return $results;
}

// 内存使用优化
public static function optimizeMemoryUsage() {
$tips = [
'1. 重用DateTime对象而不是创建新对象',
'2. 缓存DateTimeZone对象',
'3. 使用时间戳进行简单计算',
'4. 避免在循环中创建DateTime对象',
'5. 使用静态方法减少对象创建开销'
];

return $tips;
}

// 最佳实践建议
public static function getBestPractices() {
return [
'timezone' => [
'总是明确指定时区',
'在应用启动时设置默认时区',
'存储UTC时间,显示时转换为本地时区',
'缓存时区对象避免重复创建'
],
'formatting' => [
'使用ISO 8601格式进行数据交换',
'为用户界面提供本地化格式',
'避免在数据库中存储格式化字符串',
'使用常量定义常用格式'
],
'calculation' => [
'使用DateTime类进行复杂计算',
'简单计算可以使用时间戳',
'注意闰年和月份天数差异',
'处理夏令时变化'
],
'validation' => [
'验证用户输入的日期格式',
'检查日期范围的合理性',
'处理无效日期的异常情况',
'考虑不同地区的日期格式习惯'
]
];
}
}

// 使用性能优化器
echo "=== 性能优化器示例 ===\n";

// 性能测试
$benchmarkResults = DateTimeOptimizer::benchmarkDateOperations(100);
echo "性能测试结果 (100次迭代):\n";
foreach ($benchmarkResults as $method => $time) {
echo "- $method: " . round($time * 1000, 2) . "ms\n";
}

// 批量格式化
$dates = [
new DateTime(),
time(),
'2023-08-05 15:30:00',
strtotime('+1 day')
];

$formatted = DateTimeOptimizer::formatDates($dates, 'Y-m-d H:i:s');
echo "\n批量格式化结果:\n";
foreach ($formatted as $i => $date) {
echo "- 日期 $i: $date\n";
}

// 内存优化建议
echo "\n内存优化建议:\n";
$memoryTips = DateTimeOptimizer::optimizeMemoryUsage();
foreach ($memoryTips as $tip) {
echo "- $tip\n";
}

// 最佳实践
echo "\n最佳实践建议:\n";
$bestPractices = DateTimeOptimizer::getBestPractices();
foreach ($bestPractices as $category => $practices) {
$categoryNames = [
'timezone' => '时区处理',
'formatting' => '格式化',
'calculation' => '计算',
'validation' => '验证'
];

echo "\n{$categoryNames[$category]}:\n";
foreach ($practices as $practice) {
echo " - $practice\n";
}
}

// 常用日期格式常量
class DateFormats {
const ISO_DATE = 'Y-m-d';
const ISO_DATETIME = 'Y-m-d H:i:s';
const ISO_DATETIME_MS = 'Y-m-d H:i:s.u';
const RFC_2822 = 'r';
const ATOM = 'c';
const CHINESE_DATE = 'Y年m月d日';
const CHINESE_DATETIME = 'Y年m月d日 H:i:s';
const US_DATE = 'm/d/Y';
const EU_DATE = 'd/m/Y';
const TIME_12H = 'g:i A';
const TIME_24H = 'H:i:s';
}

echo "\n常用日期格式常量:\n";
$now = new DateTime();
$formats = [
'ISO_DATE' => DateFormats::ISO_DATE,
'ISO_DATETIME' => DateFormats::ISO_DATETIME,
'CHINESE_DATE' => DateFormats::CHINESE_DATE,
'US_DATE' => DateFormats::US_DATE,
'TIME_12H' => DateFormats::TIME_12H
];

foreach ($formats as $name => $format) {
echo "- $name: " . $now->format($format) . "\n";
}
?>

总结

通过本文的学习,我们全面掌握了PHP日期时间处理的各个方面:

关键要点

  1. 基础操作: 掌握了date()函数、DateTime类和时间戳的使用
  2. 时区处理: 学会了时区转换和多时区应用开发
  3. 高级应用: 了解了日期范围、定时任务和日历系统的实现
  4. 性能优化: 掌握了日期时间操作的性能优化技巧

最佳实践

  • 总是明确指定时区,避免时区混乱
  • 使用DateTime类处理复杂的日期计算
  • 缓存时区对象提高性能
  • 存储UTC时间,显示时转换为本地时区
  • 验证用户输入的日期格式

实用技巧

  • 使用相对时间格式提高用户体验
  • 实现工作日计算排除周末和节假日
  • 构建灵活的定时任务调度系统
  • 处理不同地区的日期格式习惯
  • 优化大量日期操作的性能

掌握这些日期时间处理技巧,将帮助你构建更专业、更用户友好的时间相关功能。记住,日期时间处理看似简单,但涉及时区、格式化、计算等多个方面,需要仔细考虑各种边界情况。

本站由 提供部署服务