乐趣区

关于程序员:39个-Python-Datetime-小例子拯救因时间抓狂的你

咱们都晓得,在 Python 中有各种数据类型,例如整数、浮点数、字符串等。同时在开发脚本或各种算法当中,咱们应该常常会应用日期和工夫。在日常生活中,咱们能够用多种不同的格局来示意日期和工夫,例如,7 月 4 日、2022 年 3 月 8 日、22:00 或 2022 年 12 月 31 日 23:59:59。它们应用整数和字符串的组合,或者也能够应用浮点数来示意一天、一分钟等等,各种各样的工夫示意形式,的确让人目迷五色。

不过还好,Python 有 datetime 模块,它容许咱们轻松地操作示意日期和工夫的对象。

在明天的文章中,咱们将学习以下内容:

  • Python 中 datetime 模块的应用
  • 应用 Python 日期工夫函数将字符串转换为日期工夫对象,反之亦然
  • 从日期工夫对象中提取日期和工夫
  • 应用工夫戳
  • 对日期和工夫执行算术运算
  • 应用时区
  • 创立一个倒数计时器来确定间隔 2023 年新年还有多长时间

Let’s do it!

如何在 Python 中应用日期工夫

正如咱们之前所看到的,在编程中示意日期和工夫是一项十分有挑战的事件。首先,咱们必须以规范的、广泛承受的格局来示意它们。侥幸的是,国际标准化组织 (ISO) 制订了一个寰球规范 ISO 8601,它将与日期和工夫相干的对象示意为 YYYY-MM-DD HH:MM:SS,其信息范畴从最重要的(年,YYYY)到 最不重要的(秒,SS)。这种格局的每一部分都示意为一个四位数或两位数。

Python 中的 datetime 模块有 5 个次要类(模块的一部分):

  • date 操作日期对象
  • time 操作工夫对象
  • datetime 是日期和工夫的组合
  • timedelta 容许咱们应用工夫区间
  • tzinfo 容许咱们应用时区

此外,咱们将应用 zoneinfo 模块,它为咱们提供了一种解决时区的更加古代的形式,以及 dateutil 包,它蕴含许多有用的函数来解决日期和工夫。

让咱们导入 datetime 模块并创立咱们的第一个日期和工夫对象:

# From the datetime module import date
from datetime import date

# Create a date object of 2000-02-03
date(2022, 2, 3)

Output:

datetime.date(2022, 2, 3)

在下面的代码中,咱们从模块中导入了日期类,而后创立了 2022 年 2 月 3 日的 datetime.date 对象。须要留神的是,用于创立该对象的数字程序与 ISO 8061 中的完全相同(但咱们省略了 0 并且只写了一个数字的月份和日期)。

对于工夫的编码都是基于事实的,所以假如咱们要创立一个 2000-26-03 的对象:

# Create a date object of 2000-26-03
date(2000, 26, 3)

Output:

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Input In [2], in 
      1 # Create a date object of 2000-26-03
----> 2 date(2000, 26, 3)

ValueError: month must be in 1..12

咱们失去 ValueError: month must be in 1..12,毫无疑问,日历中没有第 26 个月,抛出异样。

让咱们看看如何创立一个 datetime.time 对象:

# From the datetime module import time
from datetime import time

# Create a time object of 05:35:02
time(5, 35, 2)

Output:

datetime.time(5, 35, 2)

当初,如果咱们想要在一个对象中同时蕴含日期和工夫怎么办?咱们应该应用 datetime 类:

# From the datetime module import datetime
from datetime import datetime

# Create a datetime object of 2000-02-03 05:35:02
datetime(2000, 2, 3, 5, 35, 2)

Output:

datetime.datetime(2000, 2, 3, 5, 35, 2)

不出意外,咱们胜利创立了 datetime 对象。咱们还能够更明确地将关键字参数传递给 datetime 构造函数:

datetime(year=2000, month=2, day=3, hour=5, minute=35, second=2)

Output:

datetime.datetime(2000, 2, 3, 5, 35, 2)

如果咱们只传入三个参数(年、月和日)会怎么,是否会报错呢

# Create a datetime object of 2000-02-03
datetime(2000, 2, 3)

Output:

datetime.datetime(2000, 2, 3, 0, 0)

咱们能够看到,当初对象中有两个零(别离代表)小时和分钟。同时秒数也被省略了。

在许多状况下,咱们想晓得以后的确切工夫。能够应用 datetime 类的 now() 办法:

# Time at the moment
now = datetime.now()
now

Output:

datetime.datetime(2022, 8, 1, 0, 9, 39, 611254)

咱们失去一个日期工夫对象,这里最初一个数字是微秒。

如果咱们只须要明天的日期,咱们能够应用 date 类的 today() 办法:

today = date.today()
today

Output:

datetime.date(2022, 8, 1)

如果咱们只须要工夫,就必须拜访 datetime.now() 对象的小时、分钟和秒属性,并将它们传递给工夫构造函数:

time(now.hour, now.minute, now.second)

Output:

datetime.time(11, 33, 25)

咱们还能够应用 isocalendar() 函数从日期工夫对象中提取周数和天数。它将返回一个蕴含 ISO 年份、周数和工作日数的三项元组:

# isocalendar() returns a 3-item tuple with ISO year, week number, and weekday number
now.isocalendar()

Output:

datetime.IsoCalendarDate(year=2022, week=7, weekday=1)

在 ISO 格局中,一周从星期一开始,到星期日完结。一周中的天数由从 1(星期一)到 7(星期日)的数字编码。如果咱们想拜访这些元组元素之一,须要应用括号表示法:

# Access week number
week_number = now.isocalendar()[1]
week_number

Output:

7

从字符串中提取日期

在数据迷信和个别编程中,咱们次要应用以数十种不同格局存储为字符串的日期和工夫,具体取决于地区、公司或咱们须要的信息粒度。有时,咱们须要日期和确切工夫,但在其余状况下,咱们只须要年份和月份。咱们该如何从字符串中提取咱们须要的数据,以便将其作为日期工夫(日期、工夫)对象来操作呢?

fromisoformat() 和 isoformat()

咱们学习的第一个将日期字符串转换为日期对象的函数是 fromisoformat,咱们这样称说它是因为它应用 ISO 8601 格局(即 YYYY-MM-DD),让咱们看一个例子:

# Convert a date string into a date object
date.fromisoformat("2022-12-31")

Output:

datetime.date(2022, 12, 31)

ISO 格局也蕴含工夫,但如果咱们却不能将它传递给函数:

date.fromisoformat("2022-12-31 00:00:00")

Output:

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Input In [13], in 
----> 1 date.fromisoformat("2022-12-31 00:00:00")

ValueError: Invalid isoformat string: '2022-12-31 00:00:00'

当然,咱们也能够进行逆向运算,将 datetime 对象转换为 ISO 格局的日期字符串,咱们应该应用 isoformat()

# Convert a datetime object into a string in the ISO format
date(2022, 12, 31).isoformat()

Output:

'2022-12-31'

strptime()

为了解决上述 ValueError 问题,咱们能够应用 strptime() 函数,该函数能够将任意日期 / 工夫字符串转换为日期工夫对象。咱们的字符串不肯定须要遵循 ISO 格局,但咱们应该指定字符串的哪一部分代表哪个日期或工夫单位(年、小时等)。让咱们看一个例子,首先,咱们将应用严格的 ISO 格局将字符串转换为日期工夫对象:

# Date as a string
iso_date = "2022-12-31 23:59:58"

# ISO format
iso_format = "%Y-%m-%d %H:%M:%S"

# Convert the string into a datetime object
datetime.strptime(iso_date, iso_format)

Output:

datetime.datetime(2022, 12, 31, 23, 59, 58)

在第一行,咱们创立一个日期 / 工夫字符串。在第二行中,咱们应用非凡代码指定字符串的格局,该代码蕴含一个百分号,后跟一个编码日期或工夫单位的字符。最初,在第三行中,咱们应用 strptime() 函数将字符串转换为日期工夫对象。这个函数有两个参数:字符串和字符串的格局。

咱们下面应用的代码还能够编码其余日期和工夫单位,如工作日、月份名称、周数等。

代码 示例 阐明
%A Monday 残缺的工作日名称
%B December 全月名称
%W 2 周数(星期一为一周的第一天)

让咱们再看几个应用其余格局的示例:

# European date as a string
european_date = "31-12-2022"

# European format
european_format = "%d-%m-%Y"

# Convert the string into a datetime object
datetime.strptime(european_date, european_format)

Output:

datetime.datetime(2022, 12, 31, 0, 0)

如上所示,字符串已胜利转换,但还有额定的零示意工夫字段,让咱们看一个应用其余代码的示例:

# Full month name date
full_month_date = "12 September 2022"

# Full month format
full_month_format = "%d %B %Y"

# Convert the string into a datetime object
datetime.strptime(full_month_date, full_month_format)

Output:

datetime.datetime(2022, 9, 12, 0, 0)

还是能够失常转换,然而须要留神的是,咱们定义的格局应该与日期字符串的格局相匹配。因而,如果咱们有空格、冒号、连字符或其余字符来分隔工夫单位,那么它们也应该在代码字符串中。否则,Python 将抛出 ValueError:

# Full month name date
full_month_date = "12 September 2022"

# Wrong format (missing space)
full_month_format = "%d%B %Y"

# Convert the string into a datetime object
datetime.strptime(full_month_date, full_month_format)

Output:

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Input In [18], in 
      5 full_month_format = "%d%B %Y"
      7 # Convert the string into a datetime object
----> 8 datetime.strptime(full_month_date, full_month_format)

File ~/coding/dataquest/articles/using-the-datetime-package/env/lib/python3.10/_strptime.py:568, in _strptime_datetime(cls, data_string, format)
    565 def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
    566     """Return a class cls instance based on the input string and the
    567     format string."""
--> 568     tt, fraction, gmtoff_fraction = _strptime(data_string, format)
    569     tzname, gmtoff = tt[-2:]
    570     args = tt[:6] + (fraction,)

File ~/coding/dataquest/articles/using-the-datetime-package/env/lib/python3.10/_strptime.py:349, in _strptime(data_string, format)
    347 found = format_regex.match(data_string)
    348 if not found:
--> 349     raise ValueError("time data %r does not match format %r" %
    350                      (data_string, format))
    351 if len(data_string) != found.end():
    352     raise ValueError("unconverted data remains: %s" %
    353                       data_string[found.end():])

ValueError: time data '12 September 2022' does not match format '%d%B %Y'

能够看到,即便短少一个空格也可能导致谬误!

将日期工夫对象转换为字符串

strftime()

在 Python 中,咱们还能够应用 strftime() 函数将日期工夫对象转换为字符串。它有两个参数:一个日期工夫对象和输入字符串的格局。

# Create a datetime object
datetime_obj = datetime(2022, 12, 31)

# American date format
american_format = "%m-%d-%Y"

# European format
european_format = "%d-%m-%Y"

# American date string
print(f"American date string: {datetime.strftime(datetime_obj, american_format)}.")

# European date string
print(f"European date string: {datetime.strftime(datetime_obj, european_format)}.")

Output:

American date string: 12-31-2022.
European date string: 31-12-2022.

咱们采纳雷同的日期工夫对象并将其转换为两种不同的格局。咱们还能够指定其余格局,例如残缺的月份名称后跟日期和年份。

full_month = "%B %d, %Y"
datetime.strftime(datetime_obj, full_month)

Output:

'December 31, 2022'

另一种应用 strftime 的办法是将它放在 datetime 对象之后:

datetime_obj = datetime(2022, 12, 31, 23, 59, 59)
full_datetime = "%B %d, %Y %H:%M:%S"
datetime_obj.strftime(full_datetime)

Output:

'December 31, 2022 23:59:59'

在理论应用当中,如果咱们想提取不同年份 12 月 31 日的工作日名称,strftime() 可能很不便:

# Extract the weekday name of December 31
weekday_format = "%A"

for year in range(2022, 2026):
    print(f"Weekday of December 31, {year} is {date(year, 12, 31).strftime(weekday_format)}.")

Output:

Weekday of December 31, 2022 is Saturday.
Weekday of December 31, 2023 is Sunday.
Weekday of December 31, 2024 is Tuesday.
Weekday of December 31, 2025 is Wednesday.

工夫戳

在编程中,通常会看到以 Unix 工夫戳格局存储的日期和工夫,这种格局将任何日期示意为数字。个别状况工夫戳是从 1970 年 1 月 1 日 00:00:00 UTC(协调世界时)开始的 Unix 纪元通过的秒数。咱们能够应用 timestamp() 函数计算这个数字:

new_year_2023 = datetime(2022, 12, 31)
datetime.timestamp(new_year_2023)

Output:

1672441200.0

1672441200 就是从 Unix 纪元开始到 2022 年 12 月 31 日之间的秒数。

咱们能够应用 fromtimestamp() 函数执行逆运算:

datetime.fromtimestamp(1672441200)

Output:

datetime.datetime(2022, 12, 31, 0, 0)

带日期的算术运算

有时咱们可能想要计算两个日期之间的差别或对日期和工夫执行其余算术运算。侥幸的是,Python 的工具包中有许多工具能够执行此类计算。

根本算术运算

咱们能够执行的第一个操作是计算两个日期之间的差别。为此,咱们应用减号:

# Instatiate two dates
first_date = date(2022, 1, 1)
second_date = date(2022, 12, 31)

# Difference between two dates
date_diff = second_date - first_date

# Function to convert datetime to string
def dt_string(date, date_format="%B %d, %Y"):
    return date.strftime(date_format)

print(f"The number of days and hours between {dt_string(first_date)} and {dt_string(second_date)} is {date_diff}.")

Output:

The number of days and hours between January 01, 2022 and December 31, 2022 is 364 days, 0:00:00

让咱们看看 first_date – second_date 返回什么类型的对象:

type(date_diff)

Output:

datetime.timedelta

此对象的类型是 datetime.timedelta,它的名称中有 delta,指的是一个绿色字母 delta,在迷信和工程中,形容了一种变动 < 实际上,这里它代表了工夫上的变动(差别)。

如果咱们只对两个日期之间的天数感兴趣怎么办?咱们能够拜访 timedelta 对象的不同属性,其中之一称为 .days

print(f"The number of days between {dt_string(first_date)} and {dt_string(second_date)} is {(date_diff).days}.")

Output:

The number of days between January 01, 2022 and December 31, 2022 is 364.

timedelta() 工夫增量

当初咱们晓得了 timedelta 对象,是时候介绍 timedelta() 函数了。它容许咱们通过加减工夫单位(如天、年、周、秒等)对工夫对象执行许多算术运算。例如,咱们可能想晓得从现在起 30 天后是一周中的哪一天。为此,咱们必须创立一个示意以后工夫的对象和一个定义咱们增加到其中的工夫量的 timedelta 对象:

# Import timedelta
from datetime import timedelta

# Current time
now = datetime.now()

# timedelta of 30 days
one_month = timedelta(days=30)

# Day in one month/using dt_string function defined above
print(f"The day in 30 days is {dt_string(now + one_month)}.")

Output:

The day in 30 days is March 16, 2022.

如果咱们查看 timedelta 函数的帮忙页面 (help(timedelta)),咱们会看到它有以下参数:days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours= 0,周 =0。因而咱们还能够练习在日期中增加或减去其余工夫单位。例如,咱们能够计算 2030 年新年前 12 小时的工夫:

# New year 2030
new_year_2030 = datetime(2030, 1, 1, 0, 0)

# timedelta of 12 hours
twelve_hours = timedelta(hours=12)

# Time string of 12 hours before New Year 2023
twelve_hours_before = (new_year_2030 - twelve_hours).strftime("%B %d, %Y, %H:%M:%S")

# Print the time 12 hours before New Year 2023
print(f"The time twelve hours before New Year 2030 will be {twelve_hours_before}.")

Output:

The time twelve hours before New Year 2030 will be December 31, 2029, 12:00:00.

咱们还能够组合 timedelta() 函数的多个参数来计算出更具体的工夫。例如,从现在起 27 天 3 小时 45 分钟后的工夫是多少?

# Current time
now = datetime.now()

# Timedelta of 27 days, 3 hours, and 45 minutes
specific_timedelta = timedelta(days=27, hours=3, minutes=45)

# Time in 27 days, 3 hours, and 45 minutes
twenty_seven_days = (now + specific_timedelta).strftime("%B %d, %Y, %H:%M:%S")

print(f"The time in 27 days, 3 hours, and 45 minutes will be {twenty_seven_days}.")

Output:

The time in 27 days, 3 hours, and 45 minutes will be March 13, 2022, 15:18:39.

relativedelta() 绝对增量

咱们能够从帮忙页面中看到该性能不容许咱们应用几个月或几年。为了克服这个限度,咱们能够应用 dateutil 包中的 relativedelta 函数。此函数与 timedelta() 十分类似,但它扩大了更多的性能。

例如,咱们想从以后工夫中减去 2 年 3 个月 4 天 5 小时:

# Import relativedelta
from dateutil.relativedelta import relativedelta

# Current time
now = datetime.now()

# relativedelta object
relative_delta = relativedelta(years=2, months=3, days=4, hours=5)

two_years = (now - relative_delta).strftime("%B %d, %Y, %H:%M:%S")

print(f"The time 2 years, 3 months, 4 days, and 5 hours ago was {two_years}.")

Output:

The time 2 years, 3 months, 4 days, and 5 hours ago was November 10, 2019, 06:33:40.

咱们还能够应用 relativedelta() 来计算两个日期工夫对象之间的差别:

relativedelta(datetime(2030, 12, 31), now)

Output:

relativedelta(years=+8, months=+10, days=+16, hours=+12, minutes=+26, seconds=+19, microseconds=+728345)

这些算术运算可能看起来十分形象和不切实际,但实际上,它们在许多应用程序中都很有用。

比如说,咱们脚本中的某个操作应该只在特定日期前 30 天执行。咱们能够定义一个保留以后工夫的变量,并为其增加一个 30 天的 timedelta 对象,如果明天是这一天,就会触发相干操作!

还有,假如咱们正在应用 pandas 解决数据集,其中一列蕴含一些日期。设想一下,咱们有一个数据集,其中保留着咱们公司一年中的每一天的利润。咱们想要创立另一个数据集,该数据集将保留距以后日期正好一年的日期,并预测每一天的利润,此时咱们肯定会在日期上应用算术计算!

应用时区

上面咱们来看一看时区,它们能够有不同的模式。咱们还应该晓得,一些地区施行夏令时 (DST),而另一些地区则没有。

Python 辨别两种类型的日期和工夫对象:naiveaware。一个 naive 对象不保留任何无关时区的信息,而 aware 对象则保留了它。

首先,让咱们看一个 naive 工夫对象:

# Import tzinfo
from datetime import tzinfo

# Naive datetime
naive_datetime = datetime.now()

# Naive datetime doesn't hold any timezone information
type(naive_datetime.tzinfo)

Output:

NoneType

从 Python 3.9 开始,应用 Internet Assigned Numbers Authority 数据库实现了时区的具体实现,实现此性能的模块称为 zoneinfo

让咱们应用 zoneinfo,特地是 ZoneInfo 类创立一个感知日期工夫对象,它是 datetime.tzinfo 抽象类的一个实现:

# Import ZoneInfo
from zoneinfo import ZoneInfo

utc_tz = ZoneInfo("UTC")

# Aware datetime object with UTC timezone
dt_utc = datetime.now(tz=utc_tz)

# The type of an aware object implemented with ZoneInfo is zoneinfo.ZoneInfo
type(dt_utc.tzinfo)

Output:

zoneinfo.ZoneInfo

对于领有 awaredatetime 对象具备无关时区的信息(实现为 zoneinfo.ZoneInfo 对象)。

让咱们看几个例子,咱们想确定中欧和加利福尼亚的以后工夫。

首先,咱们能够在 `zoneinfo 中列出所有可用的时区:

import zoneinfo

# Will return a long list of timezones (opens many files!)
zoneinfo.available_timezones()

Output:

{'Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 'Africa/Dakar',
 'Africa/Dar_es_Salaam',
 'Africa/Djibouti',
 'Africa/Douala',
 ...
 'build/etc/localtime'}

当初咱们能够应用 ZoneInfo 来确定不同区域的以后工夫:

# Function to convert datetime into ISO formatted time
def iso(time, time_format="%Y-%m-%d %H:%M:%S"):
    return time.strftime(time_format)

# CET time zone
cet_tz = ZoneInfo("Europe/Paris")

# PST time zone
pst_tz = ZoneInfo("America/Los_Angeles")

# Current time in Central Europe
dt_cet = datetime.now(tz=cet_tz)

# Current time in California
dt_pst = datetime.now(tz=pst_tz)

print(f"Current time in Central Europe is {iso(dt_cet)}.")
print(f"Current time in California is {iso(dt_pst)}.")

Output:

Current time in Central Europe is 2022-02-14 11:33:42.
Current time in California is 2022-02-14 02:33:42.

让咱们打印 datetime.now(tz=cet_tz)

print(datetime.now(tz=cet_tz))

Output:

2022-02-14 11:33:43.048967+01:00

咱们看到有 +01:00,它示意 UTC 偏移量。事实上,CET 时区比 UTC 早一小时。

此外,ZoneInfo 类解决夏令时。例如,咱们能够将一天(24 小时)增加到 DST 更改产生的一天。

# Define timedelta
time_delta = timedelta(days=1)

# November 5, 2022, 3 PM
november_5_2022 = datetime(2022, 11, 5, 15, tzinfo=pst_tz) # Note that we should use tzinfo in the datetime construct
print(november_5_2022)

# Add 1 day to November 5, 2022
print(november_5_2022 + time_delta)

Output:

2022-11-05 15:00:00-07:00
2022-11-06 15:00:00-08:00

正如咱们所见,偏移量从 -07:00 变为 -08:00,但工夫放弃不变(15:00)。

2023 年新年倒数计时器

New Your City 的时代广场在新年前夜吸引了成千上万的人。让咱们利用到目前为止所学的所有来为时代广场元旦创立一个倒数计时器!

在这里,咱们将应用 dateutil 包中的 tz,它容许咱们设置本地时区来演示 dateutil 包的实用程序。然而咱们也能够应用 zoneinfo 中的 build/etc/localtime 时区来做同样的事件。

from zoneinfo import ZoneInfo
from dateutil import tz
from datetime import datetime

def main():
    """Main function."""
    # Set current time in our local time zone
    now = datetime.now(tz=tz.tzlocal())

    # New York City time zone
    nyc_tz = ZoneInfo("America/New_York")

    # New Year 2023 in NYC
    new_year_2023 = datetime(2023, 1, 1, 0, 0, tzinfo=nyc_tz)

    # Compute the time left to New Year in NYC
    countdown = relativedelta(new_year_2023, now)

    # Print time left to New Year 2023
    print(f"New Year in New Your City will come on: {new_year_2023.strftime('%B %-d, %Y %H:%M:%S')}.")
    print(f"Time left to New Year 2023 in NYC is: {countdown.months} months, {countdown.days} days, {countdown.hours} hours, {countdown.minutes} minutes, {countdown.seconds} seconds.")

if __name__ == "__main__":
    main()

Output:

New Year in New Your City will come on: January 1, 2023 00:00:00.
Time left to New Year 2023 in NYC is: 10 months, 17 days, 18 hours, 26 minutes, 16 seconds.

咱们将代码包装在 main() 函数中,当初咱们能够在 .py 文件中应用它。在这个脚本中,咱们解决了时区,创立了一个 datetime 对象,应用 strftime() 将其转换为字符串,甚至拜访了 relativedelta 对象的工夫属性!

好了,这就是明天分享的全部内容,喜爱就点个赞吧~

本文由 mdnice 多平台公布

退出移动版