Django QuerySet APIでTimeFieldの合計を求める

Django QuerySet APIでTimeFieldの合計を求める

TimeFieldとSum

Django QuerySet APIでTimeFieldの合計を普通にSumを使って求めようとすると…

# sample.py
from django.db.models import Sum
Book.objects.annotate(sec=Sum('time'))

01:00:00, 00:30:00, 00:40:00の合計が17000とおかしな値で帰ってきてしまいます。

TIME_TO_SEC関数

どうもこれは、MySQLのバグらしいのですが、TIME_TO_SEC関数を噛ましてあげると回避できるようです。

なので、DjangoでTIME_TO_SEC関数が使えるように設定してあげると治ります。

# sample.py
from django.db.models import Sum, Func

class Time_to_sec(Func):
    function = 'TIME_TO_SEC'
    lookup_name = 'sec'

Book.objects.annotate(sec=Sum(Time_to_sec('time')))

01:00:00, 00:30:00, 00:40:00の合計は7800となり、合計秒数が取得できます。

SEC_TO_TIME関数

MySQLではSEC_TO_TIME関数でSECからTIMEに戻すことができるのですが…

# sample.py
from django.db.models import Func

class Sec_to_time(Func):
    function = 'SEC_TO_TIME'
    lookup_name = 'time'

Pythonの仕様上、Timeオブジェクトは23:59:59.999999以内の時間しか定義することができないので、24時間を超えてしまうとエラーとなってしまいます。

なので、Python側で合計秒数を時間の差を示すtimedeltaオブジェクトに変換して使います。

# sample.py
import datetime
str(datetime.timedelta(seconds=666)) # '0:11:06'