专栏首页Python中文社区探究Python时间处理模块

探究Python时间处理模块

不管是哪门语言,碰触时间处理相关议题时,如果开发者要认真面对,往往都会感到异常复杂。

复杂来自两个部份:时间本身就因为历史、经济、政治等考量而复杂,API本身的设计经常令人困惑或易于犯错。

因此,如果想要避开后者,唯一能凭藉的,就是对于前者的认识。

旧有的time模块

对于时间处理,Python内建的标准程式库有著两个模块,旧有的time模块,以及自Python 2.3开始出现的datetime模块。不少文件或书籍两者都会介绍,并且鼓励开发者应该使用datetime模块。

然而,实际上,并不是那么简单的分野。毕竟,在Python 3.x之中,time模块还是存在的,从2.3到3.x这么长的时间裡,都未被废弃,突显了time模块仍有其存在的价值。

第一个价值来自于time函式。

因为,这表示了一个绝对时间:自epoch开始至今经过的秒数。虽然大多数的系统epoch,都会是1970年1月1日0时0分0秒,不过,gmtime(0)可以告诉开发者正确的答案,尽管API上有gmt字样,实际上,表示了UTC。即便有许多开发者不知道这个事实,然而,GMT时间经常不严谨(且有争议性)地被当成是UTC时间。

其他有价值的部分,则是mktime函数与struct_time。

struct_time是个桥梁,担任著人类时间概念与机器时间概念之间的转换工作。如果开发者手中有个包含了时间各属性的struct_time实例,可以透过mktime转换为epoch秒数。举例来说,开发者若手中有个代表人类时间概念的datetime实例,可以透过timetuple方法取得struct_time,这样就能透过mktime转换为epoch秒数。

除此之外,对于time模块中其他API,基本上,就不鼓励使用了。

这特别是由于time模块中许多行为,都与底层平台相依,它们会呼叫平台上的C程序库,而有些函数底层可能行为不同或不支援,像是time.tzset()就只在Unix环境中,才可使用,这连带使得strptime函数在某些情况下,无法正常运作。

使用time模块来进行时间运算,或者是时区处理,也是极度不建议的尝试。

人类时间概念的datetime模块

人类在时间的表达上,有时只需要日期、有时只需要时间,有时会一起表达日期与时间,而且,通常不会特别声明时区,可能只会提及年、月、年月、月日、时分秒等,简而言之,人类在时间概念的表达,大多是笼统、片段的资讯。

Python的datetime模块,基本上可用来表达人类的时间概念。因为当中的datetime、date、time预设没有时区资讯,单纯用来表示一个日期或时间,不过这是API上的定义。若程序运行时不需处理时区转换问题,通常所在时区就暗示著是datetime、date、time的时区,因为人们若不特别提及时区,其实就是指本地时区居多。在《Effective Python》中的〈做法45〉,就建议:「本地时钟使用datetime而非time」。

datetime模块中,有一个不错的设计是,datetime本身不可变动(immutable),也考量了时间运算的问题,可以使用timedelta方法指定days、seconds、microseconds、milliseconds等单位,来建立一个时间计量(duration),这支援了+、-、*、/、//、%甚至abs等运算。因此,想知道现在的时间3天又5小时28分之后,会是什么时间,只要撰写datetime.now() + timedelta(days = 3, hours=-5, minutes = 28)就可以了。

不过,就算使用了datetime或date的today(),或者是datetime的now()、utcnow(),谨记著它们也是不带时区资讯的,因此严格来说,开发者不能说datetime.utcnow()建立的datetime实例,代表著UTC时间。例如,datetime.utcnow()若建立了datetime(2016, 5, 23, 6, 55, 30, 505080),这纯粹就只是代表著:「2016年 5月23日6点55分30秒505080微秒」这个时间概念罢了。

复杂的时区处理

对于日期与时间的处理议题上,只要涉及时区,往往就会变得极端复杂,因为牵涉了地理、法律、经济、社会,甚至政治等问题。

例如,Python的datetime实例在建立时,可以透过tzinfo参数指定时区资讯,这必须是tzinfo的实例,然而tzinfo是个抽象类别,Python官方文件中,提供了一些如何实作tzinfo子类别的范例,并且自Python 3.2起,新增了timezone类别作为tzinfo的子类别,用来提供基本的UTC偏移时区实作,其中的timezone.utc,就是指偏移为0小时的UTC时间。

因此,现在可以正式做个区分了。datetime(1975, 5, 26, 3, 20, 50, 0)只是个时间,然而t = datetime(1975, 5, 26, 3, 20, 50, 0, tzinfo = timezone.utc)就可以说它是个代表著UTC时间了,当我们想要转换至台湾时区的时间,由于台湾时区基本上就是偏移8个小时,所以,我们可以撰写为t.astimezone(timezone(offset = timedelta(hours = 8))。

不过,Python内建的timezone只单纯考量了UTC偏移,不考量日光节约时间等其他因素,若需要timezone以外的其他时区定义,目前来说,得额外安装社群贡献的pytz模块(PEP431规范了时区支援的改进,未来可能取代pytz模块)。而pytz模块使用的是Olson时区资料库,是许多语言及作业系统的时区资料来源。

尽管如此,时区与时区之间的转换,依旧复杂而麻烦,因此若应用程式需要储存时间资讯,或甚至进行时间运算,常见的建议是使用绝对的UTC时间,然后,在需要时,再透过astimezone的帮忙,转换为当地时区。

举个例子来说,应用程式在储存留言时间时,可以使用UTC时间,然而网页上要把时间呈现给使用者看时,才依照UTC时间转为对应时区的时间。如果需要在应用程式之间交换时间资讯,以UTC时间来作为交换,也会是个好选择。

语言间真正能过渡的部份

如果开发者从未认真处理过时间的问题,对于以上的时间概念,像是epoch、GMT、UTC等没有明确的认知,等到开始面对time或datetime模块等,也许会感到十分困惑。

针对时区的处理,开发者更可能不解API在使用上何以如此复杂,若是如此,建议了解几个需要知道的时间概念,虽然这边是在讲Python,然而过去探查JDK时间API演进而获取的时间知识,却是非常的受用。

举例而言,我就曾一度被datetime的now()、utcnow(),以及today()等混淆,误认为它们带有时区的概念。然而,后来我开始察觉到它们传回的物件上tzinfo是None,这令人迅速联想到JSR310的LocalDateTime、LocalDate、LocalTime等类别,以及背后所代表的概念。于是,我将time模块与datetime模块,整个重新探查了一遍,理清机器时间与人类时间概念间的差别,接下来,相关API如何使用,也就明朗起来了。

其实时间处理只是一个例子,在探查处理特定议题的API时,对于该门议题的背景知识,往往比找出API的参数、传回值、相依性等外在形式来得重要。

事实上,背景知识能引导开发者思考,如何正确地使用API,避开那些令人困惑的误区,这才是语言间真正能过渡的部份。

本文分享自微信公众号 - Python中文社区(python-china)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-11-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python补充01 序列的方法

    在快速教程中,我们了解了最基本的序列(sequence)。回忆一下,序列包含有定值表(tuple)和表(list)。此外,字符串(string)是一种特殊的定值...

    Vamei
  • Python进阶08 异常处理

    异常处理 在项目开发中,异常处理是不可或缺的。异常处理帮助人们debug,通过更加丰富的信息,让人们更容易找到bug的所在。异常处理还可以提高程序的容错性。 我...

    Vamei
  • Python小题目 针对快速教程

    作业的目的是帮助熟悉之前学习的内容:  1. 写一个程序,判断2008年是否是闰年。 写一个程序,用于计算2008年10月1日是这一年的第几天?(2008年1月...

    Vamei
  • Python进阶06 循环对象

    这一讲的主要目的是为了大家在读Python程序的时候对循环对象有一个基本概念。 循环对象的并不是随着Python的诞生就存在的,但它的发展迅速,特别是Pytho...

    Vamei
  • Python进阶03 模块

    我们之前看到了函数和对象。从本质上来说,它们都是为了更好的组织已经有的程序,以方便重复利用。 模块(module)也是为了同样的目的。在Python中,一个.p...

    Vamei
  • Python进阶05 循环设计

    在“循环”一节,我们已经讨论了Python基本的循环语法。这一节,我们将接触更加灵活的循环方式。 range() 在Python中,for循环后的in跟随一个序...

    Vamei
  • Python进阶04 函数的参数对应

    我们已经接触过函数(function)的参数(arguments)传递。当时我们根据位置,传递对应的参数。我们将接触更多的参数传递方式。 回忆一下位置传递: d...

    Vamei
  • Python标准库的学习准备

    Python标准库是Python强大的动力所在,我们已经在前文中有所介绍。由于标准库所涉及的应用很广,所以需要学习一定的背景知识。 硬件原理 这一部份需要了解内...

    Vamei
  • Python进阶07 函数对象

    秉承着一切皆对象的理念,我们再次回头来看函数(function)。函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋值给其它对象名,或者...

    Vamei
  • Python进阶09 动态类型

    动态类型(dynamic typing)是Python另一个重要的核心概念。我们之前说过,Python的变量(variable)不需要声明,而在赋值时,变量可以...

    Vamei

扫码关注云+社区

领取腾讯云代金券