mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-21 07:55:18 +01:00
Merge 'Sqlean Time extension' from Pedro Muniz
This PR implements a sqlean time compatible extension. I would appreciate some help to review my code and see if there are ways to enhance it. Also, if there is some edge case, I have missed please tell me. https://github.com/nalgeon/sqlean/blob/main/docs/time.md Closes #854
This commit is contained in:
54
COMPAT.md
54
COMPAT.md
@@ -24,6 +24,7 @@ This document describes the compatibility of Limbo with SQLite.
|
||||
- [UUID](#uuid)
|
||||
- [regexp](#regexp)
|
||||
- [Vector](#vector)
|
||||
- [Time](#time)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -630,3 +631,56 @@ The `vector` extension is compatible with libSQL native vector search.
|
||||
| vector64(x) | Yes | |
|
||||
| vector_extract(x) | Yes | |
|
||||
| vector_distance_cos(x, y) | Yes | |
|
||||
|
||||
### Time
|
||||
|
||||
The `time` extension is compatible with [sqlean-time](https://github.com/nalgeon/sqlean/blob/main/docs/time.md).
|
||||
|
||||
|
||||
| Function | Status | Comment |
|
||||
| ------------------------------------------------------------------- | ------ | ---------------------------- |
|
||||
| time_now() | Yes | |
|
||||
| time_date(year, month, day[, hour, min, sec[, nsec[, offset_sec]]]) | Yes | offset_sec is not normalized |
|
||||
| time_get_year(t) | Yes | |
|
||||
| time_get_month(t) | Yes | |
|
||||
| time_get_day(t) | Yes | |
|
||||
| time_get_hour(t) | Yes | |
|
||||
| time_get_minute(t) | Yes | |
|
||||
| time_get_second(t) | Yes | |
|
||||
| time_get_nano(t) | Yes | |
|
||||
| time_get_weekday(t) | Yes | |
|
||||
| time_get_yearday(t) | Yes | |
|
||||
| time_get_isoyear(t) | Yes | |
|
||||
| time_get_isoweek(t) | Yes | |
|
||||
| time_get(t, field) | Yes | |
|
||||
| time_unix(sec[, nsec]) | Yes | |
|
||||
| time_milli(msec) | Yes | |
|
||||
| time_micro(usec) | Yes | |
|
||||
| time_nano(nsec) | Yes | |
|
||||
| time_to_unix(t) | Yes | |
|
||||
| time_to_milli(t) | Yes | |
|
||||
| time_to_micro(t) | Yes | |
|
||||
| time_to_nano(t) | Yes | |
|
||||
| time_after(t, u) | Yes | |
|
||||
| time_before(t, u) | Yes | |
|
||||
| time_compare(t, u) | Yes | |
|
||||
| time_equal(t, u) | Yes | |
|
||||
| time_add(t, d) | Yes | |
|
||||
| time_add_date(t, years[, months[, days]]) | Yes | |
|
||||
| time_sub(t, u) | Yes | |
|
||||
| time_since(t) | Yes | |
|
||||
| time_until(t) | Yes | |
|
||||
| time_trunc(t, field) | Yes | |
|
||||
| time_trunc(t, d) | Yes | |
|
||||
| time_round(t, d) | Yes | |
|
||||
| time_fmt_iso(t[, offset_sec]) | Yes | |
|
||||
| time_fmt_datetime(t[, offset_sec]) | Yes | |
|
||||
| time_fmt_date(t[, offset_sec]) | Yes | |
|
||||
| time_fmt_time(t[, offset_sec]) | Yes | |
|
||||
| time_parse(s) | Yes | |
|
||||
| dur_ns() | Yes | |
|
||||
| dur_us() | Yes | |
|
||||
| dur_ms() | Yes | |
|
||||
| dur_s() | Yes | |
|
||||
| dur_m() | Yes | |
|
||||
| dur_h() | Yes | |
|
||||
|
||||
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -1583,6 +1583,7 @@ dependencies = [
|
||||
"limbo_macros",
|
||||
"limbo_percentile",
|
||||
"limbo_regexp",
|
||||
"limbo_time",
|
||||
"limbo_uuid",
|
||||
"limbo_vector",
|
||||
"log",
|
||||
@@ -1678,6 +1679,18 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limbo_time"
|
||||
version = "0.0.13"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"limbo_ext",
|
||||
"mimalloc",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limbo_uuid"
|
||||
version = "0.0.14"
|
||||
|
||||
@@ -18,7 +18,8 @@ members = [
|
||||
"sqlite3",
|
||||
"tests",
|
||||
"extensions/percentile",
|
||||
"extensions/vector",
|
||||
"extensions/vector",
|
||||
"extensions/time",
|
||||
]
|
||||
exclude = ["perf/latency/limbo"]
|
||||
|
||||
|
||||
4
Makefile
4
Makefile
@@ -82,6 +82,10 @@ test-vector:
|
||||
SQLITE_EXEC=$(SQLITE_EXEC) ./testing/vector.test
|
||||
.PHONY: test-vector
|
||||
|
||||
test-time:
|
||||
SQLITE_EXEC=$(SQLITE_EXEC) ./testing/time.test
|
||||
.PHONY: test-time
|
||||
|
||||
test-sqlite3: limbo-c
|
||||
LIBS="$(SQLITE_LIB)" HEADERS="$(SQLITE_LIB_HEADERS)" make -C sqlite3/tests test
|
||||
.PHONY: test-sqlite3
|
||||
|
||||
@@ -14,7 +14,7 @@ name = "limbo_core"
|
||||
path = "lib.rs"
|
||||
|
||||
[features]
|
||||
default = ["fs", "json", "uuid", "vector", "io_uring"]
|
||||
default = ["fs", "json", "uuid", "vector", "io_uring", "time"]
|
||||
fs = []
|
||||
json = [
|
||||
"dep:jsonb",
|
||||
@@ -26,6 +26,7 @@ vector = ["limbo_vector/static"]
|
||||
io_uring = ["dep:io-uring", "rustix/io_uring"]
|
||||
percentile = ["limbo_percentile/static"]
|
||||
regexp = ["limbo_regexp/static"]
|
||||
time = ["limbo_time/static"]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
io-uring = { version = "0.6.1", optional = true }
|
||||
@@ -65,6 +66,7 @@ limbo_uuid = { path = "../extensions/uuid", optional = true, features = ["static
|
||||
limbo_vector = { path = "../extensions/vector", optional = true, features = ["static"] }
|
||||
limbo_regexp = { path = "../extensions/regexp", optional = true, features = ["static"] }
|
||||
limbo_percentile = { path = "../extensions/percentile", optional = true, features = ["static"] }
|
||||
limbo_time = { path = "../extensions/time", optional = true, features = ["static"] }
|
||||
miette = "7.4.0"
|
||||
strum = "0.26"
|
||||
|
||||
|
||||
@@ -92,6 +92,10 @@ impl Database {
|
||||
if unsafe { !limbo_regexp::register_extension_static(&ext_api).is_ok() } {
|
||||
return Err("Failed to register regexp extension".to_string());
|
||||
}
|
||||
#[cfg(feature = "time")]
|
||||
if unsafe { !limbo_time::register_extension_static(&ext_api).is_ok() } {
|
||||
return Err("Failed to register time extension".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
23
extensions/time/Cargo.toml
Normal file
23
extensions/time/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
name = "limbo_time"
|
||||
repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[features]
|
||||
static = ["limbo_ext/static"]
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
mimalloc = { version = "*", default-features = false }
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.39"
|
||||
limbo_ext = { path = "../core", features = ["static"] }
|
||||
strum = "0.26.3"
|
||||
strum_macros = "0.26.3"
|
||||
thiserror = "2.0.11"
|
||||
1019
extensions/time/src/lib.rs
Normal file
1019
extensions/time/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
560
extensions/time/src/time.rs
Normal file
560
extensions/time/src/time.rs
Normal file
@@ -0,0 +1,560 @@
|
||||
use std::ops::{Deref, Sub};
|
||||
|
||||
use chrono::{self, DateTime, Timelike, Utc};
|
||||
use chrono::{prelude::*, DurationRound};
|
||||
|
||||
use limbo_ext::Value;
|
||||
|
||||
use crate::{Result, TimeError};
|
||||
|
||||
const DAYS_BEFORE_EPOCH: i64 = 719162;
|
||||
const TIME_BLOB_SIZE: usize = 13;
|
||||
const VERSION: u8 = 1;
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd, Eq)]
|
||||
pub struct Time {
|
||||
inner: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd)]
|
||||
pub struct Duration {
|
||||
inner: chrono::Duration,
|
||||
}
|
||||
|
||||
#[derive(strum_macros::Display, strum_macros::EnumString)]
|
||||
pub enum TimeField {
|
||||
#[strum(to_string = "millennium")]
|
||||
Millennium,
|
||||
#[strum(to_string = "century")]
|
||||
Century,
|
||||
#[strum(to_string = "decade")]
|
||||
Decade,
|
||||
#[strum(to_string = "year")]
|
||||
Year,
|
||||
#[strum(to_string = "quarter")]
|
||||
Quarter,
|
||||
#[strum(to_string = "month")]
|
||||
Month,
|
||||
#[strum(to_string = "day")]
|
||||
Day,
|
||||
#[strum(to_string = "hour")]
|
||||
Hour,
|
||||
#[strum(to_string = "minute")]
|
||||
Minute,
|
||||
#[strum(to_string = "second")]
|
||||
Second,
|
||||
#[strum(to_string = "millisecond")]
|
||||
MilliSecond,
|
||||
#[strum(to_string = "milli")]
|
||||
Milli,
|
||||
#[strum(to_string = "microsecond")]
|
||||
MicroSecond,
|
||||
#[strum(to_string = "micro")]
|
||||
Micro,
|
||||
#[strum(to_string = "nanosecond")]
|
||||
NanoSecond,
|
||||
#[strum(to_string = "nano")]
|
||||
Nano,
|
||||
#[strum(to_string = "isoyear")]
|
||||
IsoYear,
|
||||
#[strum(to_string = "isoweek")]
|
||||
IsoWeek,
|
||||
#[strum(to_string = "isodow")]
|
||||
IsoDow,
|
||||
#[strum(to_string = "yearday")]
|
||||
YearDay,
|
||||
#[strum(to_string = "weekday")]
|
||||
WeekDay,
|
||||
#[strum(to_string = "epoch")]
|
||||
Epoch,
|
||||
}
|
||||
|
||||
#[derive(strum_macros::Display, strum_macros::EnumString)]
|
||||
pub enum TimeRoundField {
|
||||
#[strum(to_string = "millennium")]
|
||||
Millennium,
|
||||
#[strum(to_string = "century")]
|
||||
Century,
|
||||
#[strum(to_string = "decade")]
|
||||
Decade,
|
||||
#[strum(to_string = "year")]
|
||||
Year,
|
||||
#[strum(to_string = "quarter")]
|
||||
Quarter,
|
||||
#[strum(to_string = "month")]
|
||||
Month,
|
||||
#[strum(to_string = "week")]
|
||||
Week,
|
||||
#[strum(to_string = "day")]
|
||||
Day,
|
||||
#[strum(to_string = "hour")]
|
||||
Hour,
|
||||
#[strum(to_string = "minute")]
|
||||
Minute,
|
||||
#[strum(to_string = "second")]
|
||||
Second,
|
||||
#[strum(to_string = "millisecond")]
|
||||
MilliSecond,
|
||||
#[strum(to_string = "milli")]
|
||||
Milli,
|
||||
#[strum(to_string = "microsecond")]
|
||||
MicroSecond,
|
||||
#[strum(to_string = "micro")]
|
||||
Micro,
|
||||
}
|
||||
|
||||
impl Time {
|
||||
/// Returns a new instance of Time with tracking UTC::now
|
||||
pub fn new() -> Self {
|
||||
Self { inner: Utc::now() }
|
||||
}
|
||||
|
||||
pub fn into_blob(self) -> Value {
|
||||
let blob: [u8; 13] = self.into();
|
||||
Value::from_blob(blob.to_vec())
|
||||
}
|
||||
|
||||
pub fn fmt_iso(&self, offset_sec: i32) -> Result<String> {
|
||||
if offset_sec == 0 {
|
||||
if self.inner.nanosecond() == 0 {
|
||||
return Ok(self.inner.format("%FT%TZ").to_string());
|
||||
} else {
|
||||
return Ok(self.inner.format("%FT%T%.9fZ").to_string());
|
||||
}
|
||||
}
|
||||
// I do not see how this can error
|
||||
let offset = &FixedOffset::east_opt(offset_sec).ok_or(TimeError::InvalidFormat)?;
|
||||
|
||||
let timezone_date = self.inner.with_timezone(offset);
|
||||
|
||||
if timezone_date.nanosecond() == 0 {
|
||||
Ok(timezone_date.format("%FT%T%:z").to_string())
|
||||
} else {
|
||||
Ok(timezone_date.format("%FT%T%.9f%:z").to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt_datetime(&self, offset_sec: i32) -> Result<String> {
|
||||
let fmt = "%F %T";
|
||||
|
||||
if offset_sec == 0 {
|
||||
return Ok(self.inner.format(fmt).to_string());
|
||||
}
|
||||
// I do not see how this can error
|
||||
let offset = &FixedOffset::east_opt(offset_sec).ok_or(TimeError::InvalidFormat)?;
|
||||
|
||||
let timezone_date = self.inner.with_timezone(offset);
|
||||
|
||||
Ok(timezone_date.format(fmt).to_string())
|
||||
}
|
||||
|
||||
pub fn fmt_date(&self, offset_sec: i32) -> Result<String> {
|
||||
let fmt = "%F";
|
||||
|
||||
if offset_sec == 0 {
|
||||
return Ok(self.inner.format(fmt).to_string());
|
||||
}
|
||||
// I do not see how this can error
|
||||
let offset = &FixedOffset::east_opt(offset_sec).ok_or(TimeError::InvalidFormat)?;
|
||||
|
||||
let timezone_date = self.inner.with_timezone(offset);
|
||||
|
||||
Ok(timezone_date.format(fmt).to_string())
|
||||
}
|
||||
|
||||
pub fn fmt_time(&self, offset_sec: i32) -> Result<String> {
|
||||
let fmt = "%T";
|
||||
|
||||
if offset_sec == 0 {
|
||||
return Ok(self.inner.format(fmt).to_string());
|
||||
}
|
||||
// I do not see how this can error
|
||||
let offset = &FixedOffset::east_opt(offset_sec).ok_or(TimeError::InvalidFormat)?;
|
||||
|
||||
let timezone_date = self.inner.with_timezone(offset);
|
||||
|
||||
Ok(timezone_date.format(fmt).to_string())
|
||||
}
|
||||
|
||||
/// Adjust the datetime to the offset
|
||||
pub fn from_datetime(dt: DateTime<Utc>) -> Self {
|
||||
Self { inner: dt }
|
||||
}
|
||||
|
||||
//
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn time_date(
|
||||
year: i32,
|
||||
month: i32,
|
||||
day: i64,
|
||||
hour: i64,
|
||||
minutes: i64,
|
||||
seconds: i64,
|
||||
nano_secs: i64,
|
||||
offset: FixedOffset,
|
||||
) -> Result<Self> {
|
||||
let mut dt: NaiveDateTime = NaiveDate::from_ymd_opt(1, 1, 1)
|
||||
.unwrap()
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.unwrap();
|
||||
|
||||
match year.cmp(&0) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
dt = dt
|
||||
.checked_add_months(chrono::Months::new((year - 1).unsigned_abs() * 12))
|
||||
.ok_or(TimeError::CreationError)?
|
||||
}
|
||||
std::cmp::Ordering::Less => {
|
||||
dt = dt
|
||||
.checked_sub_months(chrono::Months::new((year - 1).unsigned_abs() * 12))
|
||||
.ok_or(TimeError::CreationError)?
|
||||
}
|
||||
std::cmp::Ordering::Equal => (),
|
||||
};
|
||||
|
||||
match month.cmp(&0) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
dt = dt
|
||||
.checked_add_months(chrono::Months::new((month - 1).unsigned_abs()))
|
||||
.ok_or(TimeError::CreationError)?
|
||||
}
|
||||
std::cmp::Ordering::Less => {
|
||||
dt = dt
|
||||
.checked_sub_months(chrono::Months::new((month - 1).unsigned_abs()))
|
||||
.ok_or(TimeError::CreationError)?
|
||||
}
|
||||
std::cmp::Ordering::Equal => (),
|
||||
};
|
||||
|
||||
dt += chrono::Duration::try_days(day - 1).ok_or(TimeError::CreationError)?;
|
||||
|
||||
dt += chrono::Duration::try_hours(hour).ok_or(TimeError::CreationError)?;
|
||||
dt += chrono::Duration::try_minutes(minutes).ok_or(TimeError::CreationError)?;
|
||||
dt += chrono::Duration::try_seconds(seconds).ok_or(TimeError::CreationError)?;
|
||||
|
||||
dt += chrono::Duration::nanoseconds(nano_secs);
|
||||
|
||||
dt = dt
|
||||
.and_local_timezone(offset)
|
||||
.single()
|
||||
.ok_or(TimeError::CreationError)?
|
||||
.naive_utc();
|
||||
|
||||
Ok(dt.into())
|
||||
}
|
||||
|
||||
pub fn time_add_date(self, years: i32, months: i32, days: i64) -> Result<Self> {
|
||||
let mut dt: NaiveDateTime = self.into();
|
||||
|
||||
match years.cmp(&0) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
dt = dt
|
||||
.checked_add_months(chrono::Months::new(years.unsigned_abs() * 12))
|
||||
.ok_or(TimeError::CreationError)?;
|
||||
}
|
||||
std::cmp::Ordering::Less => {
|
||||
dt = dt
|
||||
.checked_sub_months(chrono::Months::new(years.unsigned_abs() * 12))
|
||||
.ok_or(TimeError::CreationError)?;
|
||||
}
|
||||
std::cmp::Ordering::Equal => (),
|
||||
};
|
||||
|
||||
match months.cmp(&0) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
dt = dt
|
||||
.checked_add_months(chrono::Months::new(months.unsigned_abs()))
|
||||
.ok_or(TimeError::CreationError)?
|
||||
}
|
||||
std::cmp::Ordering::Less => {
|
||||
dt = dt
|
||||
.checked_sub_months(chrono::Months::new(months.unsigned_abs()))
|
||||
.ok_or(TimeError::CreationError)?
|
||||
}
|
||||
std::cmp::Ordering::Equal => (),
|
||||
};
|
||||
|
||||
dt += chrono::Duration::try_days(days).ok_or(TimeError::CreationError)?;
|
||||
|
||||
Ok(dt.into())
|
||||
}
|
||||
|
||||
pub fn get_second(&self) -> i64 {
|
||||
self.inner.second() as i64
|
||||
}
|
||||
|
||||
pub fn get_nanosecond(&self) -> i64 {
|
||||
self.inner.timestamp_subsec_nanos() as i64
|
||||
}
|
||||
|
||||
pub fn to_unix(&self) -> i64 {
|
||||
self.inner.timestamp()
|
||||
}
|
||||
|
||||
pub fn to_unix_milli(&self) -> i64 {
|
||||
self.inner.timestamp_millis()
|
||||
}
|
||||
|
||||
pub fn to_unix_micro(&self) -> i64 {
|
||||
self.inner.timestamp_micros()
|
||||
}
|
||||
|
||||
pub fn to_unix_nano(&self) -> Option<i64> {
|
||||
self.inner.timestamp_nanos_opt()
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, d: Duration) -> Self {
|
||||
Self {
|
||||
inner: self.inner + d.inner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, d: Duration) -> Self {
|
||||
Self {
|
||||
inner: self.inner - d.inner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trunc_duration(&self, d: Duration) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: self.inner.duration_trunc(d.inner)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn trunc_field(&self, field: TimeRoundField) -> Result<Self> {
|
||||
use TimeRoundField::*;
|
||||
|
||||
let year: i32;
|
||||
let mut month: i32 = 1;
|
||||
let mut week: i32 = 0;
|
||||
let mut day: i64 = 1;
|
||||
let mut hour: i64 = 0;
|
||||
let mut minutes: i64 = 0;
|
||||
let mut seconds: i64 = 0;
|
||||
let mut nano_secs: i64 = 0;
|
||||
let offset = FixedOffset::east_opt(0).unwrap(); // UTC
|
||||
|
||||
match field {
|
||||
Millennium => {
|
||||
let millennium = (self.inner.year() / 1000) * 1000;
|
||||
year = millennium;
|
||||
}
|
||||
Century => {
|
||||
let century = (self.inner.year() / 100) * 100;
|
||||
year = century;
|
||||
}
|
||||
Decade => {
|
||||
let decade = (self.inner.year() / 10) * 10;
|
||||
year = decade;
|
||||
}
|
||||
Year => {
|
||||
year = self.inner.year();
|
||||
}
|
||||
Quarter => {
|
||||
let quarter = ((self.inner.month() - 1) / 3) as i32;
|
||||
year = self.inner.year();
|
||||
month = (quarter * 3) + 1;
|
||||
}
|
||||
Month => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
}
|
||||
Week => {
|
||||
let isoweek = self.inner.iso_week();
|
||||
year = isoweek.year();
|
||||
week = isoweek.week() as i32;
|
||||
}
|
||||
Day => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
day = self.inner.day() as i64;
|
||||
}
|
||||
Hour => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
day = self.inner.day() as i64;
|
||||
hour = self.inner.hour() as i64;
|
||||
}
|
||||
Minute => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
day = self.inner.day() as i64;
|
||||
hour = self.inner.hour() as i64;
|
||||
minutes = self.inner.minute() as i64;
|
||||
}
|
||||
Second => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
day = self.inner.day() as i64;
|
||||
hour = self.inner.hour() as i64;
|
||||
minutes = self.inner.minute() as i64;
|
||||
seconds = self.inner.second() as i64;
|
||||
}
|
||||
MilliSecond | Milli => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
day = self.inner.day() as i64;
|
||||
hour = self.inner.hour() as i64;
|
||||
minutes = self.inner.minute() as i64;
|
||||
seconds = self.inner.second() as i64;
|
||||
nano_secs = (self.inner.nanosecond() / 1_000_000 * 1_000_000) as i64;
|
||||
}
|
||||
MicroSecond | Micro => {
|
||||
year = self.inner.year();
|
||||
month = self.inner.month() as i32;
|
||||
day = self.inner.day() as i64;
|
||||
hour = self.inner.hour() as i64;
|
||||
minutes = self.inner.minute() as i64;
|
||||
seconds = self.inner.second() as i64;
|
||||
nano_secs = (self.inner.nanosecond() / 1_000 * 1_000) as i64;
|
||||
}
|
||||
};
|
||||
|
||||
let mut ret = Self::time_date(year, month, day, hour, minutes, seconds, nano_secs, offset)?;
|
||||
|
||||
// Means we have to adjust for the week
|
||||
if week != 0 {
|
||||
ret = ret.time_add_date(0, 0, ((week - 1) * 7) as i64)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn round_duration(&self, d: Duration) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: self.inner.duration_round(d.inner)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn time_get(&self, field: TimeField) -> Value {
|
||||
use TimeField::*;
|
||||
|
||||
match field {
|
||||
Millennium => Value::from_integer((self.inner.year() / 1000) as i64),
|
||||
Century => Value::from_integer((self.inner.year() / 100) as i64),
|
||||
Decade => Value::from_integer((self.inner.year() / 10) as i64),
|
||||
Year => Value::from_integer(self.inner.year() as i64),
|
||||
Quarter => Value::from_integer(self.inner.month().div_ceil(3) as i64),
|
||||
Month => Value::from_integer(self.inner.month() as i64),
|
||||
Day => Value::from_integer(self.inner.day() as i64),
|
||||
Hour => Value::from_integer(self.inner.hour() as i64),
|
||||
Minute => Value::from_integer(self.inner.minute() as i64),
|
||||
Second => Value::from_float(
|
||||
self.inner.second() as f64 + (self.inner.nanosecond() as f64) / (1_000_000_000_f64),
|
||||
),
|
||||
MilliSecond | Milli => {
|
||||
Value::from_integer((self.inner.nanosecond() / 1_000_000 % 1_000) as i64)
|
||||
}
|
||||
MicroSecond | Micro => {
|
||||
Value::from_integer((self.inner.nanosecond() / 1_000 % 1_000_000) as i64)
|
||||
}
|
||||
NanoSecond | Nano => {
|
||||
Value::from_integer((self.inner.nanosecond() % 1_000_000_000) as i64)
|
||||
}
|
||||
IsoYear => Value::from_integer(self.inner.iso_week().year() as i64),
|
||||
IsoWeek => Value::from_integer(self.inner.iso_week().week() as i64),
|
||||
IsoDow => Value::from_integer(self.inner.weekday().days_since(Weekday::Sun) as i64),
|
||||
YearDay => Value::from_integer(self.inner.ordinal() as i64),
|
||||
WeekDay => Value::from_integer(self.inner.weekday().num_days_from_sunday() as i64),
|
||||
Epoch => Value::from_float(
|
||||
self.inner.timestamp() as f64 + self.inner.nanosecond() as f64 / 1_000_000_000_f64,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Time> for [u8; TIME_BLOB_SIZE] {
|
||||
fn from(value: Time) -> Self {
|
||||
let mut blob = [0u8; 13];
|
||||
|
||||
let seconds = value.inner.timestamp() + (3600 * 24 * DAYS_BEFORE_EPOCH);
|
||||
let nanoseconds = value.inner.timestamp_subsec_nanos();
|
||||
|
||||
blob[0] = VERSION;
|
||||
blob[1] = (seconds >> 56) as u8; // bytes 1-8: seconds
|
||||
blob[2] = (seconds >> 48) as u8;
|
||||
blob[3] = (seconds >> 40) as u8;
|
||||
blob[4] = (seconds >> 32) as u8;
|
||||
blob[5] = (seconds >> 24) as u8;
|
||||
blob[6] = (seconds >> 16) as u8;
|
||||
blob[7] = (seconds >> 8) as u8;
|
||||
blob[8] = seconds as u8;
|
||||
blob[9] = (nanoseconds >> 24) as u8; // bytes 9-12: nanoseconds
|
||||
blob[10] = (nanoseconds >> 16) as u8;
|
||||
blob[11] = (nanoseconds >> 8) as u8;
|
||||
blob[12] = (nanoseconds) as u8;
|
||||
|
||||
blob
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for Time {
|
||||
type Error = TimeError;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Time> {
|
||||
if value.len() != TIME_BLOB_SIZE {
|
||||
return Err(TimeError::InvalidSize);
|
||||
}
|
||||
if value[0] != VERSION {
|
||||
return Err(TimeError::MismatchVersion);
|
||||
}
|
||||
|
||||
let seconds = value[8] as i64
|
||||
| (value[7] as i64) << 8
|
||||
| (value[6] as i64) << 16
|
||||
| (value[5] as i64) << 24
|
||||
| (value[4] as i64) << 32
|
||||
| (value[3] as i64) << 40
|
||||
| (value[2] as i64) << 48
|
||||
| (value[1] as i64) << 56;
|
||||
|
||||
let nanoseconds = value[12] as u32
|
||||
| (value[11] as u32) << 8
|
||||
| (value[10] as u32) << 16
|
||||
| (value[9] as u32) << 24;
|
||||
|
||||
Ok(Self {
|
||||
inner: DateTime::from_timestamp(seconds - (3600 * 24 * DAYS_BEFORE_EPOCH), nanoseconds)
|
||||
.ok_or(TimeError::InvalidFormat)?
|
||||
.to_utc(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NaiveDateTime> for Time {
|
||||
fn from(value: NaiveDateTime) -> Self {
|
||||
Self {
|
||||
inner: value.and_utc(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Time> for NaiveDateTime {
|
||||
fn from(value: Time) -> Self {
|
||||
value.inner.naive_utc()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Time {
|
||||
type Output = Duration;
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Duration {
|
||||
inner: self.inner - rhs.inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Duration {
|
||||
fn from(value: i64) -> Self {
|
||||
Self {
|
||||
inner: chrono::Duration::nanoseconds(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Duration {
|
||||
type Target = chrono::Duration;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
401
testing/time.test
Executable file
401
testing/time.test
Executable file
@@ -0,0 +1,401 @@
|
||||
#!/usr/bin/env tclsh
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test time_date {
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18));
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35));
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 666777888));
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 0, 3*3600));
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 666777888, 3*3600));
|
||||
} {
|
||||
{2011-11-18T00:00:00Z}
|
||||
{2011-11-18T15:56:35Z}
|
||||
{2011-11-18T15:56:35.666777888Z}
|
||||
{2011-11-18T12:56:35Z}
|
||||
{2011-11-18T12:56:35.666777888Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_year {
|
||||
SELECT time_get_year(time_date(2011, 11, 18));
|
||||
SELECT time_get_year(time_date(1842, 11, 18));
|
||||
SELECT time_get_year(time_date(-1000, 11, 18));
|
||||
} {
|
||||
{2011}
|
||||
{1842}
|
||||
{-1000}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_month {
|
||||
SELECT time_get_month(time_date(2011, 12, 18));
|
||||
SELECT time_get_month(time_date(1842, 1, 18));
|
||||
SELECT time_get_month(time_date(-1000, 5, 18));
|
||||
} {
|
||||
{12}
|
||||
{1}
|
||||
{5}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_day {
|
||||
SELECT time_get_day(time_date(2011, 12, 31));
|
||||
SELECT time_get_day(time_date(1842, 1, 10));
|
||||
SELECT time_get_day(time_date(-1000, 5, 25));
|
||||
} {
|
||||
{31}
|
||||
{10}
|
||||
{25}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_hour {
|
||||
SELECT time_get_hour(time_date(2011, 12, 31, 10, 5, 30));
|
||||
} {
|
||||
{10}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_minute {
|
||||
SELECT time_get_minute(time_date(2011, 12, 31, 10, 5, 30));
|
||||
} {
|
||||
{5}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_second {
|
||||
SELECT time_get_second(time_date(2011, 12, 31, 10, 5, 30, 431295000));
|
||||
} {
|
||||
{30}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_nanosecond {
|
||||
SELECT time_get_nano(time_date(2011, 12, 31, 10, 5, 30, 431295000));
|
||||
} {
|
||||
{431295000}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_weekday {
|
||||
SELECT time_get_weekday(time_date(2011, 12, 31, 10, 5, 30, 431295000));
|
||||
SELECT time_get_weekday(time_date(2012, 01, 01, 10, 5, 30, 431295000));
|
||||
} {
|
||||
{6}
|
||||
{0}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_yearday {
|
||||
SELECT time_get_yearday(time_date(2011, 12, 31, 10, 5, 30, 431295000));
|
||||
SELECT time_get_yearday(time_date(2012, 01, 01, 10, 5, 30, 431295000));
|
||||
} {
|
||||
{365}
|
||||
{1}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_isoyear {
|
||||
SELECT time_get_isoyear(time_date(2011, 12, 31, 10, 5, 30, 431295000));
|
||||
SELECT time_get_isoyear(time_date(2012, 01, 01, 10, 5, 30, 431295000));
|
||||
} {
|
||||
{2011}
|
||||
{2011}
|
||||
}
|
||||
|
||||
do_execsql_test time_get_isoweek {
|
||||
SELECT time_get_isoweek(time_date(2011, 12, 31, 10, 5, 30, 431295000));
|
||||
SELECT time_get_isoweek(time_date(2012, 01, 01, 10, 5, 30, 431295000));
|
||||
SELECT time_get_isoweek(time_date(2012, 01, 10, 10, 5, 30, 431295000));
|
||||
} {
|
||||
{52}
|
||||
{52}
|
||||
{2}
|
||||
}
|
||||
|
||||
do_execsql_test time_get {
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'millennium');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'century');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'decade');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'year');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'quarter');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'month');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'day');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'hour');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'minute');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'second');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'milli');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'millisecond');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'micro');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'microsecond');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'nano');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'nanosecond');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'isoyear');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'isoweek');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'isodow');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'yearday');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'weekday');
|
||||
SELECT time_get(time_date(2024, 8, 6, 21, 22, 15, 431295000), 'epoch');
|
||||
} {
|
||||
{2}
|
||||
{20}
|
||||
{202}
|
||||
{2024}
|
||||
{3}
|
||||
{8}
|
||||
{6}
|
||||
{21}
|
||||
{22}
|
||||
{15.431295}
|
||||
{431}
|
||||
{431}
|
||||
{431295}
|
||||
{431295}
|
||||
{431295000}
|
||||
{431295000}
|
||||
{2024}
|
||||
{32}
|
||||
{2}
|
||||
{219}
|
||||
{2}
|
||||
{1722979335.431295}
|
||||
}
|
||||
|
||||
do_execsql_test time_unix {
|
||||
SELECT time_fmt_iso(time_unix(1321631795));
|
||||
SELECT time_fmt_iso(time_unix(1321631795, 666777888));
|
||||
} {
|
||||
{2011-11-18T15:56:35Z}
|
||||
{2011-11-18T15:56:35.666777888Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_milli {
|
||||
SELECT time_fmt_iso(time_milli(1321631795666));
|
||||
} {
|
||||
{2011-11-18T15:56:35.666000000Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_micro {
|
||||
SELECT time_fmt_iso(time_micro(1321631795666777));
|
||||
} {
|
||||
{2011-11-18T15:56:35.666777000Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_nano {
|
||||
SELECT time_fmt_iso(time_nano(1321631795666777888));
|
||||
} {
|
||||
{2011-11-18T15:56:35.666777888Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_to_milli {
|
||||
SELECT time_fmt_iso(time_milli(time_to_milli(time_date(2025, 01, 01))));
|
||||
} {
|
||||
{2025-01-01T00:00:00Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_to_micro {
|
||||
SELECT time_fmt_iso(time_micro(time_to_micro(time_date(2025, 01, 01))));
|
||||
} {
|
||||
{2025-01-01T00:00:00Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_to_nano {
|
||||
SELECT time_fmt_iso(time_nano(time_to_nano(time_date(2025, 01, 01))));
|
||||
} {
|
||||
{2025-01-01T00:00:00Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_after {
|
||||
SELECT time_after(time_date(2025, 10, 10), time_date(2011, 11, 18));
|
||||
} {
|
||||
{1}
|
||||
}
|
||||
|
||||
do_execsql_test time_before {
|
||||
SELECT time_before(time_date(2025, 10, 10), time_date(2011, 11, 18));
|
||||
} {
|
||||
{0}
|
||||
}
|
||||
|
||||
do_execsql_test time_compare {
|
||||
SELECT time_compare(time_date(2025, 10, 10), time_date(2011, 11, 18));
|
||||
SELECT time_compare(time_date(2025, 10, 10), time_date(2026, 11, 18));
|
||||
SELECT time_compare(time_date(2025, 10, 10), time_date(2025, 10, 10));
|
||||
} {
|
||||
{1}
|
||||
{-1}
|
||||
{0}
|
||||
}
|
||||
|
||||
do_execsql_test time_add {
|
||||
SELECT time_fmt_iso(time_add(time_date(2025, 01, 01), 24*dur_h()));
|
||||
SELECT time_fmt_iso(time_add(time_date(2025, 01, 01), 60*dur_m()));
|
||||
SELECT time_fmt_iso(time_add(time_date(2025, 01, 01), 5*dur_m()+30*dur_s()));
|
||||
SELECT time_fmt_iso(date_add(time_date(2025, 01, 01), 24*dur_h()));
|
||||
SELECT time_fmt_iso(date_add(time_date(2025, 01, 01), 60*dur_m()));
|
||||
SELECT time_fmt_iso(date_add(time_date(2025, 01, 01), 5*dur_m()+30*dur_s()));
|
||||
} {
|
||||
{2025-01-02T00:00:00Z}
|
||||
{2025-01-01T01:00:00Z}
|
||||
{2025-01-01T00:05:30Z}
|
||||
{2025-01-02T00:00:00Z}
|
||||
{2025-01-01T01:00:00Z}
|
||||
{2025-01-01T00:05:30Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_add_date {
|
||||
SELECT time_fmt_date(time_add_date(time_date(2011, 11, 18), 5));
|
||||
SELECT time_fmt_date(time_add_date(time_date(2011, 11, 18), 3, 5));
|
||||
SELECT time_fmt_date(time_add_date(time_date(2011, 11, 18), 3, 5, -10));
|
||||
} {
|
||||
{2016-11-18}
|
||||
{2015-04-18}
|
||||
{2015-04-08}
|
||||
}
|
||||
|
||||
do_execsql_test time_sub {
|
||||
SELECT time_sub(time_date(2011, 11, 19), time_date(2011, 11, 18));
|
||||
SELECT time_sub(
|
||||
time_date(2011, 11, 18, 16, 56, 35),
|
||||
time_date(2011, 11, 18, 15, 56, 35)
|
||||
);
|
||||
SELECT time_sub(time_unix(1321631795, 5000000), time_unix(1321631795, 0));
|
||||
} {
|
||||
{86400000000000}
|
||||
{3600000000000}
|
||||
{5000000}
|
||||
}
|
||||
|
||||
# time_since and time_until functions rely on time_now so it makes testing for these functions near impossible
|
||||
|
||||
do_execsql_test time_sub {
|
||||
select 'original = ' || time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 666777888));
|
||||
select 'millennium = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'millennium'));
|
||||
select 'century = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'century'));
|
||||
select 'decade = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'decade'));
|
||||
select 'year = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'year'));
|
||||
select 'quarter = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'quarter'));
|
||||
select 'month = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'month'));
|
||||
select 'week = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'week'));
|
||||
select 'day = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'day'));
|
||||
select 'hour = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'hour'));
|
||||
select 'minute = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'minute'));
|
||||
select 'second = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'second'));
|
||||
select 'milli = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'milli'));
|
||||
select 'micro = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 'micro'));
|
||||
select '12h = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 12*dur_h()));
|
||||
select '1h = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), dur_h()));
|
||||
select '30m = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 30*dur_m()));
|
||||
select '1m = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), dur_m()));
|
||||
select '30s = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), 30*dur_s()));
|
||||
select '1s = ' || time_fmt_iso(time_trunc(time_date(2011, 11, 18, 15, 56, 35, 666777888), dur_s()));
|
||||
} {
|
||||
{original = 2011-11-18T15:56:35.666777888Z}
|
||||
{millennium = 2000-01-01T00:00:00Z}
|
||||
{century = 2000-01-01T00:00:00Z}
|
||||
{decade = 2010-01-01T00:00:00Z}
|
||||
{year = 2011-01-01T00:00:00Z}
|
||||
{quarter = 2011-10-01T00:00:00Z}
|
||||
{month = 2011-11-01T00:00:00Z}
|
||||
{week = 2011-11-12T00:00:00Z}
|
||||
{day = 2011-11-18T00:00:00Z}
|
||||
{hour = 2011-11-18T15:00:00Z}
|
||||
{minute = 2011-11-18T15:56:00Z}
|
||||
{second = 2011-11-18T15:56:35Z}
|
||||
{milli = 2011-11-18T15:56:35.666000000Z}
|
||||
{micro = 2011-11-18T15:56:35.666777000Z}
|
||||
{12h = 2011-11-18T12:00:00Z}
|
||||
{1h = 2011-11-18T15:00:00Z}
|
||||
{30m = 2011-11-18T15:30:00Z}
|
||||
{1m = 2011-11-18T15:56:00Z}
|
||||
{30s = 2011-11-18T15:56:30Z}
|
||||
{1s = 2011-11-18T15:56:35Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_sub {
|
||||
SELECT '12h = ' || time_fmt_iso(time_round(time_date(2011, 11, 18, 15, 56, 35, 666777888), 12*dur_h()));
|
||||
SELECT '1h = ' || time_fmt_iso(time_round(time_date(2011, 11, 18, 15, 56, 35, 666777888), dur_h()));
|
||||
SELECT '30m = ' || time_fmt_iso(time_round(time_date(2011, 11, 18, 15, 56, 35, 666777888), 30*dur_m()));
|
||||
SELECT '1m = ' || time_fmt_iso(time_round(time_date(2011, 11, 18, 15, 56, 35, 666777888), dur_m()));
|
||||
SELECT '30s = ' || time_fmt_iso(time_round(time_date(2011, 11, 18, 15, 56, 35, 666777888), 30*dur_s()));
|
||||
SELECT '1s = ' || time_fmt_iso(time_round(time_date(2011, 11, 18, 15, 56, 35, 666777888), dur_s()));
|
||||
} {
|
||||
{12h = 2011-11-18T12:00:00Z}
|
||||
{1h = 2011-11-18T16:00:00Z}
|
||||
{30m = 2011-11-18T16:00:00Z}
|
||||
{1m = 2011-11-18T15:57:00Z}
|
||||
{30s = 2011-11-18T15:56:30Z}
|
||||
{1s = 2011-11-18T15:56:36Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_fmt_iso {
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 666777888), 3*3600);
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 666777888));
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35), 3*3600);
|
||||
SELECT time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35));
|
||||
} {
|
||||
{2011-11-18T18:56:35.666777888+03:00}
|
||||
{2011-11-18T15:56:35.666777888Z}
|
||||
{2011-11-18T18:56:35+03:00}
|
||||
{2011-11-18T15:56:35Z}
|
||||
}
|
||||
|
||||
do_execsql_test time_fmt_datetime {
|
||||
SELECT time_fmt_datetime(time_date(2011, 11, 18, 15, 56, 35), 3*3600);
|
||||
SELECT time_fmt_datetime(time_date(2011, 11, 18, 15, 56, 35));
|
||||
SELECT time_fmt_datetime(time_date(2011, 11, 18));
|
||||
} {
|
||||
{2011-11-18 18:56:35}
|
||||
{2011-11-18 15:56:35}
|
||||
{2011-11-18 00:00:00}
|
||||
}
|
||||
|
||||
do_execsql_test time_fmt_date {
|
||||
SELECT time_fmt_date(time_date(2011, 11, 18, 15, 56, 35), 12*3600);
|
||||
SELECT time_fmt_date(time_date(2011, 11, 18, 15, 56, 35));
|
||||
SELECT time_fmt_date(time_date(2011, 11, 18));
|
||||
} {
|
||||
{2011-11-19}
|
||||
{2011-11-18}
|
||||
{2011-11-18}
|
||||
}
|
||||
|
||||
do_execsql_test time_fmt_time {
|
||||
SELECT time_fmt_time(time_date(2011, 11, 18, 15, 56, 35), 3*3600);
|
||||
SELECT time_fmt_time(time_date(2011, 11, 18, 15, 56, 35));
|
||||
SELECT time_fmt_time(time_date(2011, 11, 18));
|
||||
} {
|
||||
{18:56:35}
|
||||
{15:56:35}
|
||||
{00:00:00}
|
||||
}
|
||||
|
||||
do_execsql_test time_parse {
|
||||
SELECT time_parse('2011-11-18T15:56:35.666777888Z') = time_unix(1321631795, 666777888);
|
||||
SELECT time_parse('2011-11-18T19:26:35.666777888+03:30') = time_unix(1321631795, 666777888);
|
||||
SELECT time_parse('2011-11-18T12:26:35.666777888-03:30') = time_unix(1321631795, 666777888);
|
||||
SELECT time_parse('2011-11-18T15:56:35Z') = time_unix(1321631795, 0);
|
||||
SELECT time_parse('2011-11-18T19:26:35+03:30') = time_unix(1321631795, 0);
|
||||
SELECT time_parse('2011-11-18T12:26:35-03:30') = time_unix(1321631795, 0);
|
||||
SELECT time_parse('2011-11-18 15:56:35') = time_unix(1321631795, 0);
|
||||
SELECT time_parse('2011-11-18') = time_date(2011, 11, 18);
|
||||
SELECT time_parse('15:56:35') = time_date(1, 1, 1, 15, 56, 35);
|
||||
} {
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
{1}
|
||||
}
|
||||
|
||||
do_execsql_test duration_constants {
|
||||
SELECT dur_ns();
|
||||
SELECT dur_us();
|
||||
SELECT dur_ms();
|
||||
SELECT dur_s();
|
||||
SELECT dur_m();
|
||||
SELECT dur_h();
|
||||
} {
|
||||
{1}
|
||||
{1000}
|
||||
{1000000}
|
||||
{1000000000}
|
||||
{60000000000}
|
||||
{3600000000000}
|
||||
}
|
||||
Reference in New Issue
Block a user