You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

优化说明

  1. 新增事件过滤逻辑,仅在用户真正加入/退出语音频道时触发统计,避免切换频道、调整静音等操作触发误统计
  2. 统一使用UTC时间戳计算,完全规避时区问题、跨天问题、超24小时计算问题
  3. 总时长用秒数存储,后续做排行榜排序、时长换算都更方便,需要展示可读格式时调用seconds_to_duration函数即可
  4. 补充了服务器节点初始化逻辑,避免新服务器首次统计时抛出键不存在的异常

内容的提问来源于stack exchange,提问作者gwendal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 11:54:00