0%

python cookbook(03):数字日期和时间

这篇文章将学习 Python 中数学运算、日期与时间操作的常见编码技巧。

数字的四舍五入

问题

想对浮点数执行指定精度的舍入运算。

解决方案

对于简单的舍入运算,可以使用内置的 round(value, ndigits)

  • 如果一个值刚好在两个边界的中间,round 函数返回离它最近的偶数
  • 传入 round()ndigits 参数可以是负数,此时舍入运算会作用在十位、百位、千位等上面
  • 不要将舍入和格式化输出搞混淆了,如果只是简单地输出一定宽度的数,不需要使用 round() 函数,而只需要在格式化的时候指定精度即可
  • 同样,不要试着去舍入浮点值来 修正 表面上看起来正确的问题。如果真的不能允许小误差,那么可以考虑使用 decimal 模块

示例

1
2
3
4
5
6
7
8
>>> round(1.23, 1)
1.2
>>> round(1.28, 1)
1.3
>>> round(-1.27, 1)
-1.3
>>> round(1.25361, 3)
1.254
1
2
3
4
>>> round(1.5, 0)
2.0
>>> round(2.5, 0)
2.0
1
2
3
4
5
6
7
8
9
>>> a = 1627731
>>> round(a, 0)
1627731
>>> round(a, -1)
1627730
>>> round(a, -2)
1627700
>>> round(a, -3)
1628000
1
2
3
4
5
6
7
>>> x = 1.23456
>>> format(x, '0.2f')
'1.23'
>>> format(x, '0.3f')
'1.235'
>>> 'value is {:0.3f}'.format(x)
'value is 1.235'
1
2
3
4
5
6
7
8
>>> a = 2.1
>>> b = 4.2
>>> c = a + b
>>> c
6.300000000000001
>>> c = round(c, 2)
>>> c
6.3

执行精确的浮点数运算

问题

需要对浮点数执行精确的计算操作,并且不希望有任何小误差出现。

解决方案

浮点数的一个普遍问题是他们并不能精确的表示十进制数,并且即使是最简单的数学运算也会产生小的误差。如果你想更精确(并且能容忍一定的性能损耗),可以使用 decimal 模块:

decimal 模块的一个主要特征是允许你控制计算的每一方面:包括数字位数和四舍五入运算。为了这样做,你得先创建一个本地上下文并更改它的设置。

dedimal 模块主要涉及金融领域,此时哪怕一点点小的误差在计算过程中都是不允许的。decimal 模块为解决这类问题提供了方法。

示例

1
2
3
4
5
6
>>> a = 4.2
>>> b = 2.1
>>> a + b
6.300000000000001
>>> (a + b) == 6.3
False
1
2
3
4
5
6
7
>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> a + b == Decimal('6.3')
True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> from decimal import localcontext
>>> a = Decimal('1.3')
>>> b = Decimal('1.7')
>>> print(a / b)
0.7647058823529411764705882353
>>> with localcontext() as c:
... c.prec = 3
... print(a / b)
...
0.765
>>> with localcontext() as c:
... c.prec = 50
... print(a / b)
...
0.76470588235294117647058823529411764705882352941176

数字的格式化输出

问题

你需要将数字格式化后输出,并控制数字位数、对齐、千位分隔符和其他细节。

解决方案

格式化输出单个数字的时候,可以使用内置的 format() 函数。

  • 同时指定宽度和精度的一般形式是 [<>^]?width[,]?(.digits)?widthdigits 都是整数,? 表示可选部分
  • 以上格式也适用于字符串的 format 方法
  • 对于浮点数的格式化输出,同样适用于 decimal 模块中的 Decimal 数字对象
  • 当指定数字的位数后,结果值会根据 round() 函数同样的规则进行四舍五入后返回
  • 仍然建议使用 format() 来进行格式化,% 操作符支持的特性不如 format()

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> x = 1234.56789
>>> format(x, '0.2f')
'1234.57'
>>> format(x, '.2f')
'1234.57'

>>> format(x, '>10.2f')
' 1234.57'
>>> format(x, '<10.2f')
'1234.57 '
>>> format(x, '<010.2f')
'1234.57000'
>>> format(x, '>010.2f')
'0001234.57'

>>> format(x, '0,.1f')
'1,234.6'
>>> format(x, '010,.1f')
'0,001,234.6'

>>> format(x, 'e')
'1.234568e+03'
>>> format(x, '0.2E')
'1.23E+03'
1
2
3
4
5
>>> x = 1234.56789
>>> format(x, '0.1f')
'1234.6'
>>> format(-x, '0.1f')
'-1234.6'

二八十六进制整数

问题

你需要转换或输出使用二进制、八进制或十六进制的整数。

解决方案

  • 为了将整数转换为二进制、八进制或十六进制,可以分别使用 bin()oct() 或者 hex() 函数
  • 如果不想输出前缀,可以使用 format() 函数
  • 为了将不同进制转换整数字符串,可以使用带有进制的 int() 函数即可

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> x = 1234

>>> bin(x)
'0b10011010010'
>>> oct(x)
'0o2322'
>>> hex(x)
'0x4d2'

>>> format(x, "#b")
'0b10011010010'
>>> format(x, "b")
'10011010010'
>>> format(x, "#o")
'0o2322'
>>> format(x, "o")
'2322'
>>> format(x, "#x")
'0x4d2'
>>> format(x, "x")
'4d2'

>>> x = -1234
>>> format(x, "x")
'-4d2'
>>> format(x, "b")
'-10011010010'
1
2
3
4
>>> int("0x4d2", 16)
1234
>>> int("10011010010", 2)
1234

字节到大整数的打包与解包

问题

想把一个字节字符串转换为整数,或者想把一个大整数准换为字节字符串

解决方案

  • 为了将 bytes 解析为整数,可以使用 int.from_bytes() 方法,同时指定字节序
  • 为了将一个大整数转换为一个字节序列,使用 int.to_bytes() 方法,并指定字节数
  • 如果需要的话,可以使用 int.bit_length() 方法来决定需要多少位来存储一个整数值

示例

1
2
3
4
5
6
7
8
9
10
11
>>> data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188

>>> x = 94522842520747284487117727783387188
>>> x.to_bytes(16, 'little')
b'4\x00#\x00\x01\xef\xcd\x00\xab\x90x\x00V4\x12\x00'
>>> x.to_bytes(16, 'big')
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
1
2
3
4
5
>>> x = 0x01020304
>>> x.to_bytes(4, 'little')
b'\x04\x03\x02\x01'
>>> x.to_bytes(4, 'big')
b'\x01\x02\x03\x04'
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> x = 523 ** 23
>>> x
335381300113661875107536852714019056160355655333978849017944067
>>> x.to_bytes(16, 'little')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> nbytes, rmem = divmod(x.bit_length(), 8)
>>> if rmem:
... nbytes += 1
...
>>> x.to_bytes(nbytes, 'little')
b'\x03X\xf1\x82iT\x96\xac\xc7c\x16\xf3\xb9\xcf\x18\xee\xec\x91\xd1\x98\xa2\xc8\xd9R\xb5\xd0'

复数的数学运算

问题

需要对复数执行计算操作。

解决方案

  • 复数可以使用函数 complex(real, imag) 或者是带有后缀 j 的浮点数来指定
  • 对应的实部、虚部和共轭复数可以很容易获取
  • 所有常见的数学运算也可以工作
  • 如果需要执行其他的复数函数,可以使用 cmath 模块
  • Python 中的大部分与数学相关的模块都能处理复数,比如 numpy
  • Python 的标准数学函数不会产生复数值,如果想生成一个复数返回结果,需要使用 cmath 模块

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>> a = complex(2, 4)
>>> a
(2+4j)
>>> b = 3 - 5j
>>> b
(3-5j)

>>> a.real
2.0
>>> a.imag
4.0
>>> a.conjugate()
(2-4j)

>>> a + b
(5-1j)
>>> a - b
(-1+9j)
>>> a / b
(-0.4117647058823529+0.6470588235294118j)
>>> abs(a)
4.47213595499958

>>> import cmath
>>> cmath.sin(a)
(24.83130584894638-11.356612711218173j)
>>> cmath.cos(a)
(-11.36423470640106-24.814651485634183j)
>>> cmath.exp(a)
(-4.829809383269385-5.5920560936409816j)
1
2
3
4
5
6
7
8
>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
>>> import cmath
>>> cmath.sqrt(-1)
1j

无穷大与 NaN

问题

想创建或测试正无穷、负无穷或者 NaN(非数字)的浮点数。

解决方案

  • Python 并没有特殊的语法来表示这些特殊的浮点值,但是可以使用 float() 来创建它们
  • 为了测试这些值的存在,使用 math.isinf()math.isnan() 函数来判断
  • 无穷大数在执行数学计算的时候会传播
  • NaN 值在所有操作中传播,而且不会产生异常
  • NaN 值一个特别的地方是它们之间的比较操作总是返回 Flase。因此测试 NaN 值唯一安全的方法就是使用 math.isnan()
  • 某些操作未定义时,会返回 NaN 结果

示例

1
2
3
4
5
6
7
8
9
>>> a = float('inf')
>>> b = float('-inf')
>>> c = float('nan')
>>> a
inf
>>> b
-inf
>>> c
nan
1
2
3
4
5
6
7
8
9
10
11
>>> import math
>>> math.isinf(a)
True
>>> math.isinf(b)
True
>>> math.isinf(c)
False
>>> math.isnan(c)
True
>>> math.isnan(a)
False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> a + 45
inf
>>> a * 10
inf
>>> 10 / a
0.0
>>> 1 < a
True
>>> 1 > b
True

>>> a / a
nan
>>> a + b
nan

>>> c + 233
nan
>>> c / 2
nan

>>> c == c
False
>>> math.isnan(c)
True

分数运算

问题

需要在代码中执行分数运算。

解决方案

fractions 模块可以被用来执行包含分数的数学运算,直接使用分数可以减少手动转换为小数或浮点数的工作。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> from fractions import Fraction
>>> a = Fraction(5, 4)
>>> b = Fraction(7, 16)
>>> a + b
Fraction(27, 16)
>>> print(a * b)
35/64
>>>
>>> c = a * b
>>> c.numerator
35
>>> c.denominator
64
>>> float(c)
0.546875
>>> print(c.limit_denominator(8))
4/7
>>> x = 3.75
>>> y = Fraction(*x.as_integer_ratio())
>>> y
Fraction(15, 4)

大型数组运算

问题

需要在大数据集上执行计算。

解决方案

NumPy 是 Python 领域中很多科学与工程库的基础,同时也是被广泛使用的最大、最复杂的模块。涉及数组的重量级运算操作可以使用 NumPy 库。

  • NumPy 库提供一个 NumPy 数组对象,相比于 Python 原生的数组,更适合用来做数学运算
  • NumPy 库为数组操作提供了大量的通用函数,这些函数可以作为 math 模块中类似函数的替代
  • NumPy 也扩展了 Python 列表的索引功能,特别是对于多维数组

示例

1
2
3
4
5
6
7
8
9
10
>>> x = [1, 2, 3, 4]
>>> y = [5, 6, 7, 8]
>>> x * 2
[1, 2, 3, 4, 1, 2, 3, 4]
>>> x + 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> x + y
[1, 2, 3, 4, 5, 6, 7, 8]
1
2
3
4
5
6
7
8
9
10
>>> import numpy as np

>>> ax = np.array([1, 2, 3, 4])
>>> ay = np.array([5, 6, 7, 8])
>>> ax + ay
array([ 6, 8, 10, 12])
>>> ax * 2
array([2, 4, 6, 8])
>>> ax + 10
array([11, 12, 13, 14])
1
2
3
4
>>> np.sqrt(ax)
array([1. , 1.41421356, 1.73205081, 2. ])
>>> np.cos(ax)
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> grid = np.zeros(shape=(10000, 10000), dtype=float)
>>> grid
array([[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]])
>>> grid + 10
array([[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.],
...,
[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.]])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> a
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> a[1]
array([5, 6, 7, 8])
>>> a[:,1]
array([ 2, 6, 10])
>>> a[1:3, 1:3]
array([[ 6, 7],
[10, 11]])
>>> a[1:3, 1:3] += 10
>>> a
array([[ 1, 2, 3, 4],
[ 5, 16, 17, 8],
[ 9, 20, 21, 12]])
>>> a + [100, 101, 102, 103]
array([[101, 103, 105, 107],
[105, 117, 119, 111],
[109, 121, 123, 115]])
>>> np.where(a < 10, a, 10)
array([[ 1, 2, 3, 4],
[ 5, 10, 10, 8],
[ 9, 10, 10, 10]])

矩阵与线性代数运算

问题

你需要执行矩阵和线性代数运算。

解决方案

  • Numpy 库提供一个矩阵对象。矩阵类似于数组对象,但是遵循线性代数的计算规则
  • 可以在 Numpy.linalg 子包中找到更多的操作函数

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> import numpy as np
>>> m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
>>> m
matrix([[ 1, -2, 3],
[ 0, 4, 5],
[ 7, 8, -9]])
>>> m.T
matrix([[ 1, 0, 7],
[-2, 4, 8],
[ 3, 5, -9]])
>>> m.I
matrix([[ 0.33043478, -0.02608696, 0.09565217],
[-0.15217391, 0.13043478, 0.02173913],
[ 0.12173913, 0.09565217, -0.0173913 ]])
>>> v
matrix([[2],
[3],
[4]])
>>> m * v
matrix([[ 8],
[32],
[ 2]])
1
2
3
4
5
>>> import numpy.linalg
>>> numpy.linalg.det(m)
-230.00000000000006
>>> numpy.linalg.eigvals(m)
array([-13.11474312, 2.75956154, 6.35518158])

随机选择

问题

想从一个序列中随机抽取若干个元素,或者想生成几个随机数。

解决方案

  • random 模块有大量的函数来产生随机数和随机选择元素
  • 想要从一个序列中随机抽取一个元素,可以使用 random.choice()
  • 随机抽取 N 个不同元素,可以使用 random.sample()
  • 如果只是想打乱序列中元素的顺序,可以使用 random.shuffle()
  • 生成随机整数,可以使用 random.randint()
  • 生成 0 到 1 范围内均匀分布的浮点数,使用 random.random()
  • 想要获取 N 位随机位(二进制)的整数,使用 random.getrandbits()

random 模块使用确定性算法来生成随机数,但是可以通过 random.seed() 修改初始化种子。random 模块还包含基于均匀分布、高斯分布额其他分布的随机数生成函数。

random 模块中的函数不应该用于密码学相关的程序中,如果确实需要类似功能,可以使用 ssl 模块中相应的函数。例如 ssl.RAND_bytes() 可以用来生成一个安全的随机字节序列。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> import random
>>> values = [i for i in range(1, 6)]
>>> random.choice(values)
5
>>> random.choice(values)
1
>>> random.choice(values)
3
>>> random.choice(values)
2

>>> random.sample(values, 2)
[4, 1]
>>> random.sample(values, 3)
[1, 2, 4]

>>> values
[1, 2, 3, 4, 5]
>>> random.shuffle(values)
>>> values
[2, 3, 5, 4, 1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> random.randint(0, 10)
1
>>> random.randint(0, 10)
3
>>> random.randint(0, 10)
2
>>> random.randint(0, 10)
6

>>> random.random()
0.21909657607066169
>>> random.random()
0.907961868047516

>>> random.getrandbits(10)
778
>>> random.getrandbits(10)
85
>>> random.getrandbits(10)
648
1
2
>>> random.seed()
>>> random.seed(12345)

基本的日期与时间转换

问题

需要执行简单的日期与时间转换。

解决方案

  • 为了执行不同时间单位的转换和计算,可以使用 datetime 模块
  • 为了表示时间段,可以使用其 timedelta 实例
  • 想表示指定的日期和时间,可以使用 datetime 实例
  • datetime 模块会自动处理闰年
  • 如果需要处理复杂的日期操作,比如处理时区、模糊时间范围、节假日计算等,可以考虑使用 dateutil 模块

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> from datetime import timedelta
>>> a = timedelta(days=2, hours=6)
>>> b = timedelta(hours=4.5)
>>> c = a + b
>>> c
datetime.timedelta(days=2, seconds=37800)

>>> from datetime import datetime
>>> a = datetime(2012, 9, 23)
>>> print(a + timedelta(days=10))
2012-10-03 00:00:00
>>> b = datetime(2023, 9, 1)
>>> d = b - a
>>> d.days
3995
>>> now = datetime.today()
>>> print(now)
2023-01-03 09:43:44.032733
>>> print(now + timedelta(minutes=10))
2023-01-03 09:53:44.032733
1
2
3
4
5
6
7
8
>>> c = datetime(2012, 3, 1)
>>> d = datetime(2012, 2, 28)
>>> c - d
datetime.timedelta(days=2)
>>> c = datetime(2013, 3, 1)
>>> d = datetime(2013, 2, 28)
>>> c - d
datetime.timedelta(days=1)

计算上一个周五的日期

问题

需要一个通用方法来计算一周中某一天上一次出现的日期。

解决方案

  • 可以通过 datetime 模块中的工具函数和类实现该功能
  • 第三方包 python-dateutil 直接提供了相关功能

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from datetime import datetime, timedelta


weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']


def get_previous_day(dayname, start_date=None):
if start_date is None:
start_date = datetime.now()

day_num = start_date.weekday()
day_num_target = weekdays.index(dayname)
days_ago = (7 + day_num - day_num_target) % 7
if days_ago == 0:
days_ago = 7

target_date = start_date - timedelta(days=days_ago)
return target_date


print(get_previous_day('Monday'))
1
2
3
4
5
6
7
8
9
10
>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from dateutil.rrule import *
>>> d = datetime.now()
>>> d
datetime.datetime(2023, 1, 4, 8, 35, 51, 712326)
>>> print(d + relativedelta(weekday=FR))
2023-01-06 08:35:51.712326
>>> print(d + relativedelta(weekday=FR(-1)))
2022-12-30 08:35:51.712326

计算当前月份的日期范围

问题

想要获取当前月份的的所有日期。

解决方案

  • 可以首先计算出当前月份的开始日期和结束日期,然后使用 datetime.timedelta 对象递增该日期
  • 计算一个月份的第一天的日期,最简单的方法就是调用 datedatetime 对象的 replace() 方法将 days 属性设置为 1
  • 通过 calendar.monthrange() 可以计算某个月的天数
  • Python 中的日期和时间能够使用标准的数学和比较操作来进行运算

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from datetime import datetime, date, timedelta
import calendar


def get_month_range(start_date=None):
if start_date is None:
start_date = date.today().replace(day=1)

_, days_in_month = calendar.monthrange(start_date.year, start_date.month)
end_date = start_date + timedelta(days=days_in_month)
return (start_date, end_date)


def date_range(start, stop, step):
while start < stop:
yield start
start += step


a_day = timedelta(days=1)
first_day, last_day = get_month_range()
while first_day < last_day:
print(first_day)
first_day += a_day


first_day, last_day = get_month_range()
for i in date_range(first_day, last_day, timedelta(days=2)):
print(i)

字符串转换为日期

问题

需要将字符串转换为 datetime 对象,以方便执行日期时间计算操作。

解决方案

  • datetime.strptime() 方法支持将字符串转换为 datetime 对象,其支持多种格式化代码
  • datetime.strftime() 方法可以将 datetime 对象转换为字符串
  • strptime() 的性能较差,有时候也可以自己实现解析方案,以获取更好的性能

示例

1
2
3
4
5
6
7
>>> from datetime import datetime
>>> text='2012-09-20'
>>> y = datetime.strptime(text, '%Y-%m-%d')
>>> z = datetime.now()
>>> diff = z - y
>>> diff
datetime.timedelta(days=3758, seconds=32792, microseconds=744489)
1
2
3
>>> nice_z = datetime.strftime(z, '%A %B %d, %Y')
>>> nice_z
'Wednesday January 04, 2023'
1
2
3
4
5
>>> from datetime import datetime
>>> def parse_ymd(s):
... y, m, d = s.split('-')
... return datetime(int(y), int(m), int(d))
...

结合时区的日期操作

问题

在执行日期操作时,需要结合时区信息。

解决方案

  • 几乎所有涉及到时区的问题,都应该使用 pytz 模块。它提供了 Olson 时区数据库,也是时区信息事实上的标准
  • pytz 模块主要用途是将 datetime 库创建的简单日期对象本地化。一旦日期被本地化了之后,就可以转换为其他时区时间
  • 处理本地化日期的通常策略是先将所有日期转换为 UTC 时间,并用它来执行所有中间存储和操作。一旦转换为 UTC,就不用担心和夏令时相关的问题了。
  • 在处理时区时,可以使用 ISO 3166国家代码 作为关键字去查阅字典 pytz.country_timezones 获取时区信息

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> from datetime import datetime
>>> from pytz import timezone
>>> d = datetime(2023, 1, 4, 9, 10, 0)
>>> print(d)
2023-01-04 09:10:00

>>> cn = timezone('Asia/Shanghai')
>>> local_d = cn.localize(d)
>>> print(local_d)
2023-01-04 09:10:00+08:00

>>> us_d = local_d.astimezone(timezone('US/Central'))
>>> print(us_d)
2023-01-03 19:10:00-06:00
1
2
3
4
5
6
7
8
9
10
>>> print(local_d)
2023-01-04 09:10:00+08:00
>>> utc_d = local_d.astimezone(pytz.utc)
>>> print(utc_d)
2023-01-04 01:10:00+00:00

>>> from datetime import timedelta
>>> later_utc = utc_d + timedelta(minutes=30)
>>> print(later_utc)
2023-01-04 01:40:00+00:00
1
2
>>> pytz.country_timezones('CN')
['Asia/Shanghai', 'Asia/Urumqi']