本节内容源自Cypher官方手册V4.4版,笔者对其进行学习、翻译、记录。
Cypher 具有对处理时间值的内置支持,并且底层数据库支持将这些时间值存储为节点和关系上的属性。
下表列出了时间值类型和支持的组件:
|类型|日期支持|时间支持|时区支持|
|-------|-------|-------|-------|
|Date|√|||
|Time||√|√|
|LocalTime||√||
|DateTime|√|√|√|
|LocalDateTime|√|√||
|Duration||||
Date, Time, LocalTime, DateTime 和 LocalDateTime 是时间瞬间类型。时间瞬时值以不同程度的精度表示时间点。
相比之下,Duration不是时间瞬间类型。Duration表示时间量,捕获两个瞬间之间的时间差,并且可以是负数。 Duration捕获两个瞬间之间的时间量,它不捕获开始时间和结束时间。
时区
时区表示为与 UTC 的偏移量,或表示为指定时区的逻辑标识符(这些基于IANA 时区数据库)。在任何一种情况下,时间都在内部存储为 UTC,并且时区偏移仅在显示时间时应用。这意味着可以在不考虑时区的情况下对时间瞬间进行排序。但是,如果两个时间在 UTC 中相同,则它们按时区排序。
使用命名时区创建时间时,根据时区数据库中的规则计算与 UTC 的偏移量,以创建 UTC 时间瞬间,并确保命名时区是有效的。
IANA 时区数据库中的时区规则可能会更改。例如,可能会更改某个地区的夏令时规则。如果这发生在创建时间瞬间之后,则显示的时间可能与最初输入的时间不同,就本地时区而言。但是,UTC 中的绝对时间将保持不变。
在 Cypher 中有三种指定时区的方法:
-
以小时和分钟为单位指定与 UTC 的偏移量 ( ISO 8601 )。
-
指定一个命名的时区。
-
指定偏移量和时区名称(要求它们匹配)。
有关示例,请参阅指定时区。
命名时区表使用 IANA 时区数据库的规则来管理夏令时(DST)。
可以使用配置选项来配置数据库的默认时区db.temporal.timezone。此配置选项会影响以下功能的时间类型的创建:
-
在不指定时区的情况下获取当前日期和时间。
-
从其组件创建时间类型而不指定时区。
-
通过解析字符串而不指定时区来创建时间类型。
-
通过组合或选择没有时区组件且不指定时区的值来创建时间类型。
-
截断没有时区分量且不指定时区的时间值。
瞬时时间
指定瞬时时间
一个时间瞬间由三个部分组成;date、time和timezone。这些部分可以组合起来产生各种时间值类型。
|时间瞬间类型|组成部分|
|-------|-------|
|Date|<date>|
|Time|<time><timezone> or T<time><timezone>|
|LocalTime|<time> or T<time>|
|DateTime*|<date>T<time><timezone>|
|LocalDateTime*|<date>T<time>|
*当date和time结合时,date必须是完整的;即完全确定特定的一天。
指定日期
组件 | 格式 | 描述 |
---|---|---|
Year | YYYY | 指定至少四位数字(在某些情况下适用特殊规则)。 |
Month | MM | 用从01到12的两位数指定。 |
Week | ww | 始终以W为前缀,并以01到53之间的两位数指定。 |
Quarter | q | 始终以Q为前缀,并用1到4之间的一位数指定。 |
Day of the month | DD | 用01到31之间的两位数指定。 |
Day of the week | D | 用1到7之间的一位数指定。 |
Day of the quarter | DD | 用01到92之间的两位数指定。 |
Ordinal day of the year | DDD | 用001到366之间的三位数指定。 |
如果年份在0000之前或9999之后,则适用以下附加规则:
-
减号,必须在任何0000年份之前添加前缀-,(例如 -3000-01-01)。
-
加号,必须在9999之后的任何年份添加前缀+(例如 +11000-01-01)。
-
年份必须用-将下一个组件分开:
-
如果下一个组件是月份,(例如+11000-01)。
-
如果下一个组件是一年中的某一天,(例如+11000-123)。
-
如果年份组件的前缀为-或+,并与下一个组件分开,则年份最多可以包含九位数字。因此,允许的年数范围介于-99999999和+99999999之间。对于所有其他情况,即年份介于0000和9999(含)之间,年份必须正好有四位数字(年份分量被解释为通用纪元(CE)的年份)。
指定日期支持以下格式:
|格式|描述|例子|解释|
|-------|-------|-------|-------|
|YYYY-MM-DD|Calendar date: Year-Month-Day|2015-07-21|2015-07-21|
|YYYYMMDD|Calendar date: Year-Month-Day|20150721|2015-07-21|
|YYYY-MM|Calendar date: Year-Month|2015-07|2015-07-01|
|YYYYMM|Calendar date: Year-Month|201507|2015-07-01|
|YYYY-Www-D|Week date: Year-Week-Day|2015-W30-2|2015-07-21|
|YYYYWwwD|Week date: Year-Week-Day|2015W302|2015-07-21|
|YYYY-Www|Week date: Year-Week|2015-W30|2015-07-20|
|YYYYWww|Week date: Year-Week|2015W30|2015-07-20|
|YYYY-Qq-DD|Quarter date: Year-Quarter-Day|2015-Q2-60|2015-05-30|
|YYYYQqDD|Quarter date: Year-Quarter-Day|2015Q260|2015-05-30|
|YYYY-Qq|Quarter date: Year-Quarter|2015-Q2|2015-04-01|
|YYYYQq|Quarter date: Year-Quarter|2015Q2|2015-04-01|
|YYYY-DDD|Ordinal date: Year-Day|2015-202|2015-07-21|
|YYYYDDD|Ordinal date: Year-Day|2015202|2015-07-21|
|YYYY|Year|2015|2015-01-01|
可以省略最不重要的组件。Cypher 将假定省略的组件具有最低的可能值。例如,2013-06将被解释为与2013-06-01相同的日期。
指定时间
部件 | 格式 | 描述 |
---|---|---|
Hour | HH | 用从00到23的两位数指定。 |
Minute | MM | 使用从00到59的两位数指定。 |
Second | SS | 使用从00到59的两位数指定。 |
fraction | sssssssss | 使用0到99999999之间的数字指定。不需要指定尾随零。分数是秒的可选亚秒分量。可以使用句号(.)将其与秒分隔开或逗号(,)。分数是秒的两位数的加法。 |
Cypher 不支持闰秒;UTC-SLS(带有平滑闰秒的 UTC)用于管理 UTC 和 TAI(国际原子时间)之间的时间差异。
指定时间支持以下格式:
|格式|描述|例子|示例解释|
|-------|-------|-------|-------|
|HH:MM:SS.sssssssss|Hour:Minute:Second.fraction|21:40:32.142|21:40:32.142|
|HHMMSS.sssssssss|Hour:Minute:Second.fraction|214032.142|21:40:32.142|
|HH:MM:SS|Hour:Minute:Second|21:40:32|21:40:32.000|
|HHMMSS|Hour:Minute:Second|214032|21:40:32.000|
|HH:MM|Hour:Minute|21:40|21:40:00.000|
|HHMM|Hour:Minute|2140|21:40:00.000|
|HH|Hour|21|21:00:00.000|
最不重要的部分可以省略。例如,时间可以用Hour和Minute指定,不包括Second和fraction。另一方面,不可能用Hour和Second来指定时间,而忽略Minute。
指定时区
时区通过以下方式之一指定:
-
作为 UTC 的偏移量。
-
使用UTC (±00:00) 时区的Z简写。
将时区指定为与 UTC 的偏移量时,适用以下规则:
-
时区始终以加号 ( +) 或减号 ( -) 开头。
-
正偏移量,即以+开头的时区,表示 UTC 以东的时区。
-
负偏移量,即以-开头的时区,表示 UTC 以西的时区。
-
-
+/-符号后面是两位数的小时偏移量。
-
小时偏移后有一个可选的两位数分钟偏移,可选用冒号 (:) 分隔。
-
国际日期变更线的时区由+12:00或-12:00表示,具体取决于国家/地区。
创建DateTime时间即时类型的值时,也可以使用命名时区指定时区,使用IANA 时区数据库中的名称。这可以附加地或代替偏移来提供。最后给出指定的时区,并用方括号 ([]) 括起来。如果同时提供偏移量和指定时区,则偏移量必须与指定时区匹配。
指定时区支持以下格式:
|格式|描述|例子|支持DateTime|支持Time|
|-------|-------|-------|-------|-------|
|Z|UTC|Z|√|√|
|±HH:MM|Hour:Minute|+09:30|√|√|
|±HH:MM[ZoneName]|Hour:Minute[ZoneName]|+08:45[Australia/Eucla]|√||
|±HHMM|Hour:Minute|+0100|√|√|
|±HHMM[ZoneName]|Hour:Minute[ZoneName]|+0200[Africa/Johannesburg]|√||
|±HH|Hour|-08|√|√|
|±HH[ZoneName]|Hour[ZoneName]|+08[Asia/Singapore]|√||
|[ZoneName]|[ZoneName]|[America/Regina]|√||
例子
我们在下面展示了使用各种格式解析时间瞬时值的示例。
使用日历日期格式解析DateTime :
RETURN datetime('2015-06-24T12:50:35.556+0100') AS theDateTime
2015-06-24T12:50:35.556+01:00
使用序数日期格式解析LocalDateTime :
RETURN localdatetime('2015185T19:32:24') AS theLocalDateTime
2015-07-04T19:32:24
使用星期日期格式解析日期:
RETURN date('+2015-W13-4') AS theDate
RETURN date('+2015-W13-4') AS theDate
2015-03-26
解析Time:
RETURN time('125035.556+0100') AS theTime
12:50:35.556+01:00
解析LocalTime:
RETURN localtime('12:50:35.556') AS theLocalTime
12:50:35.556
访问时间瞬间的组件
时间瞬时值的组件可以作为属性访问。
时间瞬时值的组成部分及其支持位置
|组件|描述|类型|范围/格式|Date|DateTime|LocalDateTime|Time| LocalTime|
|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|instant.year|[1]|Integer|至少4位数|√|√|√|||
|instant.quarter||Integer|1 to 4||||||
|instant.month||Integer|1 to 12||||||
|instant.week||Integer|1 to 53||||||
|instant.weekYear||Integer|至少4位数||||||
|instant.dayOfQuarter||Integer|1 to 92||||||
|instant.quarterDay||Integer|1 to 92||||||
|instant.day||Integer|1 to 31||||||
|instant.ordinalDay||Integer|1 to 366||||||
|instant.dayOfWeek||Integer|1 to 7||||||
|instant.weekDay||Integer|1 to 7||||||
|instant.hour||Integer|0 to 23||||||
|instant.minute||Integer|0 to 59||||||
|instant.second||Integer|0 to 59||||||
|instant.millisecond||Integer|0 to 999||||||
|instant.microsecond||Integer|0 to 999999||||||
|instant.nanosecond||Integer|0 to 999999999||||||
|instant.timezone||String|根据时区的指定方式,这可以是时区名称,也可以是与UTC的偏移量,格式为±HHMM||||||
|instant.offset||String|±HHMM||||||
|instant.offsetMinutes||Integer|-1080 to +1080||||||
|instant.offsetSeconds||Integer|-64800 to +64800||||||
|instant.epochMillis||Integer|1970-01-01T00:00:00+0000之后的瞬间为正值,1970-01-01T00:00:00+0000之前的瞬间为负值||||||
|instant.epochSeconds||Integer|1970-01-01T00:00:00+0000之后的瞬间为正值,1970-01-01T00:00:00+0000之前的瞬间为负值||||||
以下查询显示了如何提取Date值的组成部分:
WITH date({year: 1984, month: 10, day: 11}) AS d
RETURN d.year, d.quarter, d.month, d.week, d.weekYear, d.day, d.ordinalDay, d.dayOfWeek, d.dayOfQuarter
以下查询显示如何提取DateTime值的与日期相关的组件:
WITH datetime({
year: 1984, month: 11, day: 11,
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'
}) AS d
RETURN d.year, d.quarter, d.month, d.week, d.weekYear, d.day, d.ordinalDay, d.dayOfWeek, d.dayOfQuarter
以下查询显示如何提取DateTime值的时间相关组件:
WITH datetime({
year: 1984, month: 11, day: 11,
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'
}) AS d
RETURN d.hour, d.minute, d.second, d.millisecond, d.microsecond, d.nanosecond
以下查询显示如何提取DateTime值的纪元时间和时区相关组件:
WITH datetime({
year: 1984, month: 11, day: 11,
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'
}) AS d
RETURN d.timezone, d.offset, d.offsetMinutes, d.epochSeconds, d.epochMillis
持续时间
指定持续时间
Duration表示时间量,捕获两个瞬间之间的时间差,并且可以是负数。
Duration的规范以P为前缀,并且可以使用基于单位的形式或基于日期和时间的形式:
-
基于单位的形式:P[nY][nM]nW][nD][T[nH][nM][nS]]
-
方括号 ( []) 表示可选组件(可以省略具有零值的组件)。
-
n表示可以任意大的数值。
-
最后一个(也是最不重要的)分量的值可能包含小数部分。
-
每个组件都必须以表示该单元的组件标识符作为后缀。
-
基于单位的表单使用M作为月和分钟的后缀。因此,即使没有给出日期部分的任何部分,时间部分也必须始终以T开头。
-
-
基于日期和时间的表格:P
T - 与基于单元的表单不同,此表单要求每个组件都在有效LocalDateTime的范围内。
下表列出了基于单元的表单的组件标识符:
|标识符|描述|组件|
|-------|-------|-------|
|Y|年||
|M|月|必须在T之前指定。|
|W|周||
|D|日||
|H|时||
|M|分|必须在T之后指定。|
|S|秒||
例子
以下示例演示了解析Duration值的各种方法。
返回Duration of 14 days、16 hours和12 minutes:
RETURN duration('P14DT16H12M') AS theDuration
P14DT16H12M
返回一个Duration of5 月、1 日和12 小时:
RETURN duration('P5M1.5D') AS theDuration
P5M1DT12H
返回秒的持续时间45:
RETURN duration('PT0.75M') AS theDuration
PT45S
返回一个Duration of 2 week,3 days和12 hours:
RETURN duration('P2.5W') AS theDuration
P17DT12H
访问持续时间的组件
一个Duration可以有几个组件,每个组件分为Months、Days和Seconds组。
Duration值的组件在其组件组内被截断,如下所示:
|组件|描述|类型|详细|
|-------|-------|-------|-------|
|
duration.years|content2|content3|content2|
|duration.quarters||Integer||
|duration.months||Integer||
|duration.weeks||Integer||
|duration.days||Integer||
|duration.hours||Integer||
|duration.minutes||Integer||
|duration.seconds||Integer||
|duration.milliseconds||Integer||
|duration.microseconds||Integer||
|duration.nanoseconds||Integer||
请注意:
- Cypher在处理闰秒时使用UTC-SLS 。
- 一天中并不总是有24 几个小时;当切换到/从夏令时,一天可以有23或25小时。
- 一个月的天数并不总是相同的。
- 由于闰年,一年中的天数并不总是相同。
还可以访问由组中最大(最重要)组件所限定的组件组中较小(不太重要)的组件:
|组件|组件组|描述|类型|
|-------|-------|-------|-------|
|duration.quartersOfYear|Months||Integer|
|duration.monthsOfYear|Months||Integer|
|duration.monthsOfQuarter|Months||Integer|
|duration.daysOfWeek|Days||Integer|
|duration.minutesOfHour|Seconds||Integer|
|duration.secondsOfMinute|Seconds||Integer|
|duration.millisecondsOfSecond|Seconds||Integer|
|duration.microsecondsOfSecond|Seconds||Integer|
|duration.nanosecondsOfSecond|Seconds||Integer|
以下查询显示如何提取Duration值的基于月份的组件:
WITH duration({years: 1, months: 5, days: 111, minutes: 42}) AS d
RETURN d.years, d.quarters, d.quartersOfYear, d.months, d.monthsOfYear, d.monthsOfQuarter
以下查询显示如何提取Duration值的基于日期的组件:
WITH duration({months: 5, days: 25, hours: 1}) AS d
RETURN d.weeks, d.days, d.daysOfWeek
以下查询显示如何提取Duration值的最重要的基于秒的组件:
WITH duration({
years: 1, months:1, days:1, hours: 1,
minutes: 1, seconds: 1, nanoseconds: 111111111
}) AS d
RETURN d.hours, d.minutes, d.seconds, d.milliseconds, d.microseconds, d.nanoseconds
以下查询显示了如何提取Duration值的次要分量:
WITH duration({
years: 1, months:1, days:1,
hours: 1, minutes: 1, seconds: 1, nanoseconds: 111111111
}) AS d
RETURN d.minutesOfHour, d.secondsOfMinute, d.millisecondsOfSecond, d.microsecondsOfSecond, d.nanosecondsOfSecond
例子
以下示例说明了一些时间函数和运算符的使用。
创建一个代表1.5天的Duration:
RETURN duration({days: 1, hours: 12}) AS theDuration
P1DT12H
计算两个时间瞬间之间的持续时间:
RETURN duration.between(date('1984-10-11'), date('2015-06-24')) AS theDuration
P30Y8M13D
计算两个Date值之间的天数:
RETURN duration.inDays(date('2014-10-11'), date('2015-08-06')) AS theDuration
获取当年的第一个日期:
RETURN date.truncate('year') AS day
获取特定日期的一周中的星期四的日期:
RETURN date.truncate('week', date('2019-10-01'), {dayOfWeek: 4}) AS thursday
获取下个月最后一天的日期:
RETURN date.truncate('month', date() + duration('P2M')) - duration('P1D') AS lastDay
将Duration添加到Date:
RETURN time('13:42:19') + duration({days: 1, hours: 12}) AS theTime
添加两个Duration值:
RETURN duration({days: 2, hours: 7}) + duration({months: 1, hours: 18}) AS theDuration
将Duration乘以一个数字:
RETURN duration({hours: 5, minutes: 21}) * 14 AS theDuration
将Duration除以一个数字:
RETURN duration({hours: 3, minutes: 16}) / 2 AS theDuration
检查两个瞬间是否相隔不到一天:
WITH
datetime('2015-07-21T21:40:32.142+0100') AS date1,
datetime('2015-07-21T17:12:56.333+0100') AS date2
RETURN
CASE
WHEN date1 < date2 THEN date1 + duration("P1D") > date2
ELSE date2 + duration("P1D") > date1
END AS lessThanOneDayApart
true
返回当前月份的缩写名称:
RETURN ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date().month-1] AS month
时间索引
所有时间类型都可以被索引,从而支持相等谓词的精确查找。时间即时类型的索引还支持范围查找。