本博文写于:20241027
最近在一点点的小折腾博客,这不刚好看到了博客的统计运行时间代码,决定稍微改造一下好适应现在的需求,顺带也分享一下吧。
我原本的代码是通过获取时间戳再数学运算来计算差值统计运行时间的,其实完全够用,只是我想改成更加精确的时间,要考虑闰年之类的,所以就改了一下成博客现在这样的了。
先看代码,为了方便理解加了一些注释:
<div id="RunTime" style="display: block;"></div>
<script>
if (!window.__RunTimeTimerInit__) {
// 防止重复初始化,尤其在 PJAX 页面加载时
window.__RunTimeTimerInit__ = true;
// 命名空间管理全局变量,避免冲突
if (!window.__RunTimeTimer__) window.__RunTimeTimer__ = {
BootDate: new Date("2015-01-08T08:30:10+08:00"), // 站点的创建时间
intervalId: null, // 计时器 ID
runTimeElement: null, // 显示运行时间的 DOM 元素
observer: null // IntersectionObserver 实例
};
const timer = window.__RunTimeTimer__;
// 计算 UTC 时间差
function calculateTimeDifference(now) {
let years = now.getUTCFullYear() - timer.BootDate.getUTCFullYear();
let months = now.getUTCMonth() - timer.BootDate.getUTCMonth();
let days = now.getUTCDate() - timer.BootDate.getUTCDate();
let hours = now.getUTCHours() - timer.BootDate.getUTCHours();
let minutes = now.getUTCMinutes() - timer.BootDate.getUTCMinutes();
let seconds = now.getUTCSeconds() - timer.BootDate.getUTCSeconds();
// 借位处理
if (seconds < 0) { seconds += 60; minutes--; }
if (minutes < 0) { minutes += 60; hours--; }
if (hours < 0) { hours += 24; days--; }
if (days < 0) {
// 上个月最后一天的天数,用于借位
const lastMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0));
days += lastMonth.getUTCDate();
months--;
}
if (months < 0) { months += 12; years--; }
return { years, months, days, hours, minutes, seconds };
}
// 更新运行时间显示
function ShowRunTime() {
if (!timer.runTimeElement) return; // 如果元素不存在,直接返回
const now = new Date();
const { years, months, days, hours, minutes, seconds } = calculateTimeDifference(now);
// 更新 DOM 文本
timer.runTimeElement.textContent =
`本站在博主的反复折腾下已勉强运行:${years}年${months}月${days}天${hours}时${minutes}分${seconds}秒`;
}
// 启动计时器
function startTimer() {
// 避免重复创建计时器
if (!timer.intervalId) {
ShowRunTime(); // 立即更新一次
timer.intervalId = setInterval(ShowRunTime, 1000); // 每秒更新一次
}
}
// 停止计时器
function stopTimer() {
if (timer.intervalId) {
clearInterval(timer.intervalId);
timer.intervalId = null;
}
}
// 初始化观察元素(IntersectionObserver)
function initObserver() {
// 获取运行时间元素
timer.runTimeElement = document.getElementById('RunTime');
if (!timer.runTimeElement) return; // 如果元素不存在,返回
// 断开旧观察器,防止内存泄漏
if (timer.observer) timer.observer.disconnect();
// 创建新的 IntersectionObserver,监控元素可见性
timer.observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 元素进入视口,启动计时器
startTimer();
} else {
// 元素离开视口,停止计时器(可选优化)
stopTimer();
}
});
});
// 开始观察运行时间元素
timer.observer.observe(timer.runTimeElement);
// 首屏可见性检查,只要元素任意部分可见即可启动计时器
const rect = timer.runTimeElement.getBoundingClientRect();
if (rect.bottom >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight)) {
startTimer();
}
}
// 初始执行
initObserver();
// PJAX 适配:当 PJAX 完成更新 DOM 后重新观察元素
$(document).on('pjax:complete', initObserver);
// 浏览器前进/回退适配:监听原生 popstate 事件,重新初始化观察器
window.addEventListener('popstate', initObserver);
}
</script>这个代码主要有三个特点:
- 用日期数据计算差值,比时间戳的纯数学运算更精确一些
- 用了监听页面可见性的 Intersection Observer API,只有当内容可见(也就是滑到了显示统计时间的地方)才会运行统计脚本,小小的体验和性能优化
- 对用了 pjax 技术的博客(比如希卡米)进行支持,首次加载或者 pjax 加载页面都能正常生效
因为加了一些注释所以应该挺好理解的,就只说下这三个特点的前两点吧。
以前看过一些人的分享的代码包括原本我自己用的,都是通过时间戳的差来计算时间间隔的,因为时间戳其实就是一串数字,是表示的从 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数,所以只要算出了两个日期之间差了多少秒,再直接按照类似 60 秒为 1 分钟,60 分钟为 1 小时,24 小时为 1 天这样子去除一下就能知道距离现在时间过去了多久。只是,因为这个是简单的数学计算而没有考虑到闰年的问题,如果中间有些年是 366 天的话就有点不准确了。因为这个原因,所以我改为了 js 里按照日期来计算时间差异的方式,然后后面通过借位的方式计算精确的时间,这样就不用怕闰年的问题了。
说起借位,其实和咱们平时的减法也差不多理解,减法小于 0 时往前借一位,我这个也是类似的,只不过是借的时间。
第二个说的 Intersection Observer 就是借助了 JavaScript 的 API 检测指定的元素是否在可视范围内,如果在就运行计算时间的代码,如果不在就停止运行。之所以这样子弄是因为因为也没多少人会特意滑到统计时间的位置一直盯着看吧,大概率大家也都是放在页面最底部的,本来第一时间也不会看到,如果一直让这个脚本在持续运行的话,虽然影响不大但终归还是浪费了点性能,所以我就想了这么个法子只在看到统计时间这里的时候才真正的开始计算时间,不看了就立马停止。
以上就是对这个代码的解释了,大家如果要用的话,因为提供的是 HTML 代码,可以直接复制到自己想要使用的网页,比如像我这样的 WordPress 博客就可以直接复制到主题的 footer.php 文件里,找到要插入统计时间的位置直接插入这个代码就行了。
用了代码的各位,也别忘了修改下代码开头部分里的时间(2015-01-08T00:30:10Z),这个是写的我的博客启用时间,要改成自己的,但是格式不要乱改动,T的后面接的是小时分钟秒,中间的:要用英文的不要弄成中文的:了,最后的+08:00 是时间偏移,我是根据东八区时间来算的所以用了+08:00,你也可以根据自己情况+或者-,如果你用的 UTC 时间就在最后直接写 Z。设置的时间只是为了精准确定自己网站初始运行的时间,用哪个时区的时间都不影响代码运行,也不会因为访问人的时区导致计算不准。
如果你想看看实际运行效果的话,直接看我博客最后面的页面源代码应该能发现我现在用的就是这段代码了,所以你访问我博客看到的就是实际演示效果………………纳尼,我博客快 10 年了?!
真是不看不知道一看吓一跳,以前只具体到了天数还没觉得多大问题,原来已经过了这么久了啊……先提前恭喜我自己一下,过去这么久还是觉得互联网非常好玩。*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。






