Clickhouse SQL日期处理函数及案例分享
适用场景
高性能数据集,以及Clickhouse直连数据集。
注意事项
- 
所有涉及到时分秒(DateTime字段)的函数都可能需要时区参数 timezone,时区格式一般为UTC时区或地理位置的IANA标识符(例如 Europe/Moscow),未指定时区则默认转换为服务器的时区(例如世界标准时间UTC)。
 - 
部分函数大小写敏感:大小写混写的函数为Clickhouse专有函数,必须原样使用(例如 toYear);全部为大写或小写的函数为兼容其他数据库的函数,大小写不敏感,即大小写都行(例如 YEAR)。
 - 
以下函数非全部可用函数,是基于BI当前所用v22版本汇编而成的常用函数。更多函数请参考Clickhouse官方网站Dates and Times。
 
日期支持函数
时间或日期截取函数
A) 原字段为日期(date)或日期时间(datetime/timestamp), 返回非日期。
用途  | 函数  | 举例  | 结果  | 
取年份  | toYear() / toISOYear() /YEAR()  | toYear('2018-12-11 11:12:13')  | 2018  | 
取季度数  | toQuarter() / QUARTER()  | toQuarter('2018-12-11 11:12:13')  | 4  | 
取月份  | toMonth() / MONTH()  | toMonth('2018-12-11 11:12:13')  | 12  | 
monthName(date)  | monthName('2018-12-11 11:12:13')  | December  | |
取当月内的天数(1-31)  | toDayOfMonth() / DAYOFMONTH() / DAY()  | toDayOfMonth('2019-12-03')  | 3  | 
取当年内的天数(1-365)  | toDayOfYear() / DAYOFYEAR()  | toDayOfYear('2019-12-03')  | 337  | 
取星期  | toDayOfWeek()/DAYOFWEEK() 周一是1, 周日是7  | toDayOfWeek('2019-12-03')  | 2  | 
取小时  | toHour() / HOUR()  | toHour('2018-12-11 11:12:13')  | 11  | 
取分钟  | toMinute() / MINUTE()  | toMinute('2018-12-11 11:12:13')  | 12  | 
取秒  | toSecond() / SECOND()  | toSecond('2018-12-11 11:12:13')  | 13  | 
取ISO周数 (周一起始,第一周需包含当年天数>3)  | toISOWeek() / toWeek(date,3)  | toISOWeek('2019-12-03')  | 49  | 
取时分秒  | formatDateTime(Time, Format)  | formatDateTime('2018-12-11 11:12:13','%T')  | 11:12:13  | 
取日期时间里的特定部分  | dateName(date_part,date)  | dateName('weekday','2018-12-11 11:12:13')  | Tuesday  | 
B) 原字段为日期(date)或日期时间(datetime/timestamp), 返回日期或时间。
用途  | 函数  | 举例  | 结果  | 
取所在周的周一  | toMonday()  | toMonday('2019-12-03 09:00:00')  | 2019-12-02  | 
取所在周的第一天 [,mode]缺省默认为0  | toStartOfWeek(t[,mode])  | toStartOfWeek('2019-12-03 09:00:00',3)  | 2019-12-02  | 
取所在月第一天  | toStartOfMonth()  | toStartOfMonth('2019-12-03 09:00:00')  | 2019-12-01  | 
取所在季第一天  | toStartOfQuarter()  | toStartOfQuarter('2019-12-03 09:00:00')  | 2019-10-01  | 
取所在年第一天  | toStartOfYear()  | toStartOfYear('2019-12-03 09:00:00')  | 2019-01-01  | 
toStartOfISOYear()  | toStartOfISOYear('2019-12-03 09:00:00')  | 2018-12-31  | |
截取时间日期到天(之后归零)  | toStartOfDay()  | toStartOfDay('2019-12-03 09:00:00')  | 2019-12-03 00:00:00  | 
截取时间日期到小时(之后归零)  | toStartOfHour(value[, timezone])  | toStartOfHour('2021-11-30 13:51:35','Asia/Shanghai')  | 2021-11-30 13:00:00  | 
截取时间日期到分钟(之后归零)  | toStartOfMinute(value[, timezone])  | toStartOfHour('2021-11-30 13:51:35','Asia/Shanghai')  | 2021-11-30 13:51:00  | 
将DateTime以五分钟为单位向前取整到最接近的时间点  | toStartOfFiveMinute(value[, timezone])  | toStartOfFiveMinute('2021-11-30 13:51:35','Asia/Shanghai')  | 2021-11-30 13:50:00  | 
将DateTime以十分钟为单位向前取整到最接近的时间点  | toStartOfTenMinutes(value[, timezone])  | toStartOfTenMinutes('2021-11-30 13:51:35','Asia/Shanghai')  | 2021-11-30 13:50:00  | 
将DateTime以十五分钟为单位向前取整到最接近的时间点  | toStartOfFifteenMinutes(value[, timezone])  | toStartOfFifteenMinutes('2021-11-30 13:51:35','Asia/Shanghai')  | 2021-11-30 13:45:00  | 
将DateTime以自定义单位向前取整到最接近的时间点  | toStartOfInterval(time_or_data,间隔x单位[,time_zone])  | toStartOfInterval('2021-11-30 13:51:35',INTERVAL 20 minute,'Asia/Shanghai')  | 2021-11-30 13:40:00  | 
将时间向前取整半小时  | timeSlot()  | timeSlot('2021-12-02 16:39:09','Asia/Shanghai')  | 2021-12-02 16:30:00  | 
截取时间日期到特定部分(之后归零), 返回Date/Datetime  | date_trunc(unit,value[, timezone] / dateTrunc(unit,value[, timezone]  | date_trunc('hour', '2021-12-02 16:39:09')dateTrunc('week', '2021-12-02 16:39:09')  | 2021-12-02 16:00:00  | 
日期或时间日期生成函数
| 用途 | 函数 | 结果 | 
| 生成当前时间日期,可指定时区 | now() | 2021-12-01 20:00:00 | 
| 生成今天的日期 | today() | 2021-12-01 | 
| 生成昨天的日期 | yesterday() / today() - 1 | 2021-11-30 | 
| 生成当前时间戳 | toUnixTimestamp(now()) | 1638388800 | 
日期与时间计算
用途  | 函数  | 举例  | 结果  | 
增减日期时间  | +/- interval n unit' (中间数值n不能引用其他字段)  | '2021-07-30 15:48:08' - interval 1 year  | 2020-07-30 15:48:08  | 
'2021-07-30 15:48:08' + interval 2 hour  | 2021-07-30 17:48:08  | ||
直接加减数字, date按照天数加减; datetime按照秒数加减  | '2021-07-30 15:48:08' - 10  | 2021-07-30 15:47:52  | |
'2021-07-30' - 10  | 2021-07-20  | ||
toInterval(Year|Quarter|Month|Week|Day|Hour|Minute|Second) (number) number — 正整数,持续的时间, 可引用其他int字段  | '2021-07-30'+toIntervalDay(7)  | 2021-08-06  | |
'2021-07-30 15:48:08' - toIntervalHour(7)  | 2021-07-30 08:48:08  | ||
计算未来日期  | addYears, addMonths, addWeeks, addDays, addHours, addMinutes, addSeconds, addQuarters  | addHours('2019-12-03 09:00:00', 1, 'Asia/Shanghai')  | 2019-12-03 10:00:00  | 
addWeeks('2019-12-03',1)  | 2019-12-10  | ||
计算过去日期  | subtractYears, subtractMonths, subtractWeeks, subtractDays, subtractours, subtractMinutes, subtractSeconds, subtractQuarters  | subtractQuarters('2021-12-03',1)  | 2021-09-03  | 
subtractDays('2019-12-03 09:00:00',3,'Asia/Shanghai')  | 2019-11-30 09:00:00  | ||
计算日期时间差 --返回整数int  | dateDiff('unit', startdate, enddate, [timezone])  | dateDiff('month', '2020-12-02', '2021-11-30')  | 11  | 
dateDiff('hour','2021-11-30 08:00:00', '2021-11-30 17:36:08','Asia/Shanghai')  | 9  | 
日期与时间日期转化
用途  | 函数  | 举例  | 结果  | 
将字符型日期转化为日期型  | toDate()  | toDate('2009-07-30 04:17:52')  | 2009-07-30  | 
cast(str,'date')/cast(str as date)  | cast('2009-07-30 04:17:52','date')  | ||
将字符型日期或者时间戳转化为时间日期型  | toDateTime()  | toDateTime('2022-01-01 13:00:00','Asia/Shanghai')  | 2022-01-01 13:00:00  | 
cast(str,'datetime')/cast(str as datetime)  | cast(today() as datetime)  | 2021-11-30 08:00:00  | |
将数值型、日期型等格式转化为字符型  | toString()  | toString('2021-07-30 15:48:08')  | 2021-07-30 15:48:08  | 
cast(time,'String') /cast(time as String)  | cast('2021-07-30 15:48:08' as String)  | ||
日期时间转换为时间戳  | toUnixTimestamp(time[, timezone])  | toUnixTimestamp('2019-12-03 09:00:00')  | 1575334800  | 
日期时间格式转化,结果一般为字符型或数值型  | formatDateTime(Time, Format \ [,Timezone\])  | formatDateTime('2021-12-02 15:48:52', '%Y/%m/%d %I:%M','Asia/Shanghai')  | 2021/12/02 03:48  | 
日期时间格式转化,结果为数值型  | toYYYYMM()  | toYYYYMM('2021-12-02')  | 202112  | 
toYYYYMMDD()  | toYYYYMMDD('2021-12-02')  | 20211202  | |
toYYYYMMDDhhmmss()  | toYYYYMMDDhhmmss('2021-12-02 16:00:09','Asia/Shanghai')  | 20211202160009  | |
toYearWeek(date[,mode])  | toYearWeek('2019-12-03')  | 201949  | |
时区偏移转换  | toTimeZone(Time, Timezone)  | toTimeZone('2021-12-02 16:00:09','US/Samoa')  | 2021-12-01 21:00:09  | 
把String类型的时间日期转换为DateTime类型  | parseDateTimeBestEffort()  | 案例参考下图  | 
案例
案例一:文本类型日期转换为标准日期格式
文本日期  | 标准格式  | 函数  | |
20210808121600  | 2021-08-08 12:16:00  | parseDateTimeBestEffort([文本日期],'Asia/Shanghai')  | |
2021/07/30 13:30:00  | 2021-07-30 13:30:00  | ||
30/7/2021 01:30 PM  | 2021-07-30 13:30:00  | ||
2021-07-30T16:00:00.000Z  | 2021-07-31 00:00:00  | parseDateTimeBestEffort([文本日期])  | |
2021-07-30T17:25:53+00:00  | 2021-07-31 17:25:53  | parseDateTimeBestEffort([文本日期],'UTC')  | |
Sat, 18 Aug 2018 07:22:16 GMT  | 2018-08-18 07:22:16  | ||
July 30, 2021  | 2021-07-30  | toDate(parseDateTimeBestEffort(replaceOne([文本日期],',','')))  | |
Aug 8, 2021  | 2021-08-08  | ||
30/07/2021  | 2021-07-30  | toDate(parseDateTimeBestEffort([文本日期]))  | |
07/30/2021  | 2021-07-30  | toDate(replaceRegexpOne([文本日期],'(\\d{2})/(\\d{2})/(\\d{4})','\\3-\\1-\\2'))  | |
2021年7月30日  | 2021-07-30  | toDate(replaceRegexpOne([文本日期],'(\\d{4})年(\\d{1,2})月(\\d{1,2})日','\\1-\\2-\\3'))  | |
案例二:标准日期转换为文本类型案例
日期  | 目标格式(文本)  | 函数  | |
2021-08-08 15:16:00  | 2021-08  | formatDateTime([日期],'%Y-%m')  | |
202108 (数值)  | toYYYYMM([日期])  | ||
08/08  | formatDateTime([日期],'%m-%d')  | ||
15:16:00  | formatDateTime([日期],'%R','Asia/Shanghai')  | ||
03:16 PM  | formatDateTime([日期],'%I:%M %p','Asia/Shanghai')  | ||
2021年8月8日  | formatDateTime([日期],'%Y年%m月%d日')  | ||
2021-08-08 15:16:00 (+08:00)  | concat([日期],' (+08:00)')  | ||
Aug 8, 2021  | 不低于v21.7版本:concat(left(dateName('month',[日期]) ),3),' ', toString(Day([日期])),',',toString(toYear([日期])))  | ||
Sunday  | dateName('weekday',[日期])  | ||
附录
- 
formatDateTime 函数支持的格式修饰符
 
“举例”列是对2018-01-02 22:33:44的格式化结果:
| 符号 | 含义 | 举例 | 
| %C | 年除以100并截断为整数(00-99) | 20 | 
| %d | 月中的一天,零填充(01-31) | 2 | 
| %D | 短MM/DD/YY日期,相当于%m/%d/%y | 01/02/2018 | 
| %e | 月中的一天,空格填充( 1-31) | 2 | 
| %F | 短YYYY-MM-DD日期,相当于%Y-%m-%d | 2018/1/2 | 
| %G | ISO周号的四位数年份格式, 从基于周的年份由ISO 8601定义 标准计算得出,通常仅对%V有用 | 2018 | 
| %g | 两位数的年份格式,与ISO 8601一致,四位数表示法的缩写 | 18 | 
| %H | 24小时格式(00-23) | 22 | 
| %I | 12小时格式(01-12) | 10 | 
| %j | 一年中的一天 (001-366) | 2 | 
| %m | 月份为十进制数(01-12) | 1 | 
| %M | 分钟(00-59) | 33 | 
| %n | 换行符(") | |
| %p | AM或PM指定 | PM | 
| %Q | 季度(1-4) | 1 | 
| %R | 24小时HH:MM时间,相当于%H:%M | 22:33 | 
| %S | 秒 (00-59) | 44 | 
| %t | 水平制表符(’) | |
| %T | ISO8601时间格式(HH:MM:SS),相当于%H:%M:%S | 22:33:44 | 
| %u | ISO8601工作日为数字,星期一为1(1-7) | 2 | 
| %V | ISO8601周编号(01-53) | 1 | 
| %w | 工作日为十进制数,周日为0(0-6) | 2 | 
| %y | 年份,最后两位数字(00-99) | 18 | 
| %Y | 年 | 2018 | 
| %% | %符号 | % | 
- 
周数计算需要使用mode参数。
 
该参数可以指定星期是从星期日还是星期一开始,以及返回值应在0到53还是从1到53的范围内。取值范围 [0,9],如果省略了mode参数,则默认模式为0。使用mode参数的函数有 toWeek(date[,mode]),toYearWeek(date[,mode]),toStartOfWeek(t[,mode])。toISOWeek()是一个兼容函数,等效于toWeek(date,3)。
下表描述了mode参数的工作方式:
| Mode | First day of week | Range | Week 1 is the first week … | 
| 0 | Sunday | 0-53 | with a Sunday in this year | 
| 1 | Monday | 0-53 | with 4 or more days this year | 
| 2 | Sunday | 1-53 | with a Sunday in this year | 
| 3 | Monday | 1-53 | with 4 or more days this year | 
| 4 | Sunday | 0-53 | with 4 or more days this year | 
| 5 | Monday | 0-53 | with a Monday in this year | 
| 6 | Sunday | 1-53 | with 4 or more days this year | 
| 7 | Monday | 1-53 | with a Monday in this year | 
| 8 | Sunday | 1-53 | contains January 1 | 
| 9 | Monday | 1-53 | contains January 1 | 
对于象“with 4 or more days this year,”的mode值,根据ISO 8601:1988对周进行编号:
- 
如果包含1月1日的一周在后一年度中有4天或更多天,则为第1周。
 - 
否则,它是上一年的最后一周,下周是第1周。
 
对于像“contains January 1”的mode值, 包含1月1日的那周为本年度的第1周。