超越datetime:Arrow,Python中的日期时间管理大师

介绍

Arrow是一个Python库,它提供了一种合理且对人类友好的方法来创建、操作、格式化和转换日期、时间和时间戳。它实现了对datetime类型的更新,填补了功能上的空白,提供了一个智能的模块API,支持许多常见的创建场景。简单来说,它可以帮助您使用更少的导入和更少的代码来处理日期和时间。

Arrow以时间之箭命名,并且受到moment.jsrequests的极大启发。

为什么要使用Arrow而不是内置模块?

Python的标准库和其他一些低级模块拥有近乎完整的日期、时间和时区功能,但从可用性的角度来看,它们的工作效果并不理想:

  • 模块太多:datetime, time, calendar, dateutil, pytz

  • 类型太多:date, time, datetime, tzinfo, timedelta, relativedelta

  • 时区和时间戳转换冗长且不愉快

  • 通常情况下都是时区不敏感的

  • 功能上存在gap:ISO 8601支持、时区间隔支持、不够人性化

特性

  • 完全实现的,可替代datetime

  • 支持Python 3.6+

  • 默认支持时区感知和UTC

  • 为许多常见输入场景提供超简单的创建选项

  • shift方法支持相对偏移,包括周

  • 自动格式化和解析字符串

  • 广泛支持ISO 8601标准

  • 时区转换

  • 支持dateutil, pytzZoneInfo tzinfo对象

  • 生成时间段、范围、下限和上限,适用于从微秒到年的时间框架

  • 使用不断增长的贡献语言环境人性化日期和时间

  • 可扩展为您自己的Arrow派生类型

  • 全面支持PEP 484风格的类型提示

快速开始

安装

使用 pip or pipenv:

pip install -U arrow

简单使用

>>> import arrow
>>> arrow.get('2024-06-13T17:23:58.970460+08:00')
<Arrow [2024-06-13T17:23:58.970460+08:00]>

>>> utc = arrow.utcnow()
>>> utc
<Arrow [2024-06-13T08:37:39.950050+00:00]>

>>> utc = utc.shift(hours=-1)
>>> utc
<Arrow [2024-06-13T07:37:39.950050+00:00]>

>>> local = utc.to('Asia/Shanghai')
>>> local
<Arrow [2024-06-13T15:37:39.950050+08:00]>

>>> local.timestamp()
1718264259.95005

>>> local.format()
'2024-06-13 15:37:39+08:00'

>>> local.format('YYYY-MM-DD HH:mm:ss ZZ')
'2024-06-13 15:37:39 +08:00'

>>> local.humanize()
'an hour ago'

>>> local.humanize(locale='zh-cn')
'1小时前'

用户指南

Creation(创建对象)

通过now方法创建:

>>> arrow.utcnow()
<Arrow [2024-06-13T09:03:42.591212+00:00]>
>>> arrow.now()
<Arrow [2024-06-13T17:04:00.926319+08:00]>
>>> arrow.now('Asia/Shanghai')
<Arrow [2024-06-13T17:04:25.058905+08:00]>

通过时间戳创建 (int or float):

>>> arrow.get(1718264259) 
<Arrow [2024-06-13T07:37:39+00:00]>
>>> arrow.get(1718264259.95005)
<Arrow [2024-06-13T07:37:39.950050+00:00]>

使用无时区时间、有时区时间,或者灵活指定时区:

>>> arrow.get(datetime.utcnow())
<Arrow [2024-06-13T09:14:15.099808+00:00]>
>>> arrow.get(datetime(2024, 6, 13), 'Asia/Shanghai') 
<Arrow [2024-06-13T00:00:00+08:00]>
>>> from dateutil import tz
>>> arrow.get(datetime(2024, 6, 13), tz.gettz('Asia/Shanghai')) 
<Arrow [2024-06-13T00:00:00+08:00]>
>>> arrow.get(datetime.now(),tz.gettz('Asia/Shanghai')) 
<Arrow [2024-06-13T17:16:02.309002+08:00]>

从字符串转换:

>>> arrow.get('2024-06-13 15:30:45', 'YYYY-MM-DD HH:mm:ss') 
<Arrow [2024-06-13T15:30:45+00:00]>

从文本中查找日期:

>>> arrow.get('June was born in May 1980', 'MMMM YYYY')
<Arrow [1980-05-01T00:00:00+00:00]>

自动识别ISO 8601格式的字符串,并解析出相应的日期时间对象,包括时区信息:

>>> arrow.get('2024-06-13T15:34:00.000-08:00') 
<Arrow [2024-06-13T15:34:00-08:00]>

直接实例化Arrow对象(使用datetime类一样的参数):

>>> arrow.get(2024, 6, 13)
<Arrow [2024-06-13T00:00:00+00:00]>
>>>
>>> arrow.Arrow(2024, 6, 13)
<Arrow [2024-06-13T00:00:00+00:00]>

Properties(属性)

获取日期时间或时间戳表示形式:

>>> a = arrow.utcnow()
>>> a.datetime  
datetime.datetime(2024, 6, 13, 9, 25, 1, 195217, tzinfo=tzutc())

获取一个原生的时间,包含tzinfo:

>>> a.naive
datetime.datetime(2024, 6, 13, 9, 25, 1, 195217)
>>> a.tzinfo
tzutc()

获取任意日期时间值:

>>> a.year
2024
>>> a.month
6

调用datetime类相关函数和属性:

>>> a.date()
datetime.date(2024, 6, 13)
>>> a.time()
datetime.time(9, 25, 1, 195217)

Replace & Shift(替换和偏移)

获取一个新的Arrow对象,更改其属性,就像处理datetime类型一样:

>>> arw = arrow.utcnow()
>>> arw
<Arrow [2024-06-13T09:33:30.538303+00:00]>
>>> arw.replace(hour=4, minute=40)
<Arrow [2024-06-13T04:40:30.538303+00:00]>

或者,将属性向前或向后移动的:

>>> arw.shift(weeks=+3)
<Arrow [2024-07-04T09:33:30.538303+00:00]>
>>> arw.shift(days=-3)  
<Arrow [2024-06-10T09:33:30.538303+00:00]>

甚至,直接替换时区

>>> arw.replace(tzinfo='US/Pacific')
<Arrow [2024-06-13T09:33:30.538303-07:00]>

Format(格式化)

arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')
'2024-06-13 09:34:54 +00:00'

Convert(转换)

按名称或tzinfo从UTC转换为其他时区:

>>> utc = arrow.utcnow()
>>> utc
<Arrow [2024-06-13T09:36:22.204239+00:00]>

>>> utc.to('US/Pacific')
<Arrow [2024-06-13T02:36:22.204239-07:00]>

>>> utc.to(tz.gettz('US/Pacific'))
<Arrow [2024-06-13T02:36:22.204239-07:00]>

或者使用简写:

>>> utc.to('local')
<Arrow [2024-06-13T17:36:22.204239+08:00]>

>>> utc.to('local').to('utc')
<Arrow [2024-06-13T09:36:22.204239+00:00]>

Humanize(人性化)

相对于现在时间的人性化表现:

>>> past = arrow.utcnow().shift(hours=-1)
>>> past.humanize()
'an hour ago'

或者另一个Arrow对下或日期时间:

>>> present = arrow.utcnow()
>>> future = present.shift(hours=2)
>>> future.humanize(present)
'in 2 hours'

将时间表示为相对时间或仅包括时间距离:

>>> present = arrow.utcnow()
>>> future = present.shift(hours=2)
>>> future.humanize(present)
'in 2 hours'
>>> future.humanize(present, only_distance=True)
'2 hours'

指定特定的时间粒度(或多个):

>>> present = arrow.utcnow()
>>> future = present.shift(minutes=66)
>>> future.humanize(present, granularity="minute")
'in 66 minutes'
>>> future.humanize(present, granularity=["hour", "minute"])
'in an hour and 6 minutes'
>>> present.humanize(future, granularity=["hour", "minute"])
'an hour and 6 minutes ago'
>>> future.humanize(present, only_distance=True, granularity=["hour", "minute"])
'an hour and 6 minutes'

支持不同时区的语言显示:

>>> future = arrow.utcnow().shift(hours=1)

>>> future.humanize(a, locale='ru')
'через час'

>>> future.humanize(a, locale='zh-cn') 
'1小时后'

Dehumanize(去人性化)

使用人类可读的字符串描述,并使用它转换到过去的时间:

>>> arw = arrow.utcnow()
>>> arw
<Arrow [2024-06-13T09:47:35.616633+00:00]>

>>> earlier = arw.dehumanize("2 days ago")
>>> earlier
<Arrow [2024-06-11T09:47:35.616633+00:00]>

或者用它来转换到未来的时间:

>>> arw = arrow.utcnow()
>>> arw
<Arrow [2024-06-13T09:47:35.616633+00:00]>


>>> later = arw.dehumanize("in a month")
>>> later
<Arrow [2024-07-13T09:47:35.616633+00:00]>

支持不同时区的语言转换:

>>> later = arw.dehumanize('1小时后', locale="zh-cn") 
>>> later
<Arrow [2024-06-13T10:47:35.616633+00:00]>

Ranges & Spans(范围&跨度)

获取任意单位的时间跨度:

>>> arrow.utcnow().span('hour')
(<Arrow [2024-06-13T09:00:00+00:00]>, <Arrow [2024-06-13T09:59:59.999999+00:00]>)
>>> arrow.utcnow().span('day')  
(<Arrow [2024-06-13T00:00:00+00:00]>, <Arrow [2024-06-13T23:59:59.999999+00:00]>)

或者只获取最大最小值:

>>> arrow.utcnow().floor('hour')
<Arrow [2024-06-13T09:00:00+00:00]>
>>> arrow.utcnow().ceil('hour')
<Arrow [2024-06-13T09:59:59.999999+00:00]>

您还可以获取一系列时间跨度:

>>> start = datetime(2024, 6, 13, 12, 30) 
>>> end = datetime(2024, 6, 13, 17, 15)
 
>>> for r in arrow.Arrow.span_range('hour', start, end):
...     print(r)
...
(<Arrow [2024-06-13T12:00:00+00:00]>, <Arrow [2024-06-13T12:59:59.999999+00:00]>)
(<Arrow [2024-06-13T13:00:00+00:00]>, <Arrow [2024-06-13T13:59:59.999999+00:00]>)
(<Arrow [2024-06-13T14:00:00+00:00]>, <Arrow [2024-06-13T14:59:59.999999+00:00]>)
(<Arrow [2024-06-13T15:00:00+00:00]>, <Arrow [2024-06-13T15:59:59.999999+00:00]>)
(<Arrow [2024-06-13T16:00:00+00:00]>, <Arrow [2024-06-13T16:59:59.999999+00:00]>)
(<Arrow [2024-06-13T17:00:00+00:00]>, <Arrow [2024-06-13T17:59:59.999999+00:00]>)

或者只是获取一系列时间点:

>>> start = datetime(2024, 6, 13, 12, 30) 
>>> end = datetime(2024, 6, 13, 17, 15)

>>> for r in arrow.Arrow.range('hour', start, end):
...     print(repr(r))
...
<Arrow [2024-06-13T12:30:00+00:00]>
<Arrow [2024-06-13T13:30:00+00:00]>
<Arrow [2024-06-13T14:30:00+00:00]>
<Arrow [2024-06-13T15:30:00+00:00]>
<Arrow [2024-06-13T16:30:00+00:00]>

Factories(工厂方法)

使用工厂模式实现自定义Arrow派生类型,来继承和扩展Arrow的模块API。首先,定义您的类型:

class CustomArrow(arrow.Arrow):
...
...     def days_till_xmas(self):
...         xmas = arrow.Arrow(self.year, 12, 25)
...         if self > xmas:
...             xmas = xmas.shift(years=1)
...         return (xmas - self).days

然后使用工厂:

>>> factory = arrow.ArrowFactory(CustomArrow)
>>> custom = factory.utcnow()
>>> custom
<CustomArrow [2024-06-13T10:02:04.898347+00:00]>
>>> custom.days_till_xmas()
194

Supported Tokens(支持的格式)

使用以下标记进行分析和格式化。请注意,它们与strptime的标记不同:

标记格式 示例
Year YYYY 2000, 2001, 2002 ... 2012, 2013
YY 00, 01, 02 ... 12, 13
Month MMMM January, February, March ...1
MMM Jan, Feb, Mar ...1
MM 01, 02, 03 ... 11, 12
M 1, 2, 3 ... 11, 12
Day of Year DDDD 001, 002, 003 ... 364, 365
DDD 1, 2, 3 ... 364, 365
Day of Month DD 01, 02, 03 ... 30, 31
D 1, 2, 3 ... 30, 31
Do 1st, 2nd, 3rd ... 30th, 31st
Day of Week dddd Monday, Tuesday, Wednesday ...2
ddd Mon, Tue, Wed ...2
d 1, 2, 3 ... 6, 7
ISO week date W 2011-W05-4, 2019-W17
Hour HH 00, 01, 02 ... 23, 24
H 0, 1, 2 ... 23, 24
hh 01, 02, 03 ... 11, 12
h 1, 2, 3 ... 11, 12
AM / PM A AM, PM, am, pm1
a am, pm1
Minute mm 00, 01, 02 ... 58, 59
m 0, 1, 2 ... 58, 59
Second ss 00, 01, 02 ... 58, 59
s 0, 1, 2 ... 58, 59
Sub-second S... 0, 02, 003, 000006, 123123123123...3
Timezone ZZZ Asia/Baku, Europe/Warsaw, GMT ...4
ZZ -07:00, -06:00 ... +06:00, +07:00, +08, Z
Z -0700, -0600 ... +0600, +0700, +08, Z
Seconds Timestamp X 1381685817, 1381685817.915482 ...5
ms or µs Timestamp x 1569980330813, 1569980330813221

Built-in Formats(内置格式)

>>> arw = arrow.utcnow()
>>> arw.format(arrow.FORMAT_ATOM)
'2024-06-13 10:05:40+00:00'
>>> arw.format(arrow.FORMAT_COOKIE)
'Thursday, 13-Jun-2024 10:05:40 UTC'
>>> arw.format(arrow.FORMAT_RSS)
'Thu, 13 Jun 2024 10:05:40 +0000'
>>> arw.format(arrow.FORMAT_RFC822)
'Thu, 13 Jun 24 10:05:40 +0000'
>>> arw.format(arrow.FORMAT_RFC850)
'Thursday, 13-Jun-24 10:05:40 UTC'
>>> arw.format(arrow.FORMAT_RFC1036)
'Thu, 13 Jun 24 10:05:40 +0000'
>>> arw.format(arrow.FORMAT_RFC1123)
'Thu, 13 Jun 2024 10:05:40 +0000'
>>> arw.format(arrow.FORMAT_RFC2822)
'Thu, 13 Jun 2024 10:05:40 +0000'
>>> arw.format(arrow.FORMAT_RFC3339)  
'2024-06-13 10:05:40+00:00'
>>> arw.format(arrow.FORMAT_W3C)
'2024-06-13 10:05:40+00:00'

Escaping Formats(转义格式)

解析和格式化时,可以通过将格式字符串中的标记、短语和正则表达式括在方括号中来转义它们。

标记格式

>>> fmt = "YYYY-MM-DD h [h] m"

>>> arw = arrow.get("2024-06-13 8 h 40", fmt) 
>>> arw.format(fmt)                           
'2024-06-13 8 h 40'

>>> fmt = "YYYY-MM-DD h [hello] m"
>>> arw = arrow.get("2024-06-13 8 hello 40", fmt) 
>>> arw
<Arrow [2024-06-13T08:40:00+00:00]>
>>> arw.format(fmt)
'2024-06-13 8 hello 40'

>>> fmt = "YYYY-MM-DD h [hello world] m"
>>> arw = arrow.get("2024-05-16 8 hello world 40", fmt) 
>>> arw.format(fmt)
'2024-05-16 8 hello world 40'

正则

您还可以通过将正则表达式括在方括号内来转义它们。在下面的示例中,我们使用正则表达式s+来匹配分隔标记的任意数量的空白字符。如果您不提前知道令牌之间的空间数(例如,在日志文件中),这将非常有用。

>>> fmt = r"ddd[\s+]MMM[\s+]DD[\s+]HH:mm:ss[\s+]YYYY"
>>> arrow.get("Thu Jun 16 16:41:45 2024", fmt)
<Arrow [2024-06-16T16:41:45+00:00]>

>>> arrow.get("Thu \tJun 16     16:41:45        2024", fmt) 
<Arrow [2024-06-16T16:41:45+00:00]>

>>> arrow.get("Thu Jun 16    16:41:45  2024", fmt) 
<Arrow [2024-06-16T16:41:45+00:00]>

Punctuation(标点符号)

日期和时间格式的两侧可以用以下列表中的一个标点符号隔开:、.;:!" \ ' [ ] { } ( ) < >`

>>> arrow.get("Cool date: 2024-06-16T09:12:45.123456+04:30.", "YYYY-MM-DDTHH:mm:ss.SZZ")
<Arrow [2024-06-16T09:12:45.123456+04:30]>

>>> arrow.get("Tomorrow (2024-06-16) is Halloween!", "YYYY-MM-DD") 
<Arrow [2024-06-16T00:00:00+00:00]>

>>> arrow.get("Halloween is on 2024.06.16.", "YYYY.MM.DD") 
<Arrow [2024-06-16T00:00:00+00:00]>

>>> arrow.get("It's Halloween tomorrow (2024-06-16)!", "YYYY-MM-DD") 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\LocalCache\local-packages\Python312\site-packages\arrow\api.py", line 91, in get
    return _factory.get(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\LocalCache\local-packages\Python312\site-packages\arrow\factory.py", line 292, in get
    dt = parser.DateTimeParser(locale).parse(
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\LocalCache\local-packages\Python312\site-packages\arrow\parser.py", line 323, in parse
    raise ParserMatchError(
arrow.parser.ParserMatchError: Failed to match 'YYYY-MM-DD' when parsing "It's Halloween tomorrow (2024-06-16)!".

Redundant Whitespace(冗余空白字符处理)

多余的空白字符(空格、制表符和换行符)可以通过将normalize_whitespace标志传递到arrow.get来自动规范化:

>>> arrow.get('\t \n  2024-06-13T12:30:45.123456 \t \n', normalize_whitespace=True) 
<Arrow [2024-06-13T12:30:45.123456+00:00]>

>>> arrow.get('2024-06-13  T \n   12:30:45\t123456', 'YYYY-MM-DD T HH:mm:ss S', normalize_whitespace=True) 
<Arrow [2024-06-13T12:30:45.123456+00:00]>