Temporal types

Quick overview

This page provides more details about the various CQL time types, and the Java types they are mapped to in the driver.

CQL Java
date java.time.LocalDate
time java.time.LocalTime
timestamp java.time.Instant No time zone. Use Instant.atZone or register TypeCodecs.ZONED_TIMESTAMP_SYSTEM, TypeCodecs.ZONED_TIMESTAMP_UTC or TypeCodecs.zonedTimestampAt()
duration CqlDuration Custom driver type; can’t be accurately represented by any of the java.time types.

Date and time

CQL types date and time map directly to java.time.LocalDate and java.time.LocalTime.

These are simple, time-zone-free representations of date-only (yyyy-mm-dd) and time-only (HH:MM:SS[.fff]) types.

Timestamp

CQL type timestamp is the date-and-time representation, stored as a number of milliseconds since the epoch (01/01/1970 UTC).

No time zone

timestamp does not store a time zone. This is not always obvious because clients generally do use one for display. For instance, the following CQLSH snippet is from a machine in Pacific time:

cqlsh> CREATE TABLE test(t timestamp PRIMARY KEY);
cqlsh> INSERT INTO test (t) VALUES (dateof(now()));
cqlsh> SELECT * FROM test;

 t
---------------------------------
 2018-11-07 08:50:52.433000-0800

It looks like the timestamp has a zone (-0800), but it is actually the client’s. If you force CQLSH to a different zone and observe the same data, it will be displayed differently:

$ TZ=UTC cqlsh
cqlsh> SELECT * FROM test;

 t
---------------------------------
 2018-11-07 16:50:52.433000+0000

Internally, Cassandra only stores the raw number of milliseconds. You can observe that with a cast:

cqlsh> SELECT cast(t as bigint) FROM test;

 cast(t as bigint)
-------------------
     1541609452433

Java equivalent

By default, the driver maps timestamp to java.time.Instant. This Java type is the closest to the internal representation; in particular, it does not have a time zone. On the downside, this means you can’t directly extract calendar fields (year, month, etc.). You need to call atZone to perform the conversion:

Row row = session.execute("SELECT t FROM test").one();
Instant instant = row.getInstant("t");
ZonedDateTime dateTime = instant.atZone(ZoneId.of("America/Los_Angeles"));
System.out.println(dateTime.getYear());

Conversely, you can convert a ZonedDateTime back to an Instant with toInstant.

If you want to automate those atZone/toInstant conversions, the driver comes with an optional ZonedDateTime codec, that must be registered explicitly with the session:

CqlSession session = CqlSession.builder()
    .addTypeCodecs(TypeCodecs.ZONED_TIMESTAMP_UTC)
    .build();

Row row = session.execute("SELECT t FROM test").one();
ZonedDateTime dateTime = row.get("t", GenericType.ZONED_DATE_TIME);

There are various constants and methods to obtain a codec instance for a particular zone:

Which zone you choose is application-dependent. The driver doesn’t map to ZonedDateTime by default because it would have to make an arbitrary choice; we want you to think about time zones explicitly before you decide to use that type.

Millisecond-only precision

As already stated, timestamp is stored as a number of milliseconds. If you try to write an Instant or ZonedDateTime with higher precision through the driver, the sub-millisecond part will be truncated:

CqlSession session =
    CqlSession.builder()
        .addTypeCodecs(TypeCodecs.ZONED_TIMESTAMP_UTC)
        .build();

ZonedDateTime valueOnClient = ZonedDateTime.parse("2018-11-07T16:50:52.433395762Z");
                                                // sub-millisecond digits ^^^^^^
session.execute(
    SimpleStatement.newInstance("INSERT INTO test (t) VALUES (?)", valueOnClient));

ZonedDateTime valueInDb =
    session.execute("SELECT * FROM test").one().get(0, GenericType.ZONED_DATE_TIME);
System.out.println(valueInDb);
// Prints "2018-11-07T16:50:52.433Z"

Duration

CQL type duration represents a period in months, days and nanoseconds. The driver maps it to a custom type: CqlDuration.

We deliberately avoided java.time.Period, because it does not contain a nanoseconds part as CqlDuration does; and we also avoided java.time.Duration, because it represents an absolute time-based amount, regardless of the calendar, whereas CqlDuration manipulates conceptual days and months instead. Thus a CqlDuration of “2 months” represents a different amount of time depending on the date to which it is applied (because months have a different number of days, and because daylight savings rules might also apply, etc).

CqlDuration implements java.time.temporal.TemporalAmount, so it interoperates nicely with the JDK’s built-in temporal types:

ZonedDateTime dateTime = ZonedDateTime.parse("2018-10-04T00:00-07:00[America/Los_Angeles]");
System.out.println(dateTime.minus(CqlDuration.from("1h15s15ns")));
// prints "2018-10-03T22:59:44.999999985-07:00[America/Los_Angeles]"