目录

  • 一、datetime模块
  • 1、date 类
  • 2、time 类
  • 3、datetime 类
  • 4、strftime 方法
  • 5、strptime 方法
  • 6、replace 方法
  • 7、timedelta 类
  • 8、tzinfo 和 timezone
  • 二、dateutil 模块
  • 1、relativedelta 类
  • 2、paser 类
  • 三、pytz 模块
  • 1、查看时区,给时间添加时区属性
  • 2、localize和astimezone 给时间增加时区属性和时间的时区转换


一、datetime模块

datetime模块提供用于处理日期和时间的类。支持日期时间数学运算,更着重于有效地解析其属性用于格式化输出和数据操作。
官方文档 datetime模块中常用的和方法有:date、time、datetime、strtime、strftime、strptime、replace
导入模块:

import datetime

1、date 类

datetime模块中的一个类,用于创建一个包含年月日的日期

d = datetime.date(2020, 3, 23)  # date的参数必须是int,需要注意的是1月就是1不是01
print(d)  
print(d.year)  # 获取时间变量的年份,月份:d.month 日期: d.day
print(datetime.date.today())  # 今天的日期
print(d.weekday())  # 返回日期是周几,周一是0
print(d.isoweekday())  # 返回日期是周几,周一是1, isoweekday = weekday()+1

2、time 类

datetime模块中的一个类,用于创建一个包含时分秒的时间(每天24x60x60秒)

d = datetime.time(8, 3, 15, 30)  # default: tzinfo=None
print(d)
print(d.hour)  # 提取时间的小时数
print(d.minute)  # 分
print(d.second)  # 秒
print(d.microsecond)  # 微秒
print(d.tzinfo)  # 时区, 源数据参数 tzinfo=None 时,这里也返回None

3、datetime 类

datatime模块中的datetime类,可以看作是上面date和time的组合。Attributes: year, month, day, hour, minute, second, microsecond(微秒)

d = datetime.datetime(2020, 3, 18, 20, 15, 20, 300)
print(d)  # -->2020-03-18 20:15:20.000300
print(d.month)  # 获取时间变量的月份
print(d.timestamp())  # 把易读时间转换为时间戳格式,方式一
time = datetime.datetime.timestamp(d)  # 把易读时间转换为时间戳格式 方式二
print(time)
time = datetime.datetime.fromtimestamp(time)  # 把时间戳转换为易读时间格式
print(time)
print(datetime.date(2020, 4, 10) - datetime.date(2020, 4, 9))  # 时间差
d = datetime.datetime.now(tz=pytz.utc)  
print(d)
print(d.date())
print(d.time())  # 17:29:19.824968 提取时间,不包括 tzinfo 属性
print(d.timetz())  # 17:29:19.824968 提取时间,包括 tzinfo 属性
print(d.astimezone())  # 返回带有本地时区的时间 2020-03-28 17:29:19.824968+08:00
print(d.utcoffset())  # 返回时间变量的时区,如果原数据参数的tz=None 那么这里也返回None
print(d.dst())  # 夏令时
print(d.timetuple())  # 返回一个元组格式的时间,可以用[index] 提取对应内容,比如 d.timetuple() -- year 返回值格式:
# time.struct_time(tm_year=2020, tm_mon=3, tm_mday=28, tm_hour=9, tm_min=41, tm_sec=28, tm_wday=5, tm_yday=88,
# tm_isdst=0)
# 其实datetime 就是 date + time,属性也是覆盖date 和 time
# 需要注意的时,date 没有 tzinfo属性,而time和datetime都有

4、strftime 方法

datatime 类中的一个方法,用于把时间格式化,返回一个字符串

d = datetime.datetime.now()  # 当前的电脑时间
print(d.strftime('%Y-%m-%d %H:%M:%S'))  # 返回2020-03-28 10:20:20
print(d.strftime('%y-%m-%d %H:%M:%S'))  # 返回20-03-28 10:20:20  注意年的大写Y和小写y的区别
print(d.strftime('%D %H:%M:%S'))  # 返回03/28/20 10:21:57  注意D的用法
print(d.strftime('%b-%d-%y %H:%M:%S'))  # Mar-28-20 10:20:20
print(d.strftime('%B-%d-%y %H:%M:%S'))  # March-28-20 10:23:39  注意B和b的区别,分别是月份的全拼和简写

5、strptime 方法

datatime 类中的一个类方法,用于把字符串格式的时间转换为datetime.datetime格式,
需要注意的是date类 和 time类都没有这个方法

d = datetime.datetime.now()
print(datetime.datetime.strptime('March-28-20 10:23:39', '%B-%d-%y %H:%M:%S'))
print(datetime.datetime.strptime('2020-03-28', '%Y-%m-%d'))  # 2020-03-28 00:00:00 如果只有日期,自动填充时间0
print(datetime.datetime.strptime('10:34:22', '%H:%M:%S'))  # 1900-01-01 10:34:22 如果只有时间,那么年份将是 1900-01-01
# 返回数据类型 <class 'datetime.datetime'>
print(type(datetime.datetime.strptime('2020-03-24 10:27:29', '%Y-%m-%d %H:%M:%S')))

6、replace 方法

返回一个带相同属性的datetime,除了指定关键字参数的新值之外,还可以指定tzinfo新值

d = datetime.datetime.now()  # --> 2020-03-28 11:00:41.296596
print(d.replace(year=2022, day=23))  # --> 2022-03-23 11:00:41.296596
# replace 的全部参数:
# replace(year=self.year, month=self.month, day=self.day, hour=self.hour, minute=self.minute, second=self.second, microsecond=self.microsecond, tzinfo=self.tzinfo)
# 其中tzinfo是修改时区
print(d.replace(tzinfo=pytz.timezone('Asia/Shanghai')))  # 把原时间的时区替换为参数的时区

单纯看上面的用法貌似基本用不到,一般我们不会去强行改变一个时间的某个值。有一种情况就是如果我们要获取这个月的最后一天是几号,有的月份是30天有的有31日,二月还是28/29天,你当然可以使用字典,但是太死板,使用replace就非常方便,下面自定义函数,可以获取月份的最后一天:

def last_day_month(date_time: datetime.datetime):
    """
    返回月份的最后一天
    :return:
    """
    next_month = date_time.replace(day=28) + datetime.timedelta(days=4)  # 把天数替换为28日,因为月份中最少的天数就是28了,+4天肯定到下一个月了
    delta = datetime.timedelta(days=next_month.day)  # next_month.day 下个月的天数,
    date = (next_month - delta).replace(hour=23, minute=59, second=59, microsecond=0)
    return date

7、timedelta 类

datetime模块中的一个类,表示两个日期或时间的差

class datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

所有参数都是可选的,默认为0。参数可以是整数或浮点数,也可以是正数或负数。

d = datetime.date(2020, 3, 23)
later = datetime.timedelta(days=9)  # 创建一个时间间隔(9天)
print(later)
t = d + later
print(t)  # 返回结果:2020-04-01; 源数据是3月23日,加9天,超过了31天了,程序会自动计入4月份,时分秒同理
print(later.total_seconds())  # 把时间间隔转换为秒数

通过参数可以注意到,timedelta 只能在日及以下级别偏移,如果要偏移一个月或一年的?那就要用到 dateutil 模块的 relativedelta(下面会介绍)。

8、tzinfo 和 timezone

tzinfo 也是datetime下的类,但是这个类不会用来实例化,只是捕获关于特定时区的信息,一般在datetime和time传参时使用。上面已经多次用到,这里不再举例。
timezone 是 tzinfo 的子类,它的每个实例都表示一个由UTC的固定偏移量定义的时区

二、dateutil 模块

官方文档 dateutil 是不同于datetime模块的一个第三方时间处理模块,其实常用的时间处理模块还是datetime,但是datetime有些不足的地方,比如 timedelta 偏移月份和年不方便,再比如把一个 ‘20200325082532’ 这样的字符串之间转换为时间类型或易读的字符串格式。那么这里就介绍这两个功能的补充:
导入模块:

from dateutil.relativedelta import * 
from dateutil.parser import parse

1、relativedelta 类

从导入的方式可以推测,这是在 dateutil 模块中 relativedelta子模块中的一个类
类的定义:

relativedelta(self, dt1=None, dt2=None,
 years=0, months=0, days=0, leapdays=0, weeks=0,
 hours=0, minutes=0, seconds=0, microseconds=0,
 year=None, month=None, day=None, weekday=None,
 yearday=None, nlyearday=None,
 hour=None, minute=None, second=None, microsecond=None)


所有参数都有默认值,参数很多,通过观察可以发现大部分参数可以分为两类,比如year/years,month/months…等,其实这里是datetime类中replace和timedelta的结合体
year,month,…参数就是把变量的时间替换为关键字参数的新值,而years,months,…参数其实就是timedelta的功能,即时间间隔,可以为负数,但是不能为小数,这是与timedelta的区别,从参数就可以看到很明显,增加了years,months的偏移,弥补了 timedelta 短板。例如:

d = datetime.datetime(2020, 3, 31, 8, 3, 15, 30)
print(d)
later = relativedelta(months=2)  # 返回relativedelta(months=+2),两个月的间隔,会按月天数自动计入
print(d+later)  # --> 2020-05-31 08:03:15.000030
later = relativedelta(month=2)  # 注意 months 和 month的区别,month是把月份改成2月,months是两个月间隔,累加2个月。返回日期2月份没有31日,会自动转换到2月的最后一天
print(d + later) # --> 2020-02-29 08:03:15.000030

relativedelta 除了补充timedelta的月和年偏移意外,还有一些很方便的扩展功能:
dt1和dt2,直接看案例:

c = relativedelta(dt1=d, dt2=datetime.date(2001, 1, 1))  # dt1-dt2 两个日期之间差,返回一个relativedelta类的元祖
print(c)  # 返回 relativedelta(years=+19, months=+2, days=+30, hours=+8, minutes=+3, seconds=+15, microseconds=+30)
print(c.years)  # relativedelta 使用参数dt1和dt2返回的结果,只能使用类似属性的方式获取

参数 weekday=None,其实也非常方便:

print(datetime.date.today()+relativedelta(weekday=FR))  # 获取下一个周五的日期,也可用datetime,获取下一个周五的相同时间
print(datetime.date.today()+relativedelta(day=31, weekday=FR(-1)))  # 获取这个月最后一个周五,MO, TU, WE, TH, FR, SA, SU
print(datetime.date.today() + relativedelta(day=1, weekday=FR))  # 获取这个月第一个周五
print(datetime.date.today() + relativedelta(weekday=WE(+1)))  # 从今天开始算的下一个周三
print(datetime.date.today() + relativedelta(days=+5, weekday=WE(+1)))  # 从5天后开始算的下一个周三
print(datetime.date.today() + relativedelta(months=+1, day=1, weekday=FR))  # 下个月的第一个周五

参数yearday=None, nlyearday=None,个人感觉就有些鸡肋了

print(datetime.datetime.now() + relativedelta(yearday=260))  # 260天后的日期,但是不能大于366天,而且最终结果不能跨年,如果跨年返回的结果就是今年最后一天的日期,不能跨年在作用上就有些受限了,敝人经历中很少用到,到时 weekday 比较常用,比如在期货交易中计算交割日
print(datetime.date(2000, 1, 1) + relativedelta(yearday=260))
print(datetime.date(2000, 1, 1) + relativedelta(nlyearday=260))  # nlyearday 主要区别与 yearday 是否忽略闰年

2、paser 类

是 dateutil 模块中子模块,这个模块提供了一个通用的日期/时间字符串解析器,它能够解析大多数已知的格式来表示日期和/或时间。据官方文档介绍,即使有一些时间日期是模糊的,这个模块也可以处理。其实主要用的是模块中的 parse 函数,函数的定义:
parse(timestr, parserinfo=None, **kwargs):
解析非标准格式字符串时间方法:

d = '20200325082532'
print(parse(d))  # 返回 2020-03-25 08:25:32  <class 'datetime.datetime'>
print(parse(d).strftime('%Y-%m-%d %H:%M:%S'))  # 返回 2020-03-25 08:25:32  <class 'str'>

至于他的模糊处理还是比较麻烦的,其实也比较少用,这里列举一个官方的简单例子

m = parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
print(m)  # --> 返回一个元祖(datetime.datetime(2047, 1, 1, 8, 21), ('Today is ', ' ', ' ', 'at '))
print(m[0])  # 元祖的第一个值是处理后的时间,第二个值是一些干扰字符

三、pytz 模块

官方文档 官方文档介绍简述:pytz模块可以实现精确的跨平台时区计算,并且解决了夏令时结束时的模糊时间问题。

1、查看时区,给时间添加时区属性

print(pytz.common_timezones_set)  # 返回结果-->LazySet({'Asia/Muscat', 'America/Tortola', 'Antarctica/Troll', 'Europe/Monaco', ...})
for n in pytz.common_timezones_set:  # 查看pytz中常见哪些时区,中国只有上海
    print(n)
for n in pytz.all_timezones:  # 查看全部时区,返回值是列表格式的可迭代对象:<class 'pytz.lazy.LazyList.__new__.<locals>.LazyList'>
    print(n)
for n in pytz.all_timezones_set:  # 查看全部时区 与 pytz.all_timezones 结果相同,只是返回值是一个集合格式的可迭代对象 <class 'pytz.lazy.LazySet.__new__.<locals>.LazySet'>
    print(n)
print(pytz.country_names['CN'])  # 查看国家的全称
print(pytz.country_timezones['CN'])  # 查看对应国家的时区代表城市
tz = pytz.timezone('America/New_York')  # 纽约时区
print(datetime.datetime.now(tz))  # 对应时区的当前时间
print(datetime.datetime(2020, 3, 30, 16, 20, tzinfo=tz))  # 这里需要注意的是,now(tz=pytz.timezone)是直接获得了对应时区的当前时间,但是datetime(int,int,...,tzinfo=pytz.timezone)并不是把时间转换的对应时区,而是给前面的时间增加了时区属性,时间数值并没有改变,所以不能用这个方式进行时区换算。对于没有夏令时转换的时区这样创建跨时区时间是没有问题的。如果涉及跨时区和夏令时的转换,可以使用下面的localize和astimezone

2、localize和astimezone 给时间增加时区属性和时间的时区转换

localize 是pytz模块下UTC类内的一个方法。定义:localize(dt, is_dst=False);
但是astimezone并不是pytz模块下的类或方法,而是datetime模块下的一个方法,之所以放到这里介绍,主要是因为这个方法下的唯一参数就是时区,需要用到pytz模块的属性,定义:astimezone(self, tz=None):

eastern = pytz.timezone('US/Eastern')  # 
print(eastern.zone)
amsterdam = pytz.timezone('Europe/Amsterdam')  # 欧洲--阿姆斯特丹
print(amsterdam)
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
# localize给一个没有设置时区的时间设置时区,另外可以增加是否夏令时参数,但是这里并不如直接使用后面的astimezone更加方便。这里注意的是,要增加时区的变量时间必须是之前没有timezone属性,否则会报错!
loc_dt = eastern.localize(datetime.datetime(2020, 3, 28, 1, 0, 0), is_dst=True)
print(loc_dt)
print(loc_dt.strftime(fmt))
 # astimezone 把变量时间转换为指定时区的时间,前面变量时间不管有是否有timezone属性,都会按标准转换为参数的时区时间
ams_dt = loc_dt.astimezone(amsterdam)  # 把已经设置timezone属性的loc_dt转换为amsterdam时区时间
print(ams_dt.strftime(fmt))
ams_dt = datetime.datetime.now().astimezone(amsterdam)  # 把本地化的当前时间转换为指定时区的时间,这里的效果其实与datetime.datetime.now(tz=amsterdam)相同
print(ams_dt)

感叹:整理这篇博文用了将近一天时间,但是感觉这并不浪费,可能年龄大了脑子不好用了,很多东西记不牢,用的时候一时想不起来,与其网上搜索不如整理一下记在自己博客里面

补充:
1、获取几天前、几个月前和几年前的日期和时间
2、获取当前或指定时间季度初和季度末的时间

# 获取1天前的当下时间
date_r = datetime.now() - timedelta(days=1)
print(date_r)
# 获取1天前开始时间(一天前的0点0分0秒)
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
date_ra = today - timedelta(days=1)
print(date_ra)

# 获取几个月几年前的日期时间
def get_date_month(now, mon=0):
    """
    :param now: 当前时间或指定时间
    :param mon: 获取当前时间X月之前的时间
    :return: YYYY-MM

    """
    # 当前时间X个月前
    last_m = (int(now.year) * 12 + int(now.month) - mon) % 12
    last_y = int((int(now.year) * 12 + int(now.month) - mon) / 12)
    if last_m == 0:
        last_m = 12
        last_y -= 1
    last_m = str(last_m).zfill(2)
    last_d = str(now.day).zfill(2)
    last_h = str(now.hour).zfill(2)
    last_M = str(now.minute).zfill(2)
    last_s = str(now.second).zfill(2)

    last_time = f'{last_y}-{last_m}-{last_d} {last_h}:{last_M}:{last_s}'
    last_time = datetime.strptime(last_time, '%Y-%m-%d %H:%M:%S')
    # print(last_time)
    return last_time

# 例如:
# 获取一个月前的当前时间
get_date_month(datetime.now(), 1)
# 获取一年前的当前时间
get_date_month(datetime.now(), 1 * 12)
# 获取一个月前数据月初时间
month = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)  # 当月开始的时间
get_date_month(month, 1)

# 获取一年前年初时间
year = datetime.now().replace(month=1, day=1, hour=0, minute=0,
                              second=0, microsecond=0)  # 当前年份
get_date_month(year, 1*12)


# 获取当前季度初和季度末的时间
import calendar
def get_season_start_and_end(now):
    """
    获取当前季度初和季度末的时间
    :param now: 当前时间或指定时间
    :return:
    """
    season = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
    month = now.month
    season_min = season_max = month
    for value in season:
        if month in value:
            # 季度初时间
            season_min = now.replace(month=value[0],
                                    day=1, hour=0, minute=0, second=0, microsecond=0)
            # 季度末时间
            # 1、获取季度末月份的最后一天日期,calendar 返回元组 (周几,月底日期)
            end = calendar.monthrange(now.year, value[2])[1]
            season_max = now.replace(month=value[2],
                                    day=end, hour=23, minute=59, second=59, microsecond=0)

    return season_min, season_max
 
 # 获取一年四个季度中某个季度开始和结束的时间
def get_four_season_start_and_end(season_index: int):
    """
    获取四个季度初和季度末的时间
    :param now: 当前时间或指定时间
    :return:
    """
    if season_index not in [1, 2, 3, 4]:  # 如果输入的数据不是1-4默认显示第一季度
        season_index = 1
    season_index -= 1
    season = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
    month = season[season_index]
    now = datetime.datetime.now()
    # 季度初时间
    season_min = now.replace(month=month[0],
                             day=1, hour=0, minute=0, second=0, microsecond=0)
    # 季度末时间
    # 1、获取季度末月份的最后一天日期,calendar 返回元组 (周几,月底日期)
    end = calendar.monthrange(now.year, month[2])[1]
    season_max = now.replace(month=month[2],
                             day=end, hour=23, minute=59, second=59, microsecond=0)

    return season_min, season_max
# 获取指定季度或者指定时间所在季度初和季度末的时间
def get_season_start_and_end(season_index=0, time=None):
    """
    获取四个季度初和季度末的时间
    :param season_index: 当前第几季度
    :param time: 查询某个时间(datetime),通过时间自动获取是第几季度。填写time后season_index失效
    :return:
    """

    if season_index not in [1, 2, 3, 4]:  # 如果输入的数据不是1-4默认显示第一季度
        season_index = 1
    season_index -= 1
    season = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
    if time:
        # print(time.month, '------', type(time.month))
        for index, month in enumerate(season):
            if time.month in month:
                season_index = index
    # print(season_index, '=============')
    month = season[season_index]
    now = datetime.datetime.now()
    # 季度初时间
    season_min = now.replace(month=month[0],
                             day=1, hour=0, minute=0, second=0, microsecond=0)
    # 季度末时间
    # 1、获取季度末月份的最后一天日期,calendar 返回元组 (周几,月底日期)
    end = calendar.monthrange(now.year, month[2])[1]
    season_max = now.replace(month=month[2],
                             day=end, hour=23, minute=59, second=59, microsecond=0)

    return season_min, season_max
    
# 获取几周前周开始和结束时间
def last_first_date_and_last_date(ween_n):
    """
    获取前n周开始时间和结束时间,参数n:代表前ween_n周
    """
    now = datetime.datetime.now()
    # 上周第一天和最后一天
    before_week_start = now - datetime.timedelta(days=now.weekday() + 7 * ween_n,
                                                 hours=now.hour,
                                                 minutes=now.minute,
                                                 seconds=now.second,
                                                 microseconds=now.microsecond)
    # last_week_end = now - timedelta(days=now.weekday() + 1)
    before_week_end = before_week_start + datetime.timedelta(days=6,
                                                             hours=23,
                                                             minutes=59,
                                                             seconds=59)
    return before_week_start, before_week_end