Skip to main content
CQL provides several date and time types for representing temporal data. The driver supports both native CQL types and popular Rust date/time crates.

Overview

CQL has four temporal types:
  • date - Calendar date (year, month, day)
  • time - Time of day (hours, minutes, seconds, nanoseconds)
  • timestamp - Instant in time (milliseconds since Unix epoch)
  • duration - Time duration (months, days, nanoseconds)

Native Types

The driver provides native types that directly represent CQL’s internal format.

Date

CqlDate represents a date as days since a reference point (2^31 days before Unix epoch).
use scylla::value::CqlDate;

// Create a date (days since reference point)
let days_since_epoch: u32 = (1 << 31) + 18993; // Jan 1, 2022
let date = CqlDate(days_since_epoch);

session.query_unpaged(
    "INSERT INTO events (id, event_date) VALUES (?, ?)",
    (1, date),
).await?;

// Read back
let result = session
    .query_unpaged("SELECT event_date FROM events WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let date: CqlDate = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_cql_date())
        .unwrap();
    println!("Date: {:?}", date);
}

Time

CqlTime represents time as nanoseconds since midnight.
use scylla::value::CqlTime;

// Create a time (nanoseconds since midnight)
// 14:30:00.123456789
let nanos: i64 = (14 * 3600 + 30 * 60) * 1_000_000_000 + 123_456_789;
let time = CqlTime(nanos);

session.query_unpaged(
    "INSERT INTO schedule (id, start_time) VALUES (?, ?)",
    (1, time),
).await?;

Timestamp

CqlTimestamp represents a timestamp as milliseconds since Unix epoch.
use scylla::value::CqlTimestamp;

// Create a timestamp (milliseconds since Unix epoch)
let millis: i64 = 1640995200000; // Jan 1, 2022 00:00:00 UTC
let timestamp = CqlTimestamp(millis);

session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (1, timestamp),
).await?;

// Get current timestamp
let now = CqlTimestamp(chrono::Utc::now().timestamp_millis());

Duration

CqlDuration represents a duration with three components: months, days, and nanoseconds.
use scylla::value::CqlDuration;

// Create a duration: 2 months, 15 days, 4 hours
let duration = CqlDuration {
    months: 2,
    days: 15,
    nanoseconds: 4 * 3600 * 1_000_000_000,
};

session.query_unpaged(
    "INSERT INTO tasks (id, estimated_duration) VALUES (?, ?)",
    (1, duration),
).await?;

Using chrono (Optional)

The chrono crate provides more ergonomic date/time types. Enable the chrono-04 feature:
[dependencies]
scylla = { version = "*", features = ["chrono-04"] }
chrono = "0.4"

NaiveDate

use chrono::NaiveDate;

// Create a date
let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();

session.query_unpaged(
    "INSERT INTO events (id, event_date) VALUES (?, ?)",
    (1, date),
).await?;

// Read back
let result = session
    .query_unpaged("SELECT event_date FROM events WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let date: NaiveDate = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_cql_date())
        .and_then(|d| d.try_into().ok())
        .unwrap();
    println!("Date: {}", date.format("%Y-%m-%d"));
}

NaiveTime

use chrono::NaiveTime;

// Create a time
let time = NaiveTime::from_hms_opt(14, 30, 0).unwrap();

session.query_unpaged(
    "INSERT INTO schedule (id, start_time) VALUES (?, ?)",
    (1, time),
).await?;

DateTime<Utc>

use chrono::{DateTime, Utc};

// Current time
let now: DateTime<Utc> = Utc::now();

session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (1, now),
).await?;

// Specific timestamp
let timestamp = DateTime::parse_from_rfc3339("2024-01-15T14:30:00Z")
    .unwrap()
    .with_timezone(&Utc);

session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (2, timestamp),
).await?;

// Read back
let result = session
    .query_unpaged("SELECT created_at FROM logs WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let created_at: DateTime<Utc> = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_cql_timestamp())
        .and_then(|ts| ts.try_into().ok())
        .unwrap();
    println!("Created at: {}", created_at.format("%Y-%m-%d %H:%M:%S UTC"));
}

Using time (Optional)

The time crate is an alternative to chrono. Enable the time-03 feature:
[dependencies]
scylla = { version = "*", features = ["time-03"] }
time = "0.3"

Date

use time::{Date, Month};

// Create a date
let date = Date::from_calendar_date(2024, Month::January, 15).unwrap();

session.query_unpaged(
    "INSERT INTO events (id, event_date) VALUES (?, ?)",
    (1, date),
).await?;

Time

use time::Time;

// Create a time
let time = Time::from_hms(14, 30, 0).unwrap();

session.query_unpaged(
    "INSERT INTO schedule (id, start_time) VALUES (?, ?)",
    (1, time),
).await?;

OffsetDateTime

use time::OffsetDateTime;

// Current time
let now = OffsetDateTime::now_utc();

session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (1, now),
).await?;

Conversion Between Types

CqlDate ↔ chrono::NaiveDate

use scylla::value::CqlDate;
use chrono::NaiveDate;

// chrono to CqlDate
let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
let cql_date: CqlDate = date.into();

// CqlDate to chrono
let cql_date = CqlDate(2_u32.pow(31) + 18993);
let date: NaiveDate = cql_date.try_into().unwrap();

CqlTimestamp ↔ chrono::DateTime<Utc>

use scylla::value::CqlTimestamp;
use chrono::{DateTime, Utc};

// DateTime to CqlTimestamp
let dt = Utc::now();
let cql_ts: CqlTimestamp = dt.into();

// CqlTimestamp to DateTime
let cql_ts = CqlTimestamp(1640995200000);
let dt: DateTime<Utc> = cql_ts.try_into().unwrap();

Nullable Date/Time Values

use chrono::{DateTime, Utc};

// Nullable timestamp
let optional_time: Option<DateTime<Utc>> = Some(Utc::now());
session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (1, optional_time),
).await?;

// Null timestamp
let no_time: Option<DateTime<Utc>> = None;
session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (2, no_time),
).await?;

Working with Durations

Duration Components

use scylla::value::CqlDuration;

// 1 year, 2 months, 15 days, 4 hours, 30 minutes
let duration = CqlDuration {
    months: 14,  // 1 year + 2 months
    days: 15,
    nanoseconds: (4 * 3600 + 30 * 60) * 1_000_000_000,
};

println!("Months: {}", duration.months);
println!("Days: {}", duration.days);
println!("Hours: {}", duration.nanoseconds / 3_600_000_000_000);

Duration Arithmetic

use scylla::value::CqlDuration;

// Add durations
let d1 = CqlDuration { months: 1, days: 5, nanoseconds: 0 };
let d2 = CqlDuration { months: 0, days: 10, nanoseconds: 3600 * 1_000_000_000 };

let total = CqlDuration {
    months: d1.months + d2.months,
    days: d1.days + d2.days,
    nanoseconds: d1.nanoseconds + d2.nanoseconds,
};

Time Zones

CQL’s timestamp type is always stored as UTC. For timezone-aware operations:
use chrono::{DateTime, Utc, TimeZone, FixedOffset};

// Store as UTC
let local_tz = FixedOffset::east_opt(5 * 3600).unwrap(); // UTC+5
let local_time = local_tz.with_ymd_and_hms(2024, 1, 15, 14, 30, 0).unwrap();
let utc_time = local_time.with_timezone(&Utc);

session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (1, utc_time),
).await?;

// Read and convert back to local
let result = session
    .query_unpaged("SELECT created_at FROM logs WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let utc_time: DateTime<Utc> = /* deserialize */;
    let local_time = utc_time.with_timezone(&local_tz);
    println!("Local time: {}", local_time);
}

Common Patterns

Current Timestamp

use chrono::Utc;

// Using chrono
let now = Utc::now();
session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, ?)",
    (1, now),
).await?;

// Or using CQL's now() function
session.query_unpaged(
    "INSERT INTO logs (id, created_at) VALUES (?, now())",
    (1,),
).await?;

Date Range Queries

use chrono::{NaiveDate, Utc};

let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
let end_date = NaiveDate::from_ymd_opt(2024, 1, 31).unwrap();

let result = session
    .query_unpaged(
        "SELECT * FROM events WHERE event_date >= ? AND event_date < ?",
        (start_date, end_date),
    )
    .await?;

Time-Series Data with TTL

use chrono::Utc;

let timestamp = Utc::now();
let ttl_seconds = 86400; // 1 day

session.query_unpaged(
    "INSERT INTO sensor_data (sensor_id, timestamp, value) VALUES (?, ?, ?) USING TTL ?",
    (1, timestamp, 25.5, ttl_seconds),
).await?;

Formatting Timestamps

use chrono::{DateTime, Utc};

let timestamp: DateTime<Utc> = Utc::now();

// ISO 8601 format
println!("{}", timestamp.to_rfc3339());

// Custom format
println!("{}", timestamp.format("%Y-%m-%d %H:%M:%S"));

// Unix timestamp
println!("{}", timestamp.timestamp());
println!("{}", timestamp.timestamp_millis());

Best Practices

  1. Use timestamp for absolute times: Store events, creation times, and modification times as timestamps.
  2. Use date for calendar dates: Store birthdates, holidays, and other calendar-specific dates.
  3. Use duration for intervals: Store time intervals that need to account for variable month lengths.
  4. Always use UTC: Store timestamps in UTC and convert to local time zones in your application.
  5. Consider time precision: timestamp has millisecond precision, while time has nanosecond precision.
  6. Use TTL for time-series data: Automatically expire old data using CQL’s TTL feature.

See Also

Build docs developers (and LLMs) love