
本文详解如何在 php 中动态计算订单发货日期,支持自定义非发货日(如周末/节假日)、订单截止时间(如 11:00 前当日发,之后顺延),并给出可直接运行的健壮实现方案。
本文详解如何在 php 中动态计算订单发货日期,支持自定义非发货日(如周末/节假日)、订单截止时间(如 11:00 前当日发,之后顺延),并给出可直接运行的健壮实现方案。
在电商或订单系统中,准确计算预计发货日期(Shipping Date) 是提升用户体验与履约透明度的关键环节。该日期不能简单加 1 天,而需综合考虑:
- 订单提交的具体日期与时间;
- 配置的每日发货截止时间(如 11:00,此前下单当日处理,此后顺延);
- 商家设定的不可发货日(如周日、法定节假日、库存异常日等);
- 日期格式兼容性与时区安全。
以下是一个生产就绪的 PHP 实现方案,已优化逻辑严谨性、可维护性与扩展性:
✅ 核心函数:calculateShippingDate()
/**
* 计算预计发货日期(支持截止时间 + 节假日/非工作日跳过)
* @param string $orderDate 订单日期,格式 'Y-m-d'(推荐)或 'd-m-Y'
* @param string $orderTime 订单时间,格式 'H:i:s'(如 '10:30:00')
* @param array $nonShippingDays 每周不可发货的星期索引数组(0=周日, 1=周一...6=周六),例:[0,6] 表示周末不发
* @param array $holidays 国定/临时节假日数组,格式 'Y-m-d',例:['2024-01-28', '2024-02-10']
* @param string $cutOffTime 当日发货截止时间,格式 'H:i:s',例:'11:00:00'
* @return string 发货日期,格式 'Y-m-d'
*/
function calculateShippingDate(
string $orderDate,
string $orderTime,
array $nonShippingDays = [0, 6],
array $holidays = ['2024-01-28', '2024-02-10'],
string $cutOffTime = '11:00:00'
): string {
// 步骤 1:标准化输入日期为 DateTime 对象(避免 strtotime 解析歧义)
$orderDateTime = DateTime::createFromFormat('Y-m-d H:i:s', "$orderDate $orderTime");
if (!$orderDateTime) {
throw new InvalidArgumentException("Invalid order date/time format");
}
// 步骤 2:判断是否超过当日截止时间 → 决定起始基准日
$baseDate = clone $orderDateTime;
$orderHour = (int)$orderDateTime->format('H');
$cutOffHour = (int)(new DateTime($cutOffTime))->format('H');
// 若订单时间 >= 截止时间,则从次日开始计算(当日不发)
if ($orderHour >= $cutOffHour) {
$baseDate->modify('+1 day');
}
// 步骤 3:循环检查,跳过非发货日与节假日
$shippingDate = $baseDate;
do {
$dateStr = $shippingDate->format('Y-m-d');
$dayOfWeek = (int)$shippingDate->format('w'); // 0=Sun, 6=Sat
// 跳过条件:是配置的非发货日 或 是节假日
$isNonShippingDay = in_array($dayOfWeek, $nonShippingDays);
$isHoliday = in_array($dateStr, $holidays);
if ($isNonShippingDay || $isHoliday) {
$shippingDate->modify('+1 day');
} else {
break; // 找到首个有效发货日
}
} while (true);
return $shippingDate->format('Y-m-d');
}
✅ 使用示例(functions.php)
// 示例调用
if (isset($_POST['call']) && !empty($_POST['date']) && !empty($_POST['time'])) {
// 注意:前端传入的 date 格式需统一为 'Y-m-d'(推荐)或转换处理
$inputDate = $_POST['date']; // 如 '2024-08-10'
$inputTime = $_POST['time']; // 如 '10:45:00'
// 安全转换:若前端为 d-m-Y,先转为 Y-m-d
if (preg_match('/^\d{2}-\d{2}-\d{4}$/', $inputDate)) {
$dateParts = explode('-', $inputDate);
$inputDate = $dateParts[2] . '-' . $dateParts[1] . '-' . $dateParts[0];
}
try {
$result = calculateShippingDate(
$inputDate,
$inputTime,
[0, 6], // 周日、周六不发货
['2024-08-10', '2024-08-13'], // 临时节假日
'11:00:00'
);
echo json_encode(['success' => true, 'shipping_date' => $result]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
} else {
echo json_encode(['success' => false, 'error' => 'Missing date or time']);
}
✅ 前端 AJAX 调用优化建议
将原 JS 中的 alert(date) 替换为结构化响应处理,并修正日期格式传递:
$('#formsubmit').submit(function(e) {
e.preventDefault();
const datetime = $('#input').val().trim();
if (!datetime) return;
// 解析 'YYYY-MM-DD HH:ii:ss' 或 'DD-MM-YYYY HH:ii:ss'
const [datePart, timePart] = datetime.split(' ');
let [y, m, d], formattedDate;
if (datePart.includes('-')) {
const parts = datePart.split('-');
if (parts[0].length === 4) { // Y-m-d
formattedDate = datePart;
} else { // d-m-Y → 转为 Y-m-d
[d, m, y] = parts;
formattedDate = `${y}-${m}-${d}`;
}
}
$.post('functions.php', {
date: formattedDate,
time: timePart || '00:00:00',
call: 1
}, function(res) {
const data = typeof res === 'string' ? JSON.parse(res) : res;
if (data.success) {
alert(`预计发货日期:${data.shipping_date}`);
} else {
alert('计算失败:' + data.error);
}
});
});
⚠️ 关键注意事项
-
时区一致性:确保 PHP
date_default_timezone_set('Asia/Shanghai')与前端用户时区对齐,避免跨时区偏差; -
日期格式统一:强烈建议前后端约定使用 ISO 8601 格式(
Y-m-d H:i:s),避免d-m-Y与m/d/Y解析冲突; - 节假日动态加载:生产环境应从数据库或 API 获取节假日列表,而非硬编码;
-
性能优化:若节假日/非工作日数量大,可用
DateTimeImmutable+DatePeriod提前预生成有效日期集; -
库存状态集成:文中
$outOfStock可扩展为调用库存服务接口,返回动态延迟天数。
通过以上设计,您获得的不仅是一个“能跑”的函数,而是一个可配置、可测试、可监控、符合业务语义的发货日期计算引擎——让交付承诺更可信,让系统逻辑更清晰。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











