|
|
#!/usr/bin/env python
|
|
|
# -*- coding: utf-8 -*-
|
|
|
# @Time : 2022/9/10 10:17
|
|
|
# @Author : old tom
|
|
|
# @File : fu_date.py
|
|
|
# @Project : Futool
|
|
|
# @Desc : 日期时间工具
|
|
|
|
|
|
import time
|
|
|
from datetime import datetime, date, timedelta
|
|
|
import calendar
|
|
|
|
|
|
# 毫秒单位
|
|
|
MILLISECOND_UNIT = 1000
|
|
|
# 1小时3600秒
|
|
|
ONE_HOUR_SECOND = 3600
|
|
|
# 上午下午分界
|
|
|
AM_PM_DIV = 11
|
|
|
# 一周7天
|
|
|
ONE_WEEK_DAYS = 7
|
|
|
|
|
|
# 日期时间格式化转换
|
|
|
FMT_MAPPING = {
|
|
|
'yyyy-MM-dd': '%Y-%m-%d',
|
|
|
'yyyyMMdd': '%Y%m%d',
|
|
|
'yyyy-MM-dd hh:mm:ss': '%Y-%m-%d %I:%M:%S',
|
|
|
'yyyy-MM-dd hh24:mm:ss': '%Y-%m-%d %H:%M:%S',
|
|
|
'yyyy-MM-dd HH:mm:ss': '%Y-%m-%d %H:%M:%S'
|
|
|
}
|
|
|
|
|
|
|
|
|
def current_year() -> int:
|
|
|
"""
|
|
|
本年
|
|
|
:return:
|
|
|
"""
|
|
|
return datetime.now().year
|
|
|
|
|
|
|
|
|
def current_month() -> int:
|
|
|
"""
|
|
|
本月
|
|
|
:return:
|
|
|
"""
|
|
|
return datetime.now().month
|
|
|
|
|
|
|
|
|
def current_day() -> int:
|
|
|
return datetime.now().day
|
|
|
|
|
|
|
|
|
def current_date() -> str:
|
|
|
return str(datetime.now().date())
|
|
|
|
|
|
|
|
|
def current_datetime(fmt='yyyy-MM-dd hh24:mm:ss') -> str:
|
|
|
"""
|
|
|
获取当前日期时间
|
|
|
:param fmt:
|
|
|
:return:
|
|
|
"""
|
|
|
return datetime.now().strftime(FMT_MAPPING[fmt])
|
|
|
|
|
|
|
|
|
def current_time(fmt='s') -> int:
|
|
|
"""
|
|
|
获取当前时间绝对秒
|
|
|
:param fmt: s: 秒 ms:毫秒 ns:纳秒
|
|
|
:return:
|
|
|
"""
|
|
|
time_jar = {
|
|
|
's': int(time.time()),
|
|
|
'ms': int(time.time() * MILLISECOND_UNIT),
|
|
|
'ns': time.time_ns()
|
|
|
}
|
|
|
return time_jar[fmt]
|
|
|
|
|
|
|
|
|
def current_timestamp():
|
|
|
"""
|
|
|
当前时间戳
|
|
|
:return:
|
|
|
"""
|
|
|
return datetime.now().timestamp()
|
|
|
|
|
|
|
|
|
def format_datetime_str(dt: str, fmt='yyyy-MM-dd hh24:mm:ss') -> datetime:
|
|
|
"""
|
|
|
格式化日期时间字符串
|
|
|
:param dt: 日期时间字符串
|
|
|
:param fmt: 格式化,例:yyyy-MM-dd
|
|
|
:return: datetime对象
|
|
|
"""
|
|
|
return datetime.strptime(dt, FMT_MAPPING[fmt])
|
|
|
|
|
|
|
|
|
def format_date_str(dt: str, fmt='yyyy-MM-dd') -> date:
|
|
|
"""
|
|
|
格式化日期字符串
|
|
|
:param dt: 日期字符串
|
|
|
:param fmt: yyyy-MM-dd
|
|
|
:return:
|
|
|
"""
|
|
|
return datetime.strptime(dt, FMT_MAPPING[fmt]).date()
|
|
|
|
|
|
|
|
|
def datetime_2_second(dt: str, fmt='yyyy-MM-dd hh24:mm:ss') -> int:
|
|
|
"""
|
|
|
日期时间字符串转绝对秒
|
|
|
:param dt: 日期时间字符串 yyyy-MM-dd hh:mm:ss 格式
|
|
|
:param fmt: 格式化函数
|
|
|
:return:
|
|
|
"""
|
|
|
return int(format_datetime_str(dt, fmt).timestamp())
|
|
|
|
|
|
|
|
|
def sec_2_datatime(sec_time: int, fmt='yyyy-MM-dd hh24:mm:ss') -> str:
|
|
|
"""
|
|
|
绝对秒转日期时间
|
|
|
:param sec_time:
|
|
|
:param fmt: 格式化函数
|
|
|
:return:
|
|
|
"""
|
|
|
timed = time.localtime(sec_time)
|
|
|
return time.strftime(FMT_MAPPING[fmt], timed)
|
|
|
|
|
|
|
|
|
def is_leap(year: int) -> bool:
|
|
|
"""
|
|
|
是否闰年
|
|
|
:param year:
|
|
|
:return:
|
|
|
"""
|
|
|
return calendar.isleap(year)
|
|
|
|
|
|
|
|
|
def begin_of_week(date_str: str, fmt='yyyy-MM-dd') -> str:
|
|
|
"""
|
|
|
周开始日期
|
|
|
:param date_str: 年
|
|
|
:param fmt: 格式化
|
|
|
:return:
|
|
|
"""
|
|
|
formated_dt = format_date_str(date_str, fmt)
|
|
|
week_idx = weekday(date_str, fmt)
|
|
|
return date_str if week_idx == 0 else str(formated_dt - timedelta(days=week_idx))
|
|
|
|
|
|
|
|
|
def end_of_week(date_str: str, fmt='yyyy-MM-dd') -> str:
|
|
|
"""
|
|
|
周结束日期
|
|
|
:param date_str: 年
|
|
|
:param fmt: 格式化
|
|
|
:return:
|
|
|
"""
|
|
|
formated_dt = format_date_str(date_str, fmt)
|
|
|
week_idx = weekday(date_str, fmt)
|
|
|
return date_str if week_idx == 6 else str(formated_dt + timedelta(days=(6 - week_idx)))
|
|
|
|
|
|
|
|
|
def end_of_month(y, m) -> int:
|
|
|
"""
|
|
|
月结束日期
|
|
|
:param y 年
|
|
|
:param m 月
|
|
|
:return:
|
|
|
"""
|
|
|
return calendar.monthrange(y, m)[1]
|
|
|
|
|
|
|
|
|
def weekday(date_str: str, fmt='yyyy-MM-dd') -> int:
|
|
|
"""
|
|
|
返回日期是周几
|
|
|
:param date_str: 年
|
|
|
:param fmt: 格式化
|
|
|
:return: 0-7 ,0:周一
|
|
|
"""
|
|
|
fmted_date = format_datetime_str(date_str, fmt)
|
|
|
return calendar.weekday(fmted_date.year, fmted_date.month, fmted_date.day)
|
|
|
|
|
|
|
|
|
def age(birth: str, compare_date: str, fmt='yyyy-MM-dd') -> int:
|
|
|
"""
|
|
|
年龄计算
|
|
|
:param birth: 生日
|
|
|
:param compare_date: 被比较日期
|
|
|
:param fmt: 日期格式化 yyyy-MM-dd|yyyyMMdd
|
|
|
:return:
|
|
|
"""
|
|
|
fmt_birth = format_date_str(birth, fmt)
|
|
|
fmt_compare = format_date_str(compare_date, fmt)
|
|
|
birth_m = fmt_birth.replace(year=fmt_compare.year)
|
|
|
return fmt_compare.year - fmt_birth.year if fmt_compare > birth_m else fmt_compare.year - fmt_birth.year - 1
|
|
|
|
|
|
|
|
|
def age_of_now(birth: str) -> int:
|
|
|
"""
|
|
|
当前年龄
|
|
|
:param birth: 出生日期
|
|
|
:return:
|
|
|
"""
|
|
|
return age(birth, current_datetime('yyyy-MM-dd'))
|
|
|
|
|
|
|
|
|
def between(dt_1: str, dt_2: str, fmt='yyyy-MM-dd', time_unit='day') -> int:
|
|
|
"""
|
|
|
计算两个时间差
|
|
|
:param dt_1: 时间1 日期或日期时间
|
|
|
:param dt_2: 时间2 日期或日期时间
|
|
|
:param fmt: 格式化
|
|
|
:param time_unit: 时间单位 day: 天,hour: 小时 ,minute:分钟,second:秒
|
|
|
:return:
|
|
|
"""
|
|
|
fmt_dt1, fmt_dt2 = format_datetime_str(dt_1, fmt), format_datetime_str(dt_2, fmt)
|
|
|
return abs({
|
|
|
'day': (fmt_dt1 - fmt_dt2).days,
|
|
|
'hour': int(fmt_dt1.timestamp() - fmt_dt2.timestamp()) / ONE_HOUR_SECOND,
|
|
|
'second': int(fmt_dt1.timestamp() - fmt_dt2.timestamp())
|
|
|
}[time_unit])
|
|
|
|
|
|
|
|
|
def time_offset(start_dt: str, offset: int, fmt='yyyy-MM-dd HH:mm:ss', time_unit='day') -> datetime:
|
|
|
"""
|
|
|
时间偏移计算,例:计算相隔N天后的日期
|
|
|
:param start_dt: 开始日期(日期时间)
|
|
|
:param fmt: 时间格式化
|
|
|
:param offset: 偏移量,支持正负数
|
|
|
:param time_unit: 偏移量单位
|
|
|
:return:
|
|
|
"""
|
|
|
fmt_dt = format_datetime_str(start_dt, fmt)
|
|
|
return {'day': fmt_dt + timedelta(days=offset),
|
|
|
'hour': fmt_dt + timedelta(hours=offset),
|
|
|
'second': fmt_dt + timedelta(seconds=offset)}[time_unit]
|
|
|
|
|
|
|
|
|
def is_am(dt: str, fmt='yyyy-MM-dd HH:mm:ss') -> bool:
|
|
|
"""
|
|
|
是否上午
|
|
|
:param dt:
|
|
|
:param fmt:
|
|
|
:return:
|
|
|
"""
|
|
|
fmt_dt = format_datetime_str(dt, fmt)
|
|
|
return fmt_dt.hour <= AM_PM_DIV
|
|
|
|
|
|
|
|
|
def is_pm(dt: str, fmt='yyyy-MM-dd HH:mm:ss') -> bool:
|
|
|
"""
|
|
|
是否下午
|
|
|
:param dt:
|
|
|
:param fmt:
|
|
|
:return:
|
|
|
"""
|
|
|
fmt_dt = format_datetime_str(dt, fmt)
|
|
|
return fmt_dt.hour > AM_PM_DIV
|
|
|
|
|
|
|
|
|
def next_week(fmt='yyyy-MM-dd HH:mm:ss') -> datetime:
|
|
|
"""
|
|
|
下周同一天
|
|
|
:return:
|
|
|
"""
|
|
|
now = current_datetime(fmt)
|
|
|
return time_offset(now, ONE_WEEK_DAYS, fmt)
|
|
|
|
|
|
|
|
|
def next_month() -> datetime:
|
|
|
"""
|
|
|
下个月同一天
|
|
|
:return:
|
|
|
"""
|
|
|
return datetime.now().replace(month=current_month() + 1)
|