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

Django中如何将DurationField输入格式化为分:秒:毫秒

解决方案

方案一:用整数字段存储毫秒(推荐)

这种方式计算个人最佳、时间差更高效,也更容易控制输入输出格式。

1. 修改模型

把DurationField替换为PositiveIntegerField,直接存储毫秒数,同时添加格式化方法用于展示:

from django.db import models
from django.utils.translation import gettext_lazy as _
import timezone

class SwimTime(models.Model):
    swimmer = models.ForeignKey(Swimmer, on_delete=models.CASCADE)
    # 存储毫秒数,默认值对应01:01:001(1分1秒1毫秒=61*1000+1=61001)
    time_ms = models.PositiveIntegerField(_('Time (ms)'), default=61001)
    distance = models.PositiveIntegerField(_('Distance'), null=False, default=50)
    strokeType = models.CharField(_('Stroke Type'), max_length=20, choices=strokeTypes, default='FC')
    date = models.DateField(_('Date Recorded'), default=timezone.now)

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    # 将毫秒转换为MM:SS:mmm格式的字符串
    def get_formatted_time(self):
        total_ms = self.time_ms
        minutes = total_ms // 60000
        remaining_ms = total_ms % 60000
        seconds = remaining_ms // 1000
        ms = remaining_ms % 1000
        # 格式化补零,确保是两位分、两位秒、三位毫秒
        return f"{minutes:02d}:{seconds:02d}:{ms:03d}"

2. 自定义表单强制输入格式

创建表单时自定义输入字段,验证用户输入的MM:SS:mmm格式,并转换为毫秒数存储:

from django import forms
from .models import SwimTime
import re
from django.utils.translation import gettext_lazy as _

class SwimTimeForm(forms.ModelForm):
    # 自定义输入字段,提示用户输入格式
    time = forms.CharField(label=_('Time'), help_text=_('请输入MM:SS:mmm格式的时间(例如00:00:000)'))

    class Meta:
        model = SwimTime
        fields = ['swimmer', 'time', 'distance', 'strokeType', 'date']

    def clean_time(self):
        time_str = self.cleaned_data.get('time')
        # 验证格式是否为两位数字:两位数字:三位数字
        pattern = r'^\d{2}:\d{2}:\d{3}$'
        if not re.match(pattern, time_str):
            raise forms.ValidationError(_('时间格式无效,请使用MM:SS:mmm格式'))
        
        # 拆分时间组件并验证合法性
        minutes, seconds, ms = map(int, time_str.split(':'))
        if seconds >= 60 or ms >= 1000:
            raise forms.ValidationError(_('秒数不能超过59,毫秒数不能超过999'))
        
        # 转换为总毫秒数
        total_ms = minutes * 60000 + seconds * 1000 + ms
        return total_ms

    def save(self, commit=True):
        instance = super().save(commit=False)
        # 将转换后的毫秒数赋值给模型字段
        instance.time_ms = self.cleaned_data['time']
        if commit:
            instance.save()
        return instance

3. DetailView中展示格式化时间

在详情页模板中,调用模型的get_formatted_time方法展示格式:

<!-- swimtime_detail.html -->
<h1>单圈时间详情</h1>
<p>游泳者:{{ object.swimmer }}</p>
<p>单圈时间:{{ object.get_formatted_time }}</p>
<p>距离:{{ object.distance }}米</p>
<p>泳姿:{{ object.get_strokeType_display }}</p>
<p>记录日期:{{ object.date }}</p>

计算相关操作

  • 个人最佳成绩:直接按time_ms字段排序取最小值
    best_time = SwimTime.objects.filter(swimmer=target_swimmer).order_by('time_ms').first()
    
  • 单圈时间差:直接用两个对象的time_ms字段相减
    time_diff_ms = swim_time1.time_ms - swim_time2.time_ms
    # 转换为可读格式的话可以复用get_formatted_time的逻辑
    

方案二:保留DurationField(兼容原有模型)

如果不想修改模型字段类型,可以通过自定义表单和展示方法来实现需求:

1. 模型添加格式化方法

在原有模型中添加方法,将timedelta转换为MM:SS:mmm格式:

from datetime import timedelta

class SwimTime(models.Model):
    # 原有字段不变
    swimmer = models.ForeignKey(Swimmer, on_delete=models.CASCADE)
    time = models.DurationField(_('Time'), default=timedelta(minutes=1, seconds=1, milliseconds=1))
    distance = models.PositiveIntegerField(_('Distance'), null=False, default=50)
    strokeType = models.CharField(_('Stroke Type'), max_length=20, choices=strokeTypes, default='FC')
    date = models.DateField(_('Date Recorded'), default=timezone.now)

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    # 将DurationField的timedelta转换为MM:SS:mmm格式
    def get_formatted_time(self):
        total_seconds = self.time.total_seconds()
        minutes = int(total_seconds // 60)
        remaining_seconds = total_seconds % 60
        seconds = int(remaining_seconds)
        ms = int((remaining_seconds - seconds) * 1000)
        return f"{minutes:02d}:{seconds:02d}:{ms:03d}"

2. 自定义表单处理输入

同样创建自定义表单,将用户输入的MM:SS:mmm转换为timedelta:

from django import forms
from .models import SwimTime
import re
from datetime import timedelta
from django.utils.translation import gettext_lazy as _

class SwimTimeForm(forms.ModelForm):
    time = forms.CharField(label=_('Time'), help_text=_('请输入MM:SS:mmm格式的时间(例如00:00:000)'))

    class Meta:
        model = SwimTime
        fields = ['swimmer', 'time', 'distance', 'strokeType', 'date']

    def clean_time(self):
        time_str = self.cleaned_data.get('time')
        pattern = r'^\d{2}:\d{2}:\d{3}$'
        if not re.match(pattern, time_str):
            raise forms.ValidationError(_('时间格式无效,请使用MM:SS:mmm格式'))
        
        minutes, seconds, ms = map(int, time_str.split(':'))
        if seconds >= 60 or ms >= 1000:
            raise forms.ValidationError(_('秒数不能超过59,毫秒数不能超过999'))
        
        return timedelta(minutes=minutes, seconds=seconds, milliseconds=ms)

3. DetailView展示和计算

  • 展示方式和方案一相同,调用get_formatted_time方法
  • 计算操作:
    • 个人最佳:SwimTime.objects.filter(swimmer=target_swimmer).order_by('time').first()
    • 时间差:time_diff = swim_time1.time - swim_time2.time,转换为毫秒的话用time_diff.total_seconds() * 1000

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:05:17