Spec-Zone.ru › RethinkDB java

Даты и время в RethinkDB

RethinkDB имеет встроенную поддержку времени с точностью до миллисекунды и часовыми поясами. Некоторые особенности:

  • Время интегрировано с официальными драйверами, которые автоматически преобразуют его в и из типов времени, используемых в вашем языке.
  • Запросы учитывают часовой пояс, поэтому вы можете задавать вопросы, например, «Произошло ли это событие в понедельник в часовом поясе, в котором оно было записано?»
  • Время используется в качестве индексов, поэтому вы можете эффективно извлекать события по времени их возникновения.
  • Операции с временем являются чистым ReQL, что означает, что даже сложные запросы к датам и времени могут быть эффективно распределены по кластеру.
  • Быстрый пример
  • Технические детали
  • Вставка времени
  • Получение времени
  • Работа с временем
  • Объединение всего вместе

Dates and Times Illustration

Быстрый пример

Сначала давайте создадим таблицу и вставим некоторые события. Мы вставим первое событие, используя объект OffsetDateTime, а второе с помощью epochTime конструктора:

import java.time.OffsetDateTime;
import java.util.List;

r.tableCreate("events").run(conn);

OffsetDateTime nowDateTime = OffsetDateTime.now();

r.table("events").insert(r.array(
    r.hashMap("id", 0).with("timestamp", nowDateTime),
    r.hashMap("id", 1).with("timestamp", r.epochTime(1376436769.923))
)).run(conn);

Теперь давайте получим их обратно:

Cursor cursor = r.table("events").run(conn);
List events = cursor.toList();
System.out.println(events);

Результат:

[{id=0, timestamp=2016-01-05T10:41:45.100-08:00}, {id=1, timestamp=2013-08-13T23:32:49.923Z}]

Оба времени возвращаются как родные объекты Java 8 OffsetDateTime.

Теперь мы можем фильтровать по этим временам:

cursor = r.table("events").filter(
    row -> row.g("timestamp").hours().gt(20)
).run(conn);
events = cursor.toList();
System.out.println(events);
[{id=1, timestamp=2013-08-13T23:32:49.923Z}]

Или создать вторичный индекс по ним:

r.table("events").indexCreate("timestamp").run(conn);

cursor = r.table("events").between(
    r.epochTime(1376436769.913), r.epochTime(1376436769.933)
).optArg("index", "timestamp").run(conn);
events = cursor.toList();
System.out.println(events);
[{id=1, timestamp=2013-08-13T23:32:49.923Z}]

Технические детали

Время хранится на сервере как секунды с начала эпохи (UTC) с точностью до миллисекунды плюс часовой пояс. В настоящее время доступны только часовые пояса, представляющие сдвиг от UTC в минутах, но в будущем может быть добавлена поддержка часовых поясов, учитывающих переходы на летнее время. Часовые пояса представлены строками в формате ISO 8601.

Время считается равным, когда значения времени эпохи (UTC) равны, независимо от часового пояса. Это справедливо как для сравнений, так и для операций с индексами. Время сравнивается с плавающей точкой с точностью до миллисекунды.

Большинство операций с датой определены только для лет в диапазоне [1400, 10000] (обратите внимание, что время в году 10000 нельзя напечатать как даты ISO 8601).

Секунды високосных лет в настоящее время не поддерживаются должным образом: 2012-06-30T23:59:60 и 2012-07-01T00:00:00 интерпретируются как одно и то же время.

Вставка времени

Вы можете вставлять время, просто передавая объект OffsetDateTime.

OffsetDateTime myDateTime = OffsetDateTime.now();

r.table("events").insert(
    r.hashMap("id", 2).with("timestamp", myDateTime),
).run(conn);
{unchanged=0, skipped=0, replaced=0, inserted=1, errors=0, deleted=0}

Вы также можете использовать r.now (который сервер интерпретирует как время получения запроса в UTC), или создать время, используя r.time, r.epochTime, или r.ISO8601.

r.now().toISO8601().run(conn, callback);
// returns "2013-08-09T18:53:15.012+00:00"

r.time(2013, r.august(), 9, 18, 53, 15.012, "-07:00").toIso8601().run(conn);
// returns "2013-08-09T18:53:15.012-07:00"

r.epochTime(1376074395.012).toIso8601().run(conn);
// returns "2013-08-09T18:53:15.012+00:00"

r.iso8601("2013-08-09T18:53:15.012-07:00").toIso8601().run(conn);
// returns "2013-08-09T18:53:15.012-07:00"

Время может быть использовано в качестве первичного ключа для таблицы. Два времени считаются равными, если они имеют одинаковое количество миллисекунд с начала эпохи (UTC), независимо от часового пояса.

r.table("t").insert(
    r.hashMap("id", r.iso8601("2013-08-09T11:58:00.1111-07:00"))
).run(conn);

// returns:
// {deleted=0, errors=0, inserted=1, replaced=0, skipped=0, unchanged=0}

r.table("t").insert(
    r.hashMap("id", r.iso8601("2013-08-09T10:58:00.1112-08:00"))
).run(conn);

// returns: 
// {deleted=0, errors=1, inserted=0, replaced: 0, skipped=0, unchanged=0, first_error="Duplicate primary key `id`=..."}

Вы также можете вставить время, вставив объект псевдотипа. Это полезно, если, например, вы экспортировали строку, используя {timeFormat: 'raw'} (см. Получение времени ниже).

Примечание: Избегайте использования ключей, соответствующих регулярному выражению ^\$reql_.+\$$ в ваших объектах. RethinkDB рассматривает их как зарезервированные ключевые слова.

r.expr(
    r.hashMap("$reql_type$", "TIME")
     .with("epoch_time", 1376075362.662)
     .with("timezone", "+00:00")
).toIso8601().run(conn);

Получение времени

По умолчанию время преобразуется в родные объекты при извлечении из сервера. Это можно переопределить, передав optArg timeFormat в run. В настоящее время доступны только native, по умолчанию, и raw.

r.now().run(conn);
// returns "2016-01-06T00:34:13.623Z"

r.now().inTimezone("-07:00").run(conn);
// returns "2016-01-05T17:34:13.623Z-07:00"

import com.rethinkdb.model.OptArgs;
r.now().run(conn, OptArgs.of("time_format", "raw"));
// returns:
// {"timezone":"+00:00","$reql_type$":"TIME","epoch_time":1.452040701881E9}

r.now().inTimezone("-07:00").run(conn, OptArgs.of("time_format", "raw"));
// returns:
// {"timezone":"-07:00","$reql_type$":"TIME","epoch_time":1.452040701881E9}

Вы также можете преобразовать объект времени на сервере, используя либо toEpochTime или toIso8601.

r.now().toEpochTime().run(conn);
// returns 1376075986.574

r.now().toISO8601().run(conn);
// returns "2013-08-09T19:19:46.574+00:00"

Работа с временем

Существует только три полезные вещи, которые можно сделать со временем: изменить его, сравнить его с другим временем или извлечь часть из него.

Изменение времени

Вы можете добавить или вычесть период (в секундах):

r.time(2015, 1, 1, "Z").add(86400).run(conn);
// returns "2015-01-02T00:00Z"

Если вы вычитаете два времени, вы получите период:

r.time(2015, 1, 2, "Z").sub(r.time(2015, 1, 1, "Z")).run(conn);
// returns 86400

Сравнение времени

Все обычные операторы сравнения определены для времени:

r.epochTime(1376081287.982).lt(new Date()).run(conn, callback);
// true

Время сравнивается только с точностью до миллисекунды:

r.epochTime(1376081287.9821).eq(r.epochTime(1376081287.9822)).run(conn);
// true

Также есть команда during, которая может проверить, находится ли время в определенном диапазоне времени.

Извлечение частей времени

Если у вас есть время, вы можете извлечь определенную часть (например, месяц или часы) относительно текущего часового пояса. (См. полный список в справке по API.)

OffsetDateTime nowDateTime = OffsetDateTime.now();

r.expr(nowDateTime).run(conn);
// returns "2013-08-13T23:32:49.923Z"

r.expr(nowDateTime).month().run(conn);
// returns 8

r.expr(nowDateTime).hours().run(conn);
// returns 23

r.expr(nowDateTime).inTimezone("-06:00").hours()run(conn);
// returns 17

Мы используем определение недели ISO 8601, которая начинается с понедельника, представленного как 1.

r.expr(nowDateTime).dayOfWeek().run(conn);
// returns 2 for Tuesday

Мы определяем r.monday...r.sunday и r.january...r.december для удобства:

r.expr(nowDateTime).dayOfWeek().eq(r.tuesday).run(conn);
// returns true

Мы также позволяем вам разделить время на дату и текущее время суток (соответственно, время и период):

r.now().toEpochTime().run(conn);
// returns 1376351312.744

r.now().date().toEpochTime().run(conn);
// returns 1376265600

r.now().timeOfDay().run(conn);
// returns 85712.744

Объединение всего вместе

Комбинируя эти операции, вы можете писать удивительно полезные запросы в чистом ReQL. Например, предположим, что у вас есть таблица продаж, которые сделала ваша компания, и вы хотите узнать, сколько валового дохода приходится на сотрудников, которые работали сверхурочно:

r.table("sales").filter(sale ->
    // Weekends are overtime
    sale.g("time").dayOfWeek().eq(r.saturday())
    .or(sale.g("time").dayOfWeek().eq(r.sunday()))
    // Weekdays outside 9-5 are overtime
    .or(sale.g("time").hours().lt(9))
    .or(sale.g("time").hours().ge(17))
).sum("dollars").run(conn);

Если ваши временные метки хранятся с часовыми поясами, этот запрос будет работать даже если у вас есть продажи из разных офисов в разных странах (если все они работают по местному времени с 9 до 17).

Поскольку этот запрос написан на чистом ReQL, весь запрос будет эффективно распределен по кластеру без каких-либо вычислений на клиенте.

Кроме того, поскольку это ReQL, отдельные части запроса легко комбинируются. Если вы решите, что вам нужны эти числа на ежемесячной основе, вы можете просто добавить group:

r.table("sales").filter(sale ->
    // Weekends are overtime
    sale.g("time").dayOfWeek().eq(r.saturday())
    .or(sale.g("time").dayOfWeek().eq(r.sunday()))
    // Weekdays outside 9-5 are overtime
    .or(sale.g("time").hours().lt(9))
    .or(sale.g("time").hours().ge(17))
).group(sale -> sale.g("time").month()).sum("dollars").run(conn);

© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/dates-and-times/java/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API