前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python日期范围按旬和整月以及剩余区间拆分

Python日期范围按旬和整月以及剩余区间拆分

作者头像
可以叫我才哥
发布2024-01-02 12:13:20
1090
发布2024-01-02 12:13:20
举报
文章被收录于专栏:可以叫我才哥可以叫我才哥

原文:Python日期范围按旬和整月以及剩余区间拆分 地址:https://blog.csdn.net/as604049322/article/details/135033118 小小明

昨天见到了一个比较烧脑的问题:

image-01

咋一看可能理解问题比较费劲,可以直接看结果示例:

image-02

当然这个结果在原问题上基础上有一定改进,例如将同一天以单个日期的形式展示。

如何解决这个问题呢?大家可以先拿测试用例自己试一下:

代码语言:javascript
复制
for a, b in [
    ('2023-2-25', '2023-2-25'),
    ('2023-2-20', '2023-2-20'),
    ('2023-2-28', '2023-2-28'),
    ('2023-1-1', '2023-1-12'),
    ('2023-1-5', '2023-1-19'),
    ('2023-1-5', '2023-2-1'),
    ("2023-1-10", "2023-3-1"),
    ("2023-1-21", "2023-3-15"),
    ('2023-1-31', '2023-2-28'),
    ('2023-2-9', '2023-4-21'),
    ('2023-2-11', '2023-7-1'),
    ('2023-2-25', '2023-3-15'),
    ('2023-2-28', '2023-3-1'),
    ('2023-3-1', '2023-3-31'),
    ('2023-2-1', '2023-4-5'),
]:
    print(a, b, convert_str_to_date(a, b))

我这里的运行结果为:

代码语言:javascript
复制
2023-2-25 2023-2-25 (2023, ['2月25日'])
2023-2-20 2023-2-20 (2023, ['2月20日'])
2023-2-28 2023-2-28 (2023, ['2月28日'])
2023-1-1 2023-1-12 (2023, ['1月上旬', '1月11日-1月12日'])
2023-1-5 2023-1-19 (2023, ['1月5日-1月19日'])
2023-1-5 2023-2-1 (2023, ['1月5日-1月10日', '1月中旬', '1月下旬', '2月1日'])
2023-1-10 2023-3-1 (2023, ['1月10日', '1月中旬', '1月下旬', '2月', '3月1日'])
2023-1-21 2023-3-15 (2023, ['1月下旬', '2月', '3月上旬', '3月11日-3月15日'])
2023-1-31 2023-2-28 (2023, ['1月31日', '2月'])
2023-2-9 2023-4-21 (2023, ['2月9日-2月10日', '2月中旬', '2月下旬', '3月', '4月上旬', '4月中旬', '4月21日'])
2023-2-11 2023-7-1 (2023, ['2月中旬', '2月下旬', '3月', '4月', '5月', '6月', '7月1日'])
2023-2-25 2023-3-15 (2023, ['2月25日-2月28日', '3月上旬', '3月11日-3月15日'])
2023-2-28 2023-3-1 (2023, ['2月28日', '3月1日'])
2023-3-1 2023-3-31 (2023, ['3月'])
2023-2-1 2023-4-5 (2023, ['2月', '3月', '4月1日-4月5日'])

整体思路:

  • 将日期范围拆分为 首月、中间连续月、末月三部分
  • 针对中间连续月直接生成月份即可
  • 首月和末月都可以使用一个拆分函数进行计算

针对单月区间的计算思路:

  • 将日期拆分为s-10,11-20,21-e这三个以内的区间
  • 遍历区间,自己和上一个区间都不是旬区间则进行合并
  • 遍历合并后的区间,根据是否为旬区间进行不同的日期格式化

最终我的完整代码为:

代码语言:javascript
复制
from datetime import datetime, timedelta


def get_month_end(date):
    "获取日期当月最后一天"
    next_month = date.replace(day=28) + timedelta(days=4)
    return next_month - timedelta(days=next_month.day)


def monthly_split(start_date, end_date):
    "针对一个月之内进行计算"
    month_end_day = get_month_end(start_date).day
    if start_date.day == 1 and end_date.day == month_end_day:
        return [start_date.strftime('%#m月')]
    if start_date.day == end_date.day:
        return [start_date.strftime('%#m月%#d日')]
    periods = []
    current_date = start_date
    while current_date <= end_date:
        day = [10, 20, month_end_day][min(2, (current_date.day - 1) // 10)]
        period_end = current_date.replace(day=day)
        periods.append((current_date, min(end_date, period_end)))
        current_date = period_end + timedelta(days=1)
    merged_periods = []
    for start, end in periods:
        is_tenday = start.day in (1, 11, 21)
        is_tenday &= end.day in (10, 20, month_end_day)
        if not merged_periods or is_tenday or merged_periods[-1][2]:
            merged_periods.append([start, end, is_tenday])
        else:
            merged_periods[-1][1] = end
    formatted_periods = []
    for start, end, is_tenday in merged_periods:
        if is_tenday:
            formatted_str = f"{start.month}月{'上中下'[start.day // 10]}旬"
        else:
            formatted_str = start.strftime('%#m月%#d日')
            if start != end:
                formatted_str += f"-{end.strftime('%#m月%#d日')}"
        formatted_periods.append(formatted_str)
    return formatted_periods


def convert_str_to_date(start_date_str, end_date_str):
    start_date = datetime.strptime(start_date_str, "%Y-%m-%d").date()
    end_date = datetime.strptime(end_date_str, "%Y-%m-%d").date()
    if start_date.year != end_date.year:
        raise Exception("日期范围不在同一年")
    data = []
    month_end = get_month_end(start_date)
    if start_date.day != 1 and end_date > month_end:
        data.extend(monthly_split(start_date, month_end))
        start_date = month_end + timedelta(days=1)
    while start_date.month < end_date.month:
        data.append(start_date.strftime("%#m月"))
        start_date = (start_date.replace(day=28) +
                      timedelta(days=4)).replace(day=1)
    data.extend(monthly_split(start_date, end_date))
    return start_date.year, data

经过反复优化,最终在60行以内的代码解决了这个问题,大家有更好的代码,欢迎展示。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可以叫我才哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档