编程入门16:Python时间操作

上一篇:编程入门15:Python迭代机制

Python标准库包含有一个time模块用于基本的时间处理,其中的time()函数会读取系统时钟并返回float类型的Unix纪元“时间戳”(Timestamp),即当前时间距离国际标准时间1970年1月1日0点的秒数。另一个sleep()函数则会让程序“休眠”指定的秒数再继续执行:

In [1]: import time

In [2]: time.time()  # 北京时间2018年8月15日21时48分
Out[2]: 1534340898.5661483

In [3]: for i in range(1, 11):
   ...:     time.sleep(1)
   ...:     print("{}秒".format(i), end=" ")
   ...:     
1秒 2秒 3秒 4秒 5秒 6秒 7秒 8秒 9秒 10秒 

如果你想要确定一段程序的运行时间,可以在代码块的首尾分别获取时间并相减——比较相对时间推荐使用以下两个函数更为精确高效:perf_counter()返回当前系统开机运行的秒数,process_time()则返回当前进程实际运行的秒数(休眠时间不算在内)。

In [4]: t = time.time()
   ...: time.sleep(5)
   ...: print(time.time()-t)
5.002017259597778

In [5]: t = time.perf_counter()
   ...: time.sleep(5)
   ...: print(time.perf_counter()-t)
5.000831686000026

In [6]: t = time.process_time()
   ...: time.sleep(5)
   ...: print(time.process_time()-t)
0.0

如果你想基于公历来进行日期时间的显示和运算,就要引入datetime模块,该模块主要包含datetime类型,其中的类方法fromtimestamp()或utcfromtimestamp()可以将时间戳转换为表示对应地方时或国际标准时的datetime对象:

In [7]: import datetime as dt

In [8]: t = time.time()

In [9]: dt.datetime.fromtimestamp(t)  # 地方时(北京时间)
Out[9]: datetime.datetime(2018, 8, 15, 21, 49, 57, 47818)

In [10]: dt.datetime.utcfromtimestamp(t)  # 国际标准时(格林威治时间)
Out[10]: datetime.datetime(2018, 8, 15, 13, 49, 57, 47818)

datetime对象包含int类型的year、month、day、hour、minute、second和microsecond属性,对应年、月、日、时、分、秒和微秒。你可以调用datetime类构造器返回datetime对象,也可以调用类方法now()或utcnow()返回表示当前地方时或国际标准时的datetime对象。

datetime模块还提供了timedelta类型来表示时间差,timedelta类的构造器接受关键字参数weeks、days、hours、minutes、seconds、milliseconds和microseconds指定时间差是多少周、日、时、分、秒、毫秒和微秒——没有年和月因其长度不固定。timedelta对象可以与datetime对象做加减,也可以彼此相加减,还可以乘以或除以int或float数值。

In [11]: d = dt.datetime.now()

In [12]: str(d)  # datetime转字符串,返回标准日期格式
Out[12]: '2018-08-15 21:50:51.605667'

In [13]: d1000 = d + dt.timedelta(days=1000)  # 1000天之后的日期

In [14]: print(d1000)
2021-05-11 21:50:51.605667

datetime模块还包含timezone类型来表示时区,datetime对象要加上timezone类型的tzinfo属性才能无歧义地定位时间点。timezone类构造器的参数是表示与零时区时差的timedelta对象,你也可以引用time.timezone变量从系统时钟得到国际标准时减地方时的秒数,该值取反就是当前时区的时差。

In [15]: d0 = dt.datetime(1970, 1, 1, 0, tzinfo=dt.timezone.utc)  # Unix纪元零时的datetime

In [16]: d0
Out[16]: datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)

In [17]: print(d0)  # Unix纪元零时的标准时间格式
1970-01-01 00:00:00+00:00

In [18]: d0.timestamp()  # Unix纪元零时的时间戳
Out[18]: 0.0

In [19]: d = dt.datetime.now()  # 当前本地时的datetime,不包含时区信息

In [20]: d
Out[20]: datetime.datetime(2018, 8, 15, 21, 53, 7, 74861)

In [21]: d = d.replace(tzinfo=dt.timezone(dt.timedelta(seconds=-time.timezone)))  # 加上时区信息,从系统时钟获取

In [22]: print(d)  # 包含时区的标准时间格式
2018-08-15 21:53:07.074861+08:00

In [23]: d.astimezone(dt.timezone(dt.timedelta(hours=9)))  # 转换为东九区时
Out[23]: datetime.datetime(2018, 8, 15, 22, 53, 7, 74861, tzinfo=datetime.timezone(datetime.timedelta(seconds=32400)))

你还可以用datetime实例的strftime()方法返回指定格式的日期字符串,或用strptime()类方法将日期字符串转为datetime对象——日期格式字符串中使用百分号打头的占位符来包含日期信息,这是继承自C语言的标准格式,详情可以参看官方文档 https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior

In [24]: d.strftime("%Y年%m月%d日%H时%M分%S秒".encode("unicode-escape").decode()).encode().decode("unicode-escape")
Out[24]: '2018年08月15日21时53分07秒'

In [25]: dt.datetime.strptime("1970年1月1日0时", "%Y年%m月%d日%H时")
Out[25]: datetime.datetime(1970, 1, 1, 0, 0)

下面再来看一个定时监测网站状态的程序:

"""xmonitor.py 网站定时监测
定时访问CIV论坛,新增帖子数大于5或发生网络异常则发邮件示警
"""
from urllib.request import urlopen, Request
import re
import time
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr
mail_sender = "******@sohu.com"
mail_receiver = "******@163.com"
smtp_host = "smtp.sohu.com"  # 发送邮件配置,请参看邮箱帮助文档开通SMTP服务
smtp_user = "******@sohu.com"
smtp_pass = "******"
site = "CIV论坛"
url = "http://www.civclub.net/bbs"  # 这是个策略游戏论坛,欢迎大家访问!
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) \
           Gecko/20100101 Firefox/61.0"}


def mail(title, content):
    """发送邮件
    """
    msg = MIMEText(content, "plain", "utf-8")
    msg["From"] = formataddr(["监测员", mail_sender])
    msg["To"] = formataddr(["管理员", mail_receiver])
    msg["Subject"] = Header(f"{site}:{title}", "utf-8")
    try:
        smtp = smtplib.SMTP(smtp_host)
        smtp.ehlo()
        smtp.starttls()
        smtp.login(smtp_user, smtp_pass)
        smtp.sendmail(mail_sender, mail_receiver, msg.as_string())
        print("邮件发送成功")
    except Exception as e:
        print(f"邮件发送失败:{repr(e)}")


def main():
    """每小时获取新增帖数,大于5或发生网络异常则发邮件示警
    """
    posts = 0  # 发帖数
    while True:
        print(f"检查网站:{url}")
        try:
            req = Request(url=url, headers=headers)
            html = urlopen(req).read().decode("gbk")
            match = re.search(r'今日: <em>(\d+?)</em>', html)
            newposts = int(match.group(1))
            increase = newposts - posts
            if increase > 5:
                mail("发帖量大", f"近一小时新增{increase}个帖子")
            posts = newposts
        except Exception as e:
            mail("异常错误", repr(e))
        print("进入休眠……")
        for _ in range(3600):  # 休眠一小时
            time.sleep(1)


if __name__ == "__main__":
    main()
16_mail.png

上面的代码用3600次sleep(1)而非sleep(3600)来实现休眠一小时,以便能随时按Ctrl+C中断程序(如果是用Spyder则在IPython面板中右击选择Quit结束运行)。

——编程原来是这样……

编程小提示:字符串格式化

Python有多种字符串格式化语法,第一种是在字符串内加百分号占位符,这是继承自C语言的标准格式,详情可参看 https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
第二种是str.format()方法,在字符串内加花括号占位符,这是目前普遍使用的语法,详情可参看 https://docs.python.org/3/library/stdtypes.html#str.format
第三种就是Python 3.6新增的“格式字符串”(f-strings),在带f前缀的字符串内加花括号,其中可以放任意表达式,这种语法最为简洁自然,如果你的程序不需要兼容旧版本,那么推荐用格式字符串,详情可参看 https://docs.python.org/3/reference/lexical_analysis.html#f-strings

In [26]: name, year = "Python", 1990

In [27]: "%s语言诞生于%s年。" % (name, year)
Out[27]: 'Python语言诞生于1990年。'

In [28]: "{}语言诞生于{}年。".format(name, year)
Out[28]: 'Python语言诞生于1990年。'

In [29]: f"{name}语言诞生于{year}年。"
Out[29]: 'Python语言诞生于1990年。'
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • 原文链接 《Python数据分析》(Python for Data Analysis, 2nd Edition)第...
    李绍俊阅读 8,091评论 0 5
  • 献计九,结果:袁绍未采纳。 以下是《三国志注》中的记载: 太祖与绍相持日久,百姓疲乏,多叛应绍,军食乏。会绍遣淳于...
    椒乡书生阅读 232评论 0 0
  • 学校里,一群白天下课都不关灯的人,一群晚上盖着被子吹通宵风扇的人……在马路中间兴高采烈举着“关灯一小时”的...
    王雨晴丶WYQ阅读 290评论 0 0
  • 不记得从哪一年开始好像所有的节日都变成了GIRLS要求她们的BOYFRIENDS送礼物的日子。看着一个个因为对方没...
    2lne阅读 326评论 0 0
  • 某大公司新入职两名员工,小王和小李。小王没几个月就调到重要岗位,而小李还在试用期的基础岗位。小李纳闷,为什么我们一...
    搬砖的赵工阅读 421评论 0 0