倒計時本質是 目標時間戳 - 當前時間戳,再換算為天/時/分/秒。關鍵函數:strtotime() 解析目標日期,time() 獲取當前秒數。
<?php
// 目標:2024 年春節(農曆正月初一)對應公曆 2024-02-10
$target = strtotime('2024-02-10 00:00:00');
$now = time();
$days = ceil(($target - $now) / 86400); // 86400 = 24*60*60
echo "距離 2024 春節還有 $days 天!";
// 輸出示例:距離 2024 春節還有 26 天!
?>
PHP 只負責輸出初始剩餘秒數,前端用 setInterval 實時更新:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>春節倒計時</title>
<style>
.countdown { font-size: 2rem; font-family: monospace; color: #e74c3c; }
.unit { margin: 0 0.5rem; }
</style>
</head>
<body>
<div class="countdown" id="cd"></div>
<script>
// PHP 直接輸出初始總秒數
let totalSeconds = <?= max(0, strtotime('2024-02-10 00:00:00') - time()) ?>;
const el = document.getElementById('cd');
function update() {
if (totalSeconds <= 0) {
el.innerHTML = '🎉 春節快樂!';
clearInterval(timer);
return;
}
const d = Math.floor(totalSeconds / 86400);
const h = Math.floor((totalSeconds % 86400) / 3600);
const m = Math.floor((totalSeconds % 3600) / 60);
const s = totalSeconds % 60;
el.innerHTML =
`${d}天
${h.toString().padStart(2,'0')}時
${m.toString().padStart(2,'0')}分
${s.toString().padStart(2,'0')}秒`;
totalSeconds--;
}
update();
const timer = setInterval(update, 1000);
</script>
</body>
</html>
<?php
class Countdown {
private int $targetTimestamp;
public function __construct(string $targetDateTime, string $timezone = 'Asia/Shanghai') {
$this->targetTimestamp = (new DateTime($targetDateTime, new DateTimeZone($timezone)))->getTimestamp();
}
/** 獲取剩餘秒數(負數表示已過期) */
public function getRemainingSeconds(): int {
return $this->targetTimestamp - time();
}
/** 獲取人類可讀數組 */
public function getHumanArray(): array {
$sec = max(0, $this->getRemainingSeconds());
return [
'days' => (int)($sec / 86400),
'hours' => (int)(($sec % 86400) / 3600),
'mins' => (int)(($sec % 3600) / 60),
'secs' => $sec % 60,
'total_seconds' => $sec,
'expired' => $this->getRemainingSeconds() < 0,
];
}
/** 格式化字符串 */
public function format(string $template = '{days}天 {hours}時 {mins}分 {secs}秒'): string {
if ($this->getRemainingSeconds() < 0) {
return '已結束';
}
$a = $this->getHumanArray();
return strtr($template, [
'{days}' => $a['days'],
'{hours}' => str_pad($a['hours'], 2, '0', STR_PAD_LEFT),
'{mins}' => str_pad($a['mins'], 2, '0', STR_PAD_LEFT),
'{secs}' => str_pad($a['secs'], 2, '0', STR_PAD_LEFT),
]);
}
/** 直接輸出 JS 初始化代碼 */
public function jsInit(string $elementId = 'countdown'): string {
$sec = max(0, $this->getRemainingSeconds());
return <<format(); // 26天 14時 30分 05秒
// 前端頁面引入
// <div id="cd"></div>
// <script> <?= $cd->jsInit('cd') ?> </script>
?>
春節、中秋等農曆節日日期每年不同,需用成熟庫轉換:
<?php
// composer require overture/chinese-calendar
use Overture\ChineseCalendar\ChineseCalendar;
$cc = new ChineseCalendar();
// 獲取 2024 年農曆正月初一的公曆日期
$gregorian = $cc->lunarToGregorian(2024, 1, 1); // [2024, 2, 10]
$target = "{$gregorian[0]}-{$gregorian[1]}-{$gregorian[2]} 00:00:00";
$cd = new Countdown($target);
echo $cd->format('距離 {days}天 {hours}時 {mins}分 {secs}秒 過年!');
?>
| 問題 | 解決 |
|---|---|
| 服務器與用戶時區不同 | 構造函數傳入用戶時區,或前端用 JS 本地時間計算 |
| 閏秒導致 1 秒誤差 | 民用倒計時可忽略,精密場景用 NTP 同步 |
| 緩存頁面導致時間靜止 | 倒計時必須用 JS 前端動態更新,PHP 只提供初始值 |