discord.py语音活跃度排行榜datetime跨天与超24小时计算问题求助
问题根因
两个报错均由仅存储时分秒、丢失日期维度的设计导致:
- 跨天场景下无日期信息,较小的次日时分减去较大的前日时分直接出现负数
- 时分秒格式天然不支持表达超过24小时的时长,运算时会直接抛出格式异常
修复方案
核心思路:放弃存储格式化的时分秒字符串,改用UTC Unix时间戳(秒级)存储加入时间,用总秒数存储累计语音时长,从底层解决时间运算问题。
完整修改后代码
import datetime import json from discord.ext import commands # 可选:秒数转可读时长的工具函数,展示排行榜时调用 def seconds_to_duration(seconds: int) -> str: days = seconds // 86400 hours = (seconds % 86400) // 3600 minutes = (seconds % 3600) // 60 sec = seconds % 60 if days > 0: return f"{days}天{hours}时{minutes}分{sec}秒" return f"{hours}时{minutes}分{sec}秒" @commands.Cog.listener() async def on_voice_state_update(self, member, before, after): # 过滤非加入/退出的事件(静音、切换频道等操作不触发统计) # 场景1:用户加入语音频道 if before.channel is None and after.channel is not None: if member.bot: return with open('data/voice_leaderboard.json', 'r') as file: voice_data = json.load(file) guild_id = str(member.guild.id) user_id = str(member.id) # 初始化新服务器节点 if guild_id not in voice_data: voice_data[guild_id] = {} # 存储加入时的UTC秒级时间戳 voice_data[guild_id][f"join_{user_id}"] = int(datetime.datetime.utcnow().timestamp()) # 初始化新用户累计时长 if user_id not in voice_data[guild_id]: voice_data[guild_id][user_id] = 0 with open('data/voice_leaderboard.json', 'w') as f: json.dump(voice_data, f, indent=4) # 场景2:用户退出语音频道 elif before.channel is not None and after.channel is None: if member.bot: return with open('data/voice_leaderboard.json', 'r') as file: voice_data = json.load(file) guild_id = str(member.guild.id) user_id = str(member.id) join_key = f"join_{user_id}" # 校验加入时间存在再做计算 if guild_id not in voice_data or join_key not in voice_data[guild_id]: return # 计算本次停留秒数 join_timestamp = voice_data[guild_id][join_key] leave_timestamp = int(datetime.datetime.utcnow().timestamp()) duration_sec = leave_timestamp - join_timestamp # 累加到用户总时长 voice_data[guild_id][user_id] += duration_sec # 删除临时存储的加入时间 del voice_data[guild_id][join_key] with open('data/voice_leaderboard.json', 'w') as f: json.dump(voice_data, f, indent=4)
优化说明
- 新增事件过滤逻辑,仅在用户真正加入/退出语音频道时触发统计,避免切换频道、调整静音等操作触发误统计
- 统一使用UTC时间戳计算,完全规避时区问题、跨天问题、超24小时计算问题
- 总时长用秒数存储,后续做排行榜排序、时长换算都更方便,需要展示可读格式时调用
seconds_to_duration函数即可 - 补充了服务器节点初始化逻辑,避免新服务器首次统计时抛出键不存在的异常
内容的提问来源于stack exchange,提问作者gwendal
相关产品推荐
相关产品推荐

