发帖时间切换(xnx_posttime_toggle)
让用户在「我的-用户中心-基本资料」自由切换发帖时间显示格式:相对时间(如「3 分钟前」)或绝对时间(如「2026-07-04 15:30:00」),偏好按账号持久化保存,互不影响。
功能简介
XIUNOX BBS 默认在主帖(thread)页面以「相对时间」格式展示发帖时间(如「3 小时前」),便于快速感知新鲜度。但部分用户(尤其是归档查阅、数据核对场景)更希望看到精确的年月日时分秒。本插件在不修改核心代码的前提下,通过 Xiuno 的 hook 机制为每位登录用户提供独立的「时间显示偏好」开关,选择后立即在主帖发帖时间生效,且仅对当前账号起作用,不影响其他用户。
核心功能
两种时间格式切换
相对时间(默认):保留 Xiuno 原生
xn_date()输出,如「3 分钟前」「昨天 14:30」绝对时间:
Y-m-d H:i:s标准格式,如「2026-07-04 15:30:00」
按账号生效:偏好存储在
user表的xnx_ptt_mode字段,每个账号独立设置零侵入核心:通过
model_thread_format_endhook 在thread_format()函数末尾改写create_date_fmt,不修改任何核心文件可视化设置入口:在「我的-用户中心-基本资料」页面注入设置卡片,使用 htmx 提交,无刷新保存
CSRF 防护:表单包含
CsrfService::input()令牌,后端在CsrfService::check()之后处理多语言支持:内置简体中文、繁体中文、英文三种语言包
插件图标没设计,不喜欢设计图片😂
安装与启用
安装前提
XIUNOX BBS(XIUNO BBS X 重构版)4.5+
PHP 8.0+
MySQL(pdo_mysql 扩展)
user表有写入权限(用于新增xnx_ptt_mode字段)
安装步骤
将
xnx_posttime_toggle目录整体放入plugin/目录登录后台 → 「插件管理」→ 找到「发帖时间切换」
点击「安装」→ 系统自动执行
install.php,向user表添加xnx_ptt_mode字段点击「启用」→ 插件开始生效
卸载说明
后台「插件管理」→ 点击「禁用」
点击「卸载」→ 系统自动执行
uninstall.php,幂等删除xnx_ptt_mode字段卸载后所有用户的时间显示偏好会被清空,回退到默认相对时间
使用指南
用户端操作
登录账号
点击右上角用户名 → 进入「我的」
在「用户中心」中找到「基本资料」页面
滚动到「时间显示偏好」卡片
在「发帖时间显示格式」下拉框中选择:
相对时间(如 3分钟前):Xiuno 默认行为
绝对时间(如 2026-07-04 15:30:00):精确时间格式
点击「保存」按钮,页面顶部出现绿色「偏好已保存」提示
进入任意帖子详情页,主帖的发帖时间立即按新格式显示
生效范围
✅ 主帖(thread)的发帖时间
create_date_fmt❌ 回帖列表(post)的时间(保留 Xiuno 原生相对时间)
❌ 帖子列表页的时间(保留 Xiuno 原生相对时间)
❌ 游客(未登录用户):始终显示相对时间
注意事项
偏好按账号存储,同一账号在多设备登录会同步生效
切换后立即生效,无需清缓存
卸载插件后字段被删除,所有用户回退到默认相对时间
技术实现
数据库设计
向 user 表新增字段:
ALTER TABLE `xn_user` ADD COLUMN `xnx_ptt_mode` tinyint(1) unsigned
NOT NULL DEFAULT 0
COMMENT '发帖时间显示偏好:0=相对,1=绝对'
AFTER `logins`;
字段名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
|
|
| 0=相对时间,1=绝对时间 |
Hook 注册清单
conf.json 中 hooks_rank 注册 6 个 hook:
Hook 文件 | 触发位置 | 作用 |
|---|---|---|
|
| 改写 |
| 「基本资料」页面末尾 | 注入设置卡片 HTML |
| 「基本资料」POST 处理入口 | 拦截 |
| 简体中文语言包加载 | 注入 6 个 |
| 繁体中文语言包加载 | 注入 6 个 |
| 英文语言包加载 | 注入 6 个 |
时间格式改写逻辑
hook/model_thread_format_end.php 核心代码:
global $user;
$ptt_mode = isset($user['xnx_ptt_mode']) ? intval($user['xnx_ptt_mode']) : 0;
if ($ptt_mode === 1 && isset($thread['create_date'])) {
$thread['create_date_fmt'] = date('Y-m-d H:i:s', intval($thread['create_date']));
}
编译进
thread_format()函数末尾,$thread为引用参数仅当
xnx_ptt_mode === 1时才覆盖默认的相对时间游客(
$user为空)走默认分支,不受影响
前端表单(htmx 实现)
hook/my_profile_end.htm 注入一个独立 htmx 表单:
<form hx-post="{my_profile_url}" hx-target="#xnx-ptt-msg" hx-swap="innerHTML">
{CsrfService::input()}
<input type="hidden" name="xnx_ptt_action" value="save">
<select name="xnx_ptt_mode">...</select>
<button type="submit">保存</button>
</form>
使用
hx-post提交到「基本资料」URLhx-target="#xnx-ptt-msg"将响应 HTML 注入到消息容器hx-swap="innerHTML"替换容器内容后端返回纯 HTML 片段(
<div class="alert alert-success">偏好已保存</div>),符合 htmx 规范
后端保存逻辑
hook/my_profile_post_start.php 在 CsrfService::check() 之后、昵称/签名处理之前执行:
if (param('xnx_ptt_action') === 'save') {
$ptt_mode = intval(param('xnx_ptt_mode', 0));
if ($ptt_mode !== 0) $ptt_mode = 1; // 只允许 0 或 1
if (db_check_column_exists('user', 'xnx_ptt_mode')) {
user_update($uid, array('xnx_ptt_mode' => $ptt_mode));
}
echo '<div class="alert alert-success mb-3">偏好已保存</div>';
exit;
}
通过
xnx_ptt_action=save隐藏字段区分本表单与其他资料保存强制
xnx_ptt_mode只能为0或1,防注入二次校验字段是否存在,避免字段缺失时报错
调用
user_update()自动触发 cache_delete + hook,下次请求立即生效exit阻止后续资料处理逻辑,避免误改昵称等
幂等性保证
install.php:先db_check_column_exists检查,再ALTER TABLE ADD COLUMNuninstall.php:先db_check_column_exists检查,再ALTER TABLE DROP COLUMN重复安装/卸载不会报错,安全可重复执行
多语言支持
插件内置三种语言包,覆盖所有用户可见文案:
语言 | Hook 文件 | 适用地区 |
|---|---|---|
简体中文 |
| 中国大陆 |
繁体中文 |
| 港台地区 |
英文 |
| 海外用户 |
语言键清单
键名 | 简体中文 | 繁体中文 | 英文 |
|---|---|---|---|
| 时间显示偏好 | 時間顯示偏好 | Time Display Preference |
| 发帖时间显示格式 | 發帖時間顯示格式 | Post Time Format |
| 相对时间(如 3分钟前) | 相對時間(如 3分鐘前) | Relative (e.g., 3 minutes ago) |
| 绝对时间(如 2026-07-04 15:30:00) | 絕對時間(如 2026-07-04 15:30:00) | Absolute (e.g., 2026-07-04 15:30:00) |
| 选择后在主帖发帖时间立即生效,对当前账号生效 | 選擇後在主帖發帖時間立即生效,對當前賬號生效 | Takes effect immediately on thread pages for your account |
| 偏好已保存 | 偏好已保存 | Preference saved |
⚠️ 语言 hook 文件位于
lang_*_bbs.php(非lang_*_bbs_admin.php),因为它们位于return array(...)之外,使用$lang['key'] = 'value';语法。每行以;结尾,禁止尾随注释。
常见问题
Q1:为什么切换后帖子列表页的时间没变?
A:本插件只改写主帖(thread)详情页的发帖时间 create_date_fmt,不影响帖子列表页和回帖列表。这是设计上的取舍——列表页通常需要相对时间快速感知新鲜度,详情页才需要精确时间。
Q2:游客能看到绝对时间吗?
A:不能。游客未登录,$user 为空,xnx_ptt_mode 字段不存在,默认走相对时间分支。这是有意为之,避免影响未注册用户的浏览体验。
Q3:卸载插件后我的偏好还在吗?
A:不在。uninstall.php 会删除 xnx_ptt_mode 字段,所有用户的偏好记录被清空。重新安装后所有人回退到默认相对时间(mode=0)。
Q4:管理员能强制所有用户使用绝对时间吗?
A:当前版本不支持。本插件定位为「用户个性化偏好」,每位用户独立选择。若需全站强制,可通过 SQL 批量更新:UPDATE xn_user SET xnx_ptt_mode = 1;,但插件本身不提供此开关。
Q5:保存后页面没有提示?
A:请检查浏览器是否拦截了 htmx 请求(如广告屏蔽插件)。打开开发者工具 Network 面板,查看 my-*.htm 请求是否返回 200 和 HTML 片段。若返回 403,可能是 CSRF 令牌缺失。
Q6:可以扩展到回帖时间也切换吗?
A:可以。在 hook/ 目录新增 model_post_format_end.php,复用相同逻辑改写 $post['create_date_fmt'],并在 conf.json 的 hooks_rank 中注册。当前版本未做此扩展以保持插件轻量。
注意事项
生产环境部署前:先在测试环境验证
install.php的ALTER TABLE语句不与大表锁表冲突(user 表通常较小,无影响)字段保护:
xnx_ptt_mode不在USER_UPDATE_PROTECTED_FIELDS中,user_update()可正常写入缓存影响:
user_update()会自动cache_delete("user-$uid"),下次请求重新读取,无需手动清缓存tmp 缓存:修改 hook 文件后需清理
tmp/目录,确保新改动重新编译PHP 版本:兼容 PHP 8.0+,无废弃函数调用
MySQL 字符集:字段为整型,与 utf8mb4 字符集无冲突
htmx 依赖:前端表单依赖 htmx 4.x,确保主题模板已加载
htmx.min.js
版本历史
版本 | 日期 | 变更说明 |
|---|---|---|
1.1 | 2026-07 | 初始版本:实现用户中心时间显示偏好切换,支持简繁英三语,htmx 无刷新保存 |
下载地址:





