python中datetime模块详解

作者: iszhenyu | 来源:发表于2016-09-01 16:35 被阅读6051次

    timedelta

    timedelta的实例化

    一个timedalta对象代表了一个时间差,当两个datedatetime进行相减操作时会返回一个timedelta对象,或者,我们也可以手动对其进行实例化,其构造函数的原型如下:

    class datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]])
    

    其中,所有的参数都是可选的,并且默认为0,一般情况下,我们常用的是其中三个参数dayssecondsmicroseconds,如果我们传递了其他的几个参数值,python会帮助我们自动转换成上面三个参数,转换的规则是:

    • 1 millisecond(毫秒) 转换成 1000 microseconds(微秒)
    • 1 minute 转换成 60 seconds
    • 1 hour 转换成 3600 seconds
    • 1 week转换成 7 days

    如果我们在实例化的时候直接传递的是上面三个参数值,那么也要注意下它们的取值范围:

    • 0 <= microseconds < 1000000
    • 0 <= seconds < 3600*24 (一天的秒数)
    • -999999999 <= days <= 999999999

    那么,如果我们在传递这三个参数的时候超出了这个范围会有什么问题吗,答案是不一定,例如:

    >>> tmp = datetime.timedelta(seconds=86400)
    >>> tmp.days
    1
    >>> tmp.seconds
    0
    

    可以看到,如果超过范围,python是会帮我们自动转换的,但是如果days参数超出范围会有什么结果呢?

    >>> ee = datetime.timedelta(days=1000000000)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OverflowError: days=1000000000; must have magnitude <= 999999999
    

    所以,只有days是不能超出范围的,否则会抛出OverflowError异常。

    在实例化的时候,参数不仅仅可以是整数,也可以是浮点数、正数或者负数,当参数为负数的时候,要特别注意,因为生成的样式也许与我们设想的不太一致

    >>> tmp = datetime.timedelta(microseconds=-1)
    >>> tmp.days, tmp.seconds, tmp.microseconds
    (-1, 86399, 999999)
    

    所以,我们传递参数的时候,尽量避免传递负数的情况,同样,我们也应该极力避免传递的参数为浮点数,我们在使用的时候一般以秒作为单位就能满足99%的需求了。

    timedelta的运算

    + 操作

    >>> t1 = datetime.timedelta(seconds=60)
    >>> t2 = datetime.timedelta(seconds=30)
    >>> t3 = t1 + t2
    >>> t3.seconds
    90
    

    - 操作

    >>> t4 = t1 -t2
    >>> t4.seconds
    30
    

    * 操作

    >>> t5 = t1 * 2
    >>> t5
    datetime.timedelta(0, 120)
    >>> t6 = t1 * 0
    >>> t6
    datetime.timedelta(0)
    

    ///操作

    >>> t7 = t1 / 3
    >>> t7
    datetime.timedelta(0, 20)
    >>> t7 = t1 / 7
    >>> t7
    datetime.timedelta(0, 8, 571428)
    >>> t7 = t1 // 7
    >>> t7
    datetime.timedelta(0, 8, 571428)
    

    注意这里的被除数不能是0,否则会抛出ZeroDivisionError

    比较操作

    >>> t1 > t2
    True
    >>> t1 < t2
    False
    >>> t1 == t2
    False
    >>> t1 == 60
    False
    >>> t1 != 60
    True
    >>> t1 > 60
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: can't compare datetime.timedelta to int
    

    可以看到,两个timedelta对象可以直接进行比较操作,而一个timedelta对象与一个非timedelta对象进行==!=操作时总是返回False,而进行><操作则会抛出TypeError

    其他操作

    >>> +t1
    datetime.timedelta(0, 60)
    >>> -t1
    datetime.timedelta(-1, 86340)
    
    // 返回的格式为[D day[s], ][H]H:MM:SS[.UUUUUU]
    >>> str(t1)
    '0:01:00'
    >>> t1
    datetime.timedelta(0, 60)
    >>> str(-t1)
    '-1 day, 23:59:00'
    
    >>> repr(t1)
    'datetime.timedelta(0, 60)'
    

    其他

    在2.7版本后,新增了一个方法timedelta.total_seconds()用于计算秒数,它等价于(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6

    >>> t1
    datetime.timedelta(0, 60)
    >>> t1.total_seconds()
    60.0
    

    date

    一个date对象代表使用年、月、日表示的时间,我们可以只用年、月、日三个值直接构造一个date对象,且这三个参数缺一不可,它们的取值范围如下

    • MINYEAR <= year <= MAXYEAR
    • 1 <= month <= 12
    • 1 <= day <= 给定年月的天数

    如果我们传递的参数超出这个范围,将会抛出ValueError异常。除了手动传入年、月、日来构造date对象外,系统还提供了静态方法,我们可以使用这些静态方法来方便的得到一个date对象

    • date.today()
      返回当前的本地时间,等价于date.fromtimestamp(time.time())

    • date.fromtimestamp(timestamp)

    当得到date对象后,就可以直接访问它的年、月、日属性了

    >>> today = date.today()
    >>> today
    datetime.date(2016, 9, 1)
    >>> today.year
    2016
    >>> today.month
    9
    >>> today.day
    1
    

    运算与比较操作

    >>> delta = timedelta(days=7)
    >>> future = today + delta
    >>> future
    datetime.date(2016, 9, 8)
    >>> future - delta
    datetime.date(2016, 9, 1)
    >>> future - today
    datetime.timedelta(7)
    >>> future > today
    True
    

    date可以作为字典的key,并且,所有的date对象都会被认为是True的,也就是 if date 这个判断永远是成立的。

    实例方法

    • date.replace(year, month, day)
    >>> d = date(2002, 12, 31)
    >>> d
    datetime.date(2002, 12, 31)
    >>> d.replace(day=26)
    datetime.date(2002, 12, 26)
    
    • date.weekday()
      返回一周的礼拜几,用int值表示,从0开始
    • date.isoweekday()
      返回一周的礼拜几,用int值表示,从1开始
    • date.isoformat()
    >>> d.isoformat()
    '2002-12-31'
    

    datetime

    datetime从字面意思上看是datetime的结合,而实际上也是包含了这两个对象的全部信息,我们可以手动构造datetime对象,也可以使用系统提供的静态方法,当我们手动构造的时候,必须要传入yearmonthday三个参数,他们的取值范围与上面讲到的date对象一致。

    我们在处理时间问题时,始终无法回避的是时区问题,在python中,使用datetime.tzinfo来表示时区,但这是一个抽象基类,python也并没帮我们实现任意的时区,因此,我们首先来看下在不涉及时区的时候datetime都有哪些用法,然后我们会创建一个本地时区,也就是东八区,来说明datetime如何与tzinfo结合使用。

    datetime对象的创建

    我们可以通过指定年月日的形式来手动创建一个datetime实例

    >>> cur = datetime(year=2016, month=9, day=2)
    >>> cur
    datetime.datetime(2016, 9, 2, 0, 0)
    

    也可以直接通过静态方法来获的

    >>> datetime.today()
    datetime.datetime(2016, 9, 2, 17, 50, 21, 810692)
    >>> datetime.now()
    datetime.datetime(2016, 9, 2, 17, 50, 34, 89763)
    

    这两个方法都会返回一个本地当前时间,也就是上面的两个写法是等效的,但是要注意的是,now()方法可以传入时区信息,我们稍后再一起讨论。

    除了上面的方法,我们还可以通过时间戳来获取一个对象

    >>> datetime.fromtimestamp(time.time())
    datetime.datetime(2016, 9, 2, 17, 54, 56, 65907)
    

    我们这里同样暂不考虑时区的问题。
    除了通过时间戳获取datetime实例外,我们还可以通过一个格式化的时间字符串来获得实例,这个方法同样是不带时区信息的

    >>> datetime.strptime('2016-09-02 18:00:00', '%Y-%m-%d %H:%M:%S')
    datetime.datetime(2016, 9, 2, 18, 0)
    

    既然一个datetime对象包含了datetime的所有信息,那么能不能通过这两对象来生成一个datetime实例呢,答案是肯定的

    >>> d = date(year=2016, month=9, day=2)
    >>> d
    datetime.date(2016, 9, 2)
    >>> t = time(hour=18, minute=14)
    >>> t
    datetime.time(18, 14)
    >>> datetime.combine(d, t)
    datetime.datetime(2016, 9, 2, 18, 14)
    

    现在datetime也有了,我们总要在它身上做点什么吧,能做什么呢?

    常用方法介绍

    有了datetime对象之后,我们就可以获得对应的datetime对象了

    >>> today = datetime.today()
    >>> today.date()
    datetime.date(2016, 9, 2)
    >>> today.time()
    datetime.time(18, 30, 10, 304536)
    

    那如果我希望得到下个月的今天,该怎么做呢?方法有多种,但是最方便的是直接把月份进行加一操作(不考虑跨年和日期超过月份最大值的情况),这个时候replace函数就派上用场了

    >>> next_month = today.replace(month=today.month+1)
    >>> next_month
    datetime.datetime(2016, 10, 2, 18, 30, 10, 304536)
    

    replace()方法允许我们对datetime的任意字段进行替换,并返回一个新的datetime对象,这个新的对象在其他字段上与原有对象保持一致。

    除此之外,还有一个比较常用的方法strftime(),通过它可以格式化成我们希望的样式

    >>> next_month.strftime('%Y-%m-%d %H:%M:%S')
    '2016-10-02 18:30:10'
    

    还有一种情况,是我们希望将一个datetime对象转成时间戳,很遗憾的是python并没直接提供这个方法,但是提供了一个timetuple()方法,它返回一个time.struct_time对象,通过它我们可以构造出时间戳了

    >>> dd = datetime.today()
    >>> tt = time.mktime(dd.timetuple())
    >>> print int(tt)
    1472816083
    

    时区的定义

    python中给我们提供了datetime.tzinfo这一抽象的基类,如果我们想使用时区,则必须继承这个类来实现自己的时区定义。我们先来看下都有哪些方法可能需要我们来实现

    • tzinto.utcoffset(self, dt)

    这个方法返回本地时间与UTC时间的时差,我们知道,我们国家使用的是东八区,也就是比世界协调时间(UTC)/格林尼治时间(GMT)快8小时的时区,因此我们可以如下实现这个方法

    def utcoffset(self, dt):
        return timedelta(hours=8)
    
    • tzinfo.dst(self, dt)

    这个方法主要是考虑到一些采用夏令时的国家,在固定月份来调整时间,而我们国家是没有采用夏令时,所以直接返回0

    def dst(self, dt):
        return timedelta(0)
    

    而对于采纳了夏令时的国家,则需要把夏令时考虑进去

    def dst(self, dt):
        # Code to set dston and dstoff to the time zone's DST
        # transition times based on the input dt.year, and expressed
        # in standard local time.  Then
    
        if dston <= dt.replace(tzinfo=None) < dstoff:
            return timedelta(hours=1)
        else:
            return timedelta(0)
    

    对于这些国家,我还要修改utcoffset函数,将夏令时的偏移量考虑进去

    def utcoffset(self, dt):
        return timedelta(hours=N) + self.dst(dt)
    
    • tzinfo.tzname(self, dt)

    这个方法用来返回时区的名称,没有太多可说的

    一般情况下,我们只要实现上面三个方法就可以了,例如我们可以这样定义一个UTC时区和我们所在的东八区

    ZERO = timedelta(0)
    HOUR = timedelta(hours=1)
    
    class UTC(tzinfo):
        """UTC"""
    
        def utcoffset(self, dt):
            return ZERO
    
        def tzname(self, dt):
            return "UTC"
    
        def dst(self, dt):
            return ZERO
    
    
    class GMT8(tzinfo):
        """东八区""
    
        def utcoffset(self, dt):
            return HOUR * 8
    
        def tzname(self, dt):
            return 'GMT-8'
    
        def dst(self, dt):
            return ZERO
    

    除了我们自己来定义时区外,我们还可以使用pytz这个模块,通过easy_install命令直接安装即可。

    datetime与时区的结合使用

    我们上面已经介绍了在不考虑时区因素的时候,datetime的一些简单用法,接下来看下加入时区后,有哪些不一样的地方,以及我们需要注意的地方。

    我们在上面提到了类方法now,在不考虑时区的时候,它的作用和类方法today基本是一致的,我们也可以在使用now的时候传递一个tzinfo,如下的两种方式是等效的

    today = datetime.today()
    today = today.replace(tzinfo=GMT8())
    // 等效于
    today = datetime.now(GMT8())
    

    除了now()方法外,系统还提供了utcnow()方法,这个方法没有参数,返回的是UTC的时间,但是要注意的是,这个方法创建的datetime对象同样是不带时区信息的。

    到目前为止,我们都还不知道时区到底有什么作用,假设说我们现在有这样的需求:在数据库中记录的时间全部采用UTC时间,而在展示的时候需要转换成本地时间。我们在查库后,一些ORM(例如:SQLAlchemy)会自动帮我们将时间值转化成datetime对象,但这些datetime都是不带时区信息的,如果我们想转化成本地时间则必须要创建本地的时区

    today = datetime.utcnow()
    today = today.replace(tzinfo=UTC())
    print today //2016-09-06 06:47:42.665574+00:00
    
    today = today.astimezone(GMT8())
    print today //2016-09-06 14:47:42.665574+08:00
    

    如上所示,系统提供了datetime.astimezone(tz)方法,使用这个方法可以在各个时区之间来回转换时间。这里有两点需要注意:

    • 如果datetime对象本身没有包含时区信息,调用这个方法会抛出ValueError,并提示astimezone() cannot be applied to a naive datetime
    • replace(tzinfo=...)方法只会替换tzinfo的值,并不会更改时分秒等时间信息

    最后,还有两个通过时间戳获取datetime的类方法,datetime.fromtimestamp(timestamp[, tz])datetime.utcfromtimestamp(timestamp)utcfromtimestamp得到的是一个UTC时间,并且不带tzinfo信息,除此之外,两者并无太大区别。

    到这里,datetime模块的大部分方法就介绍完了,还有一些我们不是很常用的方法,大家可以自行看看文档了解下就好。


    这样学机器学习

    相关文章

      网友评论

        本文标题:python中datetime模块详解

        本文链接:https://www.haomeiwen.com/subject/wdthettx.html