语法-日期、时间

Neo
Neo
2022-03-15 / 0 评论 / 68 阅读

本节内容源自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必须是完整的;即完全确定特定的一天。

指定日期

组件格式描述
YearYYYY指定至少四位数字(在某些情况下适用特殊规则)。
MonthMM用从01到12的两位数指定。
Weekww始终以W为前缀,并以01到53之间的两位数指定。
Quarterq始终以Q为前缀,并用1到4之间的一位数指定。
Day of the monthDD用01到31之间的两位数指定。
Day of the weekD用1到7之间的一位数指定。
Day of the quarterDD用01到92之间的两位数指定。
Ordinal day of the yearDDD用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相同的日期。

指定时间

部件格式描述
HourHH用从00到23的两位数指定。
MinuteMM使用从00到59的两位数指定。
SecondSS使用从00到59的两位数指定。
fractionsssssssss使用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开头。

  • 基于日期和时间的表格:PT

    • 与基于单元的表单不同,此表单要求每个组件都在有效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

时间索引

所有时间类型都可以被索引,从而支持相等谓词的精确查找。时间即时类型的索引还支持范围查找。