<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
-->

## 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: 

```java
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:

```java
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:

* [TypeCodecs.ZONED_TIMESTAMP_SYSTEM]\: system default;
* [TypeCodecs.ZONED_TIMESTAMP_UTC]\: UTC;
* [TypeCodecs.zonedTimestampAt()]\: user-provided.

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:

```java
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:

```java
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]"
```

[CqlDuration]:                       https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/data/CqlDuration.html
[TypeCodecs.ZONED_TIMESTAMP_SYSTEM]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/type/codec/TypeCodecs.html#ZONED_TIMESTAMP_SYSTEM
[TypeCodecs.ZONED_TIMESTAMP_UTC]:    https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/type/codec/TypeCodecs.html#ZONED_TIMESTAMP_UTC
[TypeCodecs.zonedTimestampAt()]:     https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/type/codec/TypeCodecs.html#zonedTimestampAt-java.time.ZoneId-
