简单实现
计算两个日期差值,通常使用datetime模块即可方便实现。
简单示例如下:
ts1 = datetime.now() + timedelta(days=-2, hours=23)
ts2 = datetime.now()
ts_delta = ts1 - ts2
print(ts_delta.days)
特殊场景
但在某些特殊场景下,我们可能会需要一些特殊的计算日期差值的方法。
例如:
- 日期差值四舍五入,满半天按一天计算,不满半天计为0
- 日期差值向上取整,超出一天的无论多长时间均按一天计算
- 日期差值向下取整,超出一天但不满一天的全部计为0
在如下代码中,简单试下了一下这些功能。
"""
用于计算两个日期时间(datetime对象或字符串)之间的日期差值
"""
from datetime import datetime
from typing import Union
DateTime = Union[str, datetime]
DELTA_ZERO = 0 # seconds 与microseconds均为0
DELTA_UN_ZERO = 1 # seconds 或 microseconds存在不为0的值
DAYS_POSITIVE = 1 # days为自然数
DAYS_NAGATIVE = -1 # days为负整数
def fmt(ts: str, fmt_str: str) -> datetime:
if isinstance(ts, datetime):
return ts
if isinstance(ts, date):
return datetime(year=date.year, month=date.month,day=date.day)
if not fmt_str:
fmt_str = '%Y-%m-%d %H:%M:%S'
return datetime.strptime(ts, fmt_str)
class DateDelta(object):
def __init__(self):
self._ts1 = None
self._ts2 = None
self._ts_delta = None
self._code_days = -2 # days的判断结果
self._code_ts_delta = -2 # seconds 与microseconds的判断结果
def delta(self, ts1: DateTime, ts2: DateTime, fmt_str: str = '', mode=''):
if isinstance(ts1, str) or isinstance(ts2, str):
ts2 = fmt(ts2, fmt_str)
ts1 = fmt(ts1, fmt_str)
if isinstance(ts1, datetime) and isinstance(ts2, datetime):
self._ts1 = ts1
self._ts2 = ts2
self._ts_delta = ts1 - ts2
self._code_days, self._code_ts_delta = self._cmp()
else:
raise ValueError(f'ts1,ts2 must be an instance of datetime or a datetime string:\n{ts1},\n{ts2}\n')
return self
def ceil(self) -> int:
"""
# 向上取整,不足一天按一天计算
# 如 days为自然数:
# seconds或microsecods不为0,向上取整,days+1;
# seconds与microsecods均为0,days取原值
# 如 days为负整数:
# seconds或microsecods不为0,向上取整,days取原值;
# days+1seconds与microsecods均为0,days取原值
:return:
"""
code_days, code_ts_delta = self._code_days, self._code_ts_delta
if code_days == DAYS_POSITIVE:
if code_ts_delta == DELTA_ZERO:
return self._ts_delta.days
else:
return self._ts_delta.days + 1
elif code_days == DAYS_NAGATIVE:
if code_ts_delta == DELTA_ZERO:
return self._ts_delta.days
else:
return self._ts_delta.days
else:
raise ValueError(F'code_days illegal: {code_days}\n')
def floor(self) -> int:
"""
# 向下取整,不足一天计为0
# days为自然数:
# seconds或microsecods不为0(即超出days但不满一天),向下取整,不足1天部分舍去,days取原值;
# seconds与microsecods均为0(刚好相差{days}天),days取原值
# days为负整数:
# seconds或microsecods不为0,向下取整,days+1;
# days+1seconds与microsecods均为0,days取原值
:return:
"""
code_days, code_ts_delta = self._code_days, self._code_ts_delta
if code_days == DAYS_POSITIVE:
if code_ts_delta == DELTA_ZERO:
return self._ts_delta.days
else:
return self._ts_delta.days
elif code_days == DAYS_NAGATIVE:
if code_ts_delta == DELTA_ZERO:
return self._ts_delta.days
else:
return self._ts_delta.days + 1
else:
raise ValueError(F'code_days illegal: {code_days}\n')
def round(self) -> int:
"""
# 四舍五入,超过半天按一天计算,不足半天计为0
# days为自然数:
# seconds(60*60)>=12(即超出days且满半天),四舍五入,days+1;
# seconds(60*60)<12(即超出days但不满半天),四舍五入,days不变;
# days为负整数:
# seconds(60*60)>=12(即超出days且满半天),四舍五入,days+1;
# seconds(60*60)<12(即超出days但不满半天),四舍五入,days不变;
:return:
"""
if self._ts_delta.seconds / (60 * 60) >= 12:
return self._ts_delta.days + 1
elif self._ts_delta.seconds / (60 * 60) < 12:
return self._ts_delta.days
else:
raise ValueError(F'unknown exception happened:\n')
def _cmp(self):
days = self._ts_delta.days
seconds = self._ts_delta.seconds
microseconds = self._ts_delta.microseconds
if days >= 0:
code_days = DAYS_POSITIVE
else:
code_days = DAYS_NAGATIVE
if seconds or microseconds:
# 若 ts2 + timedela(days=days) < ts1
code_ts_delta = DELTA_UN_ZERO
else:
# 若 ts2 + timedela(days=days) == ts1
code_ts_delta = DELTA_ZERO
return code_days, code_ts_delta
最后,为了保证功能的正常可用,必要的单元测试也是必不可少。 这里我使用了pytest来编写单元测试用例。
from date.timedelta import DateDelta
import pytest
from datetime import datetime, timedelta
args = 'ts1,ts2,expect'
ts = datetime.now()
ceil_data = [
(ts, ts, 0),
(ts + timedelta(days=2), ts, 2),
(ts + timedelta(days=-2), ts, -2),
(ts + timedelta(days=2, hours=1), ts, 3),
(ts + timedelta(days=-2, hours=1), ts, -2),
(ts + timedelta(days=2, seconds=1), ts, 3),
(ts + timedelta(days=-2, seconds=1), ts, -2),
(ts + timedelta(days=2, microseconds=1), ts, 3),
(ts + timedelta(days=-2, microseconds=1), ts, -2),
(ts + timedelta(days=-2, microseconds=-1), ts, -3),
]
floor_data = [
(ts, ts, 0),
(ts + timedelta(days=2), ts, 2),
(ts + timedelta(days=-2), ts, -2),
(ts + timedelta(days=2, hours=1), ts, 2),
(ts + timedelta(days=-2, hours=1), ts, -1),
(ts + timedelta(days=2, seconds=1), ts, 2),
(ts + timedelta(days=-2, seconds=1), ts, -1),
(ts + timedelta(days=2, microseconds=1), ts, 2),
(ts + timedelta(days=-2, microseconds=1), ts, -1),
(ts + timedelta(days=-2, microseconds=-1), ts, -2),
]
round_data = [
(ts, ts, 0),
(ts + timedelta(days=2), ts, 2),
(ts + timedelta(days=-2), ts, -2),
(ts + timedelta(days=2, hours=11, minutes=59, seconds=59), ts, 2),
(ts + timedelta(days=-2, hours=11, minutes=59, seconds=59), ts, -2),
(ts + timedelta(days=2, hours=12), ts, 3),
(ts + timedelta(days=-2, hours=12), ts, -1),
]
class TestCase(object):
def setup_class(self):
self.td = DateDelta()
@pytest.mark.parametrize(args, ceil_data)
def test_ceil(self, ts1, ts2, expect):
rst = self.td.delta(ts1, ts2).ceil()
assert rst == expect
@pytest.mark.parametrize(args, floor_data)
def test_floor(self, ts1, ts2, expect):
rst = self.td.delta(ts1, ts2).floor()
assert rst == expect
@pytest.mark.parametrize(args, round_data)
def test_round(self, ts1, ts2, expect):
rst = self.td.delta(ts1, ts2).round()
assert rst == expect
题外话
当下这个大数据时代不掌握一门编程语言怎么跟的上脚本呢?当下最火的编程语言Python前景一片光明!如果你也想跟上时代提升自己那么请看一下.
感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
工具都帮大家整理好了,安装就可直接上手!
三、最新Python学习笔记
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、Python视频合集
观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
五、实战案例
纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
六、面试宝典
简历模板![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e609d9de17e680ccba27f351612a0860.png#pic_center)
![](https://i-blog.csdnimg.cn/blog_migrate/d90fe6963ecad746db5d328490915a43.png)