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

Django日历如何显示起止时间区间内跨多天的每日事件

问题根因

现有逻辑直接按事件start_time的日期值分组,跨天事件仅会被归入起始日期对应的分组,自然无法在覆盖的中间日期下展示。此前GTE/LTE过滤未达预期,基本是两个原因:一是判断逻辑未覆盖「事件区间与目标日期存在交集」的场景,仅匹配了事件开始时间等于目标日期的记录;二是直接携带时分秒的datetime值与纯日期值比较,存在匹配偏差。修改formatmonth时出现的多余数字、格式错乱问题,基本是方法内误用print输出内容、未按要求返回拼接完成的HTML字符串导致。

分步解决方案

1. 整合日期范围生成工具

先把通用的连续日期生成函数放到项目工具模块中,用来遍历事件覆盖的所有日期:

from datetime import timedelta

def daterange(start_date, end_date):
    for n in range(int((end_date - start_date).days) + 1):
        yield start_date + timedelta(n)

跨天事件与某一日期存在交集的判断规则固定:事件开始日期 ≤ 目标日期 且 事件结束日期 ≥ 目标日期,所有过滤逻辑都按这个规则实现即可。

2. 调整视图层逻辑,预组装日期-事件映射

不要在模板层写复杂匹配逻辑,所有数据分组在视图层完成,避免渲染时出现格式问题:

from django.db.models import Q
from datetime import date, timedelta

# 假设休假事件模型为Leave,包含start_time(DateTimeField)、end_time(DateTimeField)、title等字段
def calendar_view(request, year=None, month=None):
    today = date.today()
    year = year or today.year
    month = month or today.month

    # 计算当月起止日期
    first_day_of_month = date(year, month, 1)
    if month == 12:
        last_day_of_month = date(year+1, 1, 1) - timedelta(days=1)
    else:
        last_day_of_month = date(year, month+1, 1) - timedelta(days=1)

    # 过滤所有和当月有交集的休假事件,用__date截断时分秒避免匹配偏差
    leaves = Leave.objects.filter(
        Q(end_time__date__gte=first_day_of_month) &
        Q(start_time__date__lte=last_day_of_month)
    )

    # 初始化日期-事件映射字典,当月所有日期默认对应空事件列表
    date_event_map = {}
    for cur_date in daterange(first_day_of_month, last_day_of_month):
        date_event_map[cur_date] = []

    # 遍历事件,将事件塞入其覆盖的所有当日列表中,跨月事件仅保留当月范围内的关联
    for leave in leaves:
        leave_start = leave.start_time.date()
        leave_end = leave.end_time.date()
        for event_date in daterange(
            max(leave_start, first_day_of_month),
            min(leave_end, last_day_of_month)
        ):
            date_event_map[event_date].append(leave)

    # 生成月历HTML,传入上下文
    cal = LeaveCalendar()
    context = {
        "html_calendar": cal.formatmonth(year, month),
        "date_event_map": date_event_map,
        "year": year,
        "month": month
    }
    return render(request, "calendar.html", context)

3. 修复自定义Calendar类的输出异常

自定义Calendar类的所有格式化方法禁止使用print输出内容,所有展示内容必须拼接为字符串最终返回,即可解决多余数字、格式错乱问题,参考实现如下:

from calendar import HTMLCalendar

class LeaveCalendar(HTMLCalendar):
    def formatday(self, day, weekday, events=None):
        events = events or []
        # 上月/下月补位的空白单元格
        if day == 0:
            return '<td class="noday">&nbsp;</td>'
        # 拼接当日事件展示内容
        event_html = "".join([f'<div class="leave-event">{leave.title}</div>' for leave in events])
        return f'<td class="day"><span class="date-num">{day}</span>{event_html}</td>'

    def formatmonth(self, year, month, withyear=True):
        # 所有内容拼接为字符串返回,不要加print语句
        content = []
        content.append('<table border="0" cellpadding="0" cellspacing="0" class="month">')
        content.append(self.formatmonthname(year, month, withyear=withyear))
        content.append(self.formatweekheader())
        for week in self.monthdays2calendar(year, month):
            content.append(self.formatweek(week))
        content.append('</table>')
        return ''.join(content)

4. 模板侧渲染逻辑

模板直接使用视图层组装好的数据渲染即可,无需额外做事件匹配:

<!-- 月历区域渲染后端生成的安全HTML -->
{{ html_calendar|safe }}

<!-- 按天展示的事件列表 -->
<div class="daily-event-wrapper">
{% for cur_date, events in date_event_map.items %}
    <div class="day-block">
        <h4>{{ cur_date|date:"Y年m月d日" }}</h4>
        <ul>
            {% for event in events %}
            <li>
                {{ event.title }}:{{ event.start_time|date:"H:i" }} - {{ event.end_time|date:"H:i" }}
                {% if event.start_time.date != cur_date or event.end_time.date != cur_date %}
                <span class="tag">跨天</span>
                {% endif %}
            </li>
            {% empty %}
            <li>当日无休假安排</li>
            {% endfor %}
        </ul>
    </div>
{% endfor %}
</div>
避坑提示
  • 所有DateTimeField与日期值比较时,必须加__date后缀截断时分秒,否则会出现匹配遗漏
  • 跨月的长假期事件要做日期边界截断,避免把上月/下月的事件关联到当月列表
  • 自定义Calendar的格式化方法必须返回完整拼接的字符串,禁止直接打印内容,否则会出现内容错位、多余字符问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:57:17