在sql及Python数据分析过程中,经常需要对时间进行转换和处理,对此进行了总结。

sql

implala

格式转换

flowchart TD a[string] b[timestamp] c[unix_timestamp] a -- to_timestamp(string dt, string pattern) --> b b -- from_timestamp(timestamp t, string pattern) --> a a -- unix_timestamp(string dt, string pattern) --> c c -- from_unixtime(bigint unixtime, string pattern) --> a b -- unix_timestamp(timestamp t) --> c c -- to_timestamp(bigint unixtime) --> b

hive

格式转换

取消timestamp节点后,可复用impala架构。

oracle

格式转换

flowchart TD a[string] b[timestamp] a -- to_date(string dt, string pattern) --> b b -- to_char(timestamp t, string pattern) --> a

Python

基于datetime模块

格式转换

[In] [1]: from datetime import datetime, date, timedelta
[In] [2]: raw_str = '2023-01-14 00:00:00'

从文本转为时间  
[In] [3]: ts = datetime.strptime(raw_str, '%Y-%m-%d %H:%M:%S')
[In] [4]: ts
[Out][4]: datetime.datetime(2023, 1, 14, 0, 0)

从时间转为文本  
[In] [5]: ts_str = ts.strftime('%Y-%m')
[In] [6]: ts_str
[Out][6]: '2023-01'

数据运算

简单的天和小时数间隔  
[In] [7]: t2 = ts + timedelta(days=-5, hours=6)
[In] [8]: t2
[Out][8]: datetime.datetime(2023, 1, 9, 6, 0)

通过dateutil.relativedelta增加对年月间隔的支持  
[In] [9]: from dateutil.relativedelta import relativedelta
[In] [10]: t3 = t2 + relativedelta(months=3, years=5)
[In] [11]: t3
[Out][11]: datetime.datetime(2028, 4, 9, 6, 0)

日期构建  
[In] [12]: t4 = date(t3.year, t3.month, t3.day)
[In] [13]: t4
[Out][13]: datetime.date(2028, 4, 9)
[In] [14]: t5 = t4.replace(day=10)
[In] [15]: t5
[Out][15]: datetime.date(2028, 4, 10)

时间差
[In] [16]: delta = t3 - t2
[In] [17]: delta
[Out][17]: datetime.timedelta(days=1917)

基于pandas包

格式转换

[In] [1]: import pandas as pd
[In] [2]: df = pd.
[In] [3]: df = pd.DataFrame({'raw_str': [
                   '2023-01-14 00:00:00',
                   '2023-01-21 00:00:00',
                   '2023-02-11 00:00:00',
                   '2023-02-18 00:00:00']})
[Out][3]: 
| raw_str             |
|:-------------------:|
| 2023-01-14 00:00:00 |
| 2023-01-21 00:00:00 |
| 2023-02-11 00:00:00 |
| 2023-02-18 00:00:00 |

从文本转为日期时间  
[In] [4]: df['ts'] = pd.to_datetime(df['raw_str'], format='%Y-%m-%d %H:%M:%S')
[In] [5]: df
[Out][5]: 
| raw_str             | ts                  |
|:--------------------|:--------------------|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 |

从日期时间转为文本  
[In] [6]: df['str_month'] = df['ts'].dt.strftime('%Y-%m')
[In] [7]: df
[Out][7]: 
| raw_str             | ts                  | str_month   |
|:--------------------|:--------------------|:------------|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 | 2023-01     |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 | 2023-01     |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 | 2023-02     |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 | 2023-02     |

可直接获取年月日季度等数值  
[In] [8]: df['quarter'] = df['ts'].dt.quarter
[In] [9]: df
[Out][9]:
| raw_str             | ts                  | str_month   |   quarter |
|:--------------------|:--------------------|:------------|----------:|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 | 2023-01     |         1 |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 | 2023-01     |         1 |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 | 2023-02     |         1 |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 | 2023-02     |         1 |

指定年月日等参数  
[In] [10]: df['set_day_1'] = df['ts'].transform(lambda x: x.replace(day=1))
[In] [11]: df
[Out][11]: 
| raw_str             | ts                  | set_day_1           |
|:--------------------|:--------------------|:--------------------|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 | 2023-01-01 00:00:00 |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 | 2023-01-01 00:00:00 |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 | 2023-02-01 00:00:00 |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 | 2023-02-01 00:00:00 |

数据运算

简单的天和小时数间隔  
[In] [12]: df['+30days'] = df['ts'] + pd.Timedelta(days=30)
[In] [13]: df
[Out][13]: 
| raw_str             | ts                  | +30days             |
|:--------------------|:--------------------|:--------------------|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 | 2023-02-13 00:00:00 |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 | 2023-02-20 00:00:00 |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 | 2023-03-13 00:00:00 |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 | 2023-03-20 00:00:00 |

通过DateOffset增加对年月间隔的支持  
[In] [14]: from pandas.tseries.offsets import DateOffset
[In] [15]: df['months_years'] = df['ts'] + DateOffset(years=2, months=-3)
[Out][15]: 
| raw_str             | ts                  | months_years        |
|:--------------------|:--------------------|:--------------------|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 | 2024-10-14 00:00:00 |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 | 2024-10-21 00:00:00 |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 | 2024-11-11 00:00:00 |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 | 2024-11-18 00:00:00 |

时间差
[In] [16]: df['diff'] = df['months_years'] - df['ts']
[In] [17]: df
[Out][17]:
| raw_str             | ts                  | months_years        | diff              |
|:--------------------|:--------------------|:--------------------|:------------------|
| 2023-01-14 00:00:00 | 2023-01-14 00:00:00 | 2024-10-14 00:00:00 | 639 days 00:00:00 |
| 2023-01-21 00:00:00 | 2023-01-21 00:00:00 | 2024-10-21 00:00:00 | 639 days 00:00:00 |
| 2023-02-11 00:00:00 | 2023-02-11 00:00:00 | 2024-11-11 00:00:00 | 639 days 00:00:00 |
| 2023-02-18 00:00:00 | 2023-02-18 00:00:00 | 2024-11-18 00:00:00 | 639 days 00:00:00 |
[In] [18]: df['diff'].dt.days
[Out][18]: 
0    639
1    639
2    639
3    639
Name: diff, dtype: int64

分析和总结

  1. 数据库的时间处理方法,可视化后,基本都是一些范式;
  2. Python中datetime和pandas,类和方法基本是对应的。

附表

附表1:Python时间代码

指令 含意 示例 备注
%a 当地工作日的缩写。 Sun, Mon, …, Sat (en_US); So, Mo, …, Sa (de_DE) (1)
%A 本地化的星期中每日的完整名称。 Sunday, Monday, …, Saturday (en_US); Sonntag, Montag, …, Samstag (de_DE) (1)
%w 以十进制数显示的工作日,其中0表示星期日,6表示星期六。 0, 1, …, 6 nan
%d 补零后,以十进制数显示的月份中的一天。 01, 02, …, 31 (9)
%b 当地月份的缩写。 Jan, Feb, …, Dec (en_US); Jan, Feb, …, Dez (de_DE) (1)
%B 本地化的月份全名。 January, February, …, December (en_US); Januar, Februar, …, Dezember (de_DE) (1)
%m 补零后,以十进制数显示的月份。 01, 02, …, 12 (9)
%y 补零后,以十进制数表示的,不带世纪的年份。 00, 01, …, 99 (9)
%Y 十进制数表示的带世纪的年份。 0001, 0002, …, 2013, 2014, …, 9998, 9999 (2)
%H 以补零后的十进制数表示的小时(24 小时制)。 00, 01, …, 23 (9)
%I 以补零后的十进制数表示的小时(12 小时制)。 01, 02, …, 12 (9)
%p 本地化的 AM 或 PM 。 AM, PM (en_US); am, pm (de_DE) (1), (3)
%M 补零后,以十进制数显示的分钟。 00, 01, …, 59 (9)
%S 补零后,以十进制数显示的秒。 00, 01, …, 59 (4), (9)
%f 微秒作为一个十进制数,零填充到 6 位。 000000, 000001, …, 999999 (5)
%z UTC 偏移量,格式为 ±HHMM[SS[.ffffff]] (如果是简单型对象则为空字符串)。 (空), +0000, -0400, +1030, +063415, -030712.345216 (6)
%Z 时区名称(如果对象为简单型则为空字符串)。 (空), UTC, GMT (6)
%j 以补零后的十进制数表示的一年中的日序号。 001, 002, …, 366 (9)
%U 以补零后的十进制数表示的一年中的周序号(星期日作为每周的第一天)。 在新的一年中第一个星期日之前的所有日子都被视为是在第 0 周。 00, 01, …, 53 (7), (9)
%W 以补零后的十进制数表示的一年中的周序号(星期一作为每周的第一天)。 在新的一年中第一个星期一之前的所有日子都被视为是在第 0 周。 00, 01, …, 53 (7), (9)
%c 本地化的适当日期和时间表示。 Tue Aug 16 21:30:00 1988 (en_US); Di 16 Aug 21:30:00 1988 (de_DE) (1)
%x 本地化的适当日期表示。 08/16/88 (None); 08/16/1988 (en_US); 16.08.1988 (de_DE) (1)
%X 本地化的适当时间表示。 21:30:00 (en_US); 21:30:00 (de_DE) (1)
%% 字面的 ‘%’ 字符。 % nan

版本记录

2024-01-27,初稿
2024-02-20,增加Python部分