Skip to content

Commit

Permalink
feat: support set timezone in db
Browse files Browse the repository at this point in the history
  • Loading branch information
Taylor-lagrange committed Dec 25, 2023
1 parent 06fd7fd commit 82e756d
Show file tree
Hide file tree
Showing 25 changed files with 182 additions and 114 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions config/frontend.example.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Node running mode, see `standalone.example.toml`.
mode = "distributed"
# The default time zone of the server
# default_time_zone = "UTC"

[heartbeat]
# Interval for sending heartbeat task to the Metasrv, 5 seconds by default.
Expand Down
2 changes: 2 additions & 0 deletions config/standalone.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
mode = "standalone"
# Whether to enable greptimedb telemetry, true by default.
enable_telemetry = true
# The default time zone of the server
# default_time_zone = "UTC"

# HTTP server options.
[http]
Expand Down
1 change: 1 addition & 0 deletions src/cmd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ common-recordbatch.workspace = true
common-telemetry = { workspace = true, features = [
"deadlock_detection",
] }
common-time.workspace = true
config = "0.13"
datanode.workspace = true
datatypes.workspace = true
Expand Down
7 changes: 7 additions & 0 deletions src/cmd/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ pub enum Error {
source: common_meta::error::Error,
},

#[snafu(display("Failed to init default time zone"))]
InitTimeZone {
location: Location,
source: common_time::error::Error,
},

#[snafu(display("Failed to start procedure manager"))]
StartProcedureManager {
location: Location,
Expand Down Expand Up @@ -268,6 +274,7 @@ impl ErrorExt for Error {
| Error::LoadLayeredConfig { .. }
| Error::IllegalConfig { .. }
| Error::InvalidReplCommand { .. }
| Error::InitTimeZone { .. }
| Error::ConnectEtcd { .. }
| Error::NotDataFromOutput { .. }
| Error::CreateDir { .. }
Expand Down
6 changes: 5 additions & 1 deletion src/cmd/src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use client::client_manager::DatanodeClients;
use common_meta::heartbeat::handler::parse_mailbox_message::ParseMailboxMessageHandler;
use common_meta::heartbeat::handler::HandlerGroupExecutor;
use common_telemetry::logging;
use common_time::timezone::set_default_time_zone;
use frontend::frontend::FrontendOptions;
use frontend::heartbeat::handler::invalidate_table_cache::InvalidateTableCacheHandler;
use frontend::heartbeat::HeartbeatTask;
Expand All @@ -32,7 +33,7 @@ use servers::tls::{TlsMode, TlsOption};
use servers::Mode;
use snafu::{OptionExt, ResultExt};

use crate::error::{self, MissingConfigSnafu, Result, StartFrontendSnafu};
use crate::error::{self, InitTimeZoneSnafu, MissingConfigSnafu, Result, StartFrontendSnafu};
use crate::options::{CliOptions, Options};
use crate::App;

Expand Down Expand Up @@ -217,6 +218,9 @@ impl StartCommand {
logging::info!("Frontend start command: {:#?}", self);
logging::info!("Frontend options: {:#?}", opts);

set_default_time_zone(opts.default_time_zone.as_deref().unwrap_or(""))
.context(InitTimeZoneSnafu)?;

let meta_client_options = opts.meta_client.as_ref().context(MissingConfigSnafu {
msg: "'meta_client'",
})?;
Expand Down
11 changes: 9 additions & 2 deletions src/cmd/src/standalone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use common_meta::wal::{WalOptionsAllocator, WalOptionsAllocatorRef};
use common_procedure::ProcedureManagerRef;
use common_telemetry::info;
use common_telemetry::logging::LoggingOptions;
use common_time::timezone::set_default_time_zone;
use datanode::config::{DatanodeOptions, ProcedureConfig, RegionEngineConfig, StorageConfig};
use datanode::datanode::{Datanode, DatanodeBuilder};
use file_engine::config::EngineConfig as FileEngineConfig;
Expand All @@ -50,8 +51,8 @@ use servers::Mode;
use snafu::ResultExt;

use crate::error::{
CreateDirSnafu, IllegalConfigSnafu, InitDdlManagerSnafu, InitMetadataSnafu, Result,
ShutdownDatanodeSnafu, ShutdownFrontendSnafu, StartDatanodeSnafu, StartFrontendSnafu,
CreateDirSnafu, IllegalConfigSnafu, InitDdlManagerSnafu, InitMetadataSnafu, InitTimeZoneSnafu,
Result, ShutdownDatanodeSnafu, ShutdownFrontendSnafu, StartDatanodeSnafu, StartFrontendSnafu,
StartProcedureManagerSnafu, StartWalOptionsAllocatorSnafu, StopProcedureManagerSnafu,
};
use crate::options::{CliOptions, MixOptions, Options};
Expand Down Expand Up @@ -97,6 +98,7 @@ impl SubCommand {
pub struct StandaloneOptions {
pub mode: Mode,
pub enable_telemetry: bool,
pub default_time_zone: Option<String>,
pub http: HttpOptions,
pub grpc: GrpcOptions,
pub mysql: MysqlOptions,
Expand All @@ -120,6 +122,7 @@ impl Default for StandaloneOptions {
Self {
mode: Mode::Standalone,
enable_telemetry: true,
default_time_zone: None,
http: HttpOptions::default(),
grpc: GrpcOptions::default(),
mysql: MysqlOptions::default(),
Expand All @@ -146,6 +149,7 @@ impl StandaloneOptions {
fn frontend_options(self) -> FrontendOptions {
FrontendOptions {
mode: self.mode,
default_time_zone: self.default_time_zone,
http: self.http,
grpc: self.grpc,
mysql: self.mysql,
Expand Down Expand Up @@ -366,6 +370,9 @@ impl StartCommand {

info!("Building standalone instance with {opts:#?}");

set_default_time_zone(opts.frontend.default_time_zone.as_deref().unwrap_or(""))
.context(InitTimeZoneSnafu)?;

// Ensure the data_home directory exists.
fs::create_dir_all(path::Path::new(&opts.data_home)).context(CreateDirSnafu {
dir: &opts.data_home,
Expand Down
1 change: 1 addition & 0 deletions src/common/time/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ chrono-tz = "0.8"
chrono.workspace = true
common-error.workspace = true
common-macro.workspace = true
once_cell.workspace = true
serde = { version = "1.0", features = ["derive"] }
serde_json.workspace = true
snafu.workspace = true
Expand Down
57 changes: 28 additions & 29 deletions src/common/time/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ use chrono::{NaiveDateTime, NaiveTime, TimeZone as ChronoTimeZone, Utc};
use serde::{Deserialize, Serialize};

use crate::timestamp::TimeUnit;
use crate::timezone::TimeZone;
use crate::util::format_utc_datetime;
use crate::timezone::{get_time_zone, TimeZone};

/// Time value, represents the elapsed time since midnight in the unit of `TimeUnit`.
#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
Expand Down Expand Up @@ -109,13 +108,13 @@ impl Time {
self.as_formatted_string("%H:%M:%S%.f%z", None)
}

/// Format Time for local timezone.
/// Format Time for server timezone.
pub fn to_local_string(&self) -> String {
self.as_formatted_string("%H:%M:%S%.f", None)
}

/// Format Time for given timezone.
/// When timezone is None, using local time by default.
/// When timezone is None, using server time zone by default.
pub fn to_timezone_aware_string(&self, tz: Option<TimeZone>) -> String {
self.as_formatted_string("%H:%M:%S%.f", tz)
}
Expand All @@ -124,15 +123,13 @@ impl Time {
if let Some(time) = self.to_chrono_time() {
let date = Utc::now().date_naive();
let datetime = NaiveDateTime::new(date, time);

match timezone {
Some(TimeZone::Offset(offset)) => {
match get_time_zone(timezone) {
TimeZone::Offset(offset) => {
format!("{}", offset.from_utc_datetime(&datetime).format(pattern))
}
Some(TimeZone::Named(tz)) => {
TimeZone::Named(tz) => {
format!("{}", tz.from_utc_datetime(&datetime).format(pattern))
}
None => format_utc_datetime(&datetime, pattern),
}
} else {
format!("[Time{}: {}]", self.unit, self.value)
Expand Down Expand Up @@ -223,6 +220,7 @@ mod tests {
use serde_json::Value;

use super::*;
use crate::timezone::set_default_time_zone;

#[test]
fn test_time() {
Expand Down Expand Up @@ -312,57 +310,57 @@ mod tests {

#[test]
fn test_to_iso8601_string() {
std::env::set_var("TZ", "Asia/Shanghai");
set_default_time_zone("+10:00").unwrap();
let time_millis = 1000001;
let ts = Time::new_millisecond(time_millis);
assert_eq!("08:16:40.001+0800", ts.to_iso8601_string());
assert_eq!("10:16:40.001+1000", ts.to_iso8601_string());

let time_millis = 1000;
let ts = Time::new_millisecond(time_millis);
assert_eq!("08:00:01+0800", ts.to_iso8601_string());
assert_eq!("10:00:01+1000", ts.to_iso8601_string());

let time_millis = 1;
let ts = Time::new_millisecond(time_millis);
assert_eq!("08:00:00.001+0800", ts.to_iso8601_string());
assert_eq!("10:00:00.001+1000", ts.to_iso8601_string());

let time_seconds = 9 * 3600;
let ts = Time::new_second(time_seconds);
assert_eq!("17:00:00+0800", ts.to_iso8601_string());
assert_eq!("19:00:00+1000", ts.to_iso8601_string());

let time_seconds = 23 * 3600;
let ts = Time::new_second(time_seconds);
assert_eq!("07:00:00+0800", ts.to_iso8601_string());
assert_eq!("09:00:00+1000", ts.to_iso8601_string());
}

#[test]
fn test_serialize_to_json_value() {
std::env::set_var("TZ", "Asia/Shanghai");
set_default_time_zone("+10:00").unwrap();
assert_eq!(
"08:00:01+0800",
"10:00:01+1000",
match serde_json::Value::from(Time::new(1, TimeUnit::Second)) {
Value::String(s) => s,
_ => unreachable!(),
}
);

assert_eq!(
"08:00:00.001+0800",
"10:00:00.001+1000",
match serde_json::Value::from(Time::new(1, TimeUnit::Millisecond)) {
Value::String(s) => s,
_ => unreachable!(),
}
);

assert_eq!(
"08:00:00.000001+0800",
"10:00:00.000001+1000",
match serde_json::Value::from(Time::new(1, TimeUnit::Microsecond)) {
Value::String(s) => s,
_ => unreachable!(),
}
);

assert_eq!(
"08:00:00.000000001+0800",
"10:00:00.000000001+1000",
match serde_json::Value::from(Time::new(1, TimeUnit::Nanosecond)) {
Value::String(s) => s,
_ => unreachable!(),
Expand All @@ -372,46 +370,47 @@ mod tests {

#[test]
fn test_to_timezone_aware_string() {
std::env::set_var("TZ", "Asia/Shanghai");
set_default_time_zone("+10:00").unwrap();

assert_eq!(
"08:00:00.001",
"10:00:00.001",
Time::new(1, TimeUnit::Millisecond).to_timezone_aware_string(None)
);
std::env::set_var("TZ", "Asia/Shanghai");
assert_eq!(
"08:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("SYSTEM").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("SYSTEM").unwrap()))
);
assert_eq!(
"08:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("+08:00").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("+08:00").unwrap()))
);
assert_eq!(
"07:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("+07:00").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("+07:00").unwrap()))
);
assert_eq!(
"23:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("-01:00").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("-01:00").unwrap()))
);
assert_eq!(
"08:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("Asia/Shanghai").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("Asia/Shanghai").unwrap()))
);
assert_eq!(
"00:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("UTC").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("UTC").unwrap()))
);
assert_eq!(
"03:00:00.001",
Time::new(1, TimeUnit::Millisecond)
.to_timezone_aware_string(TimeZone::from_tz_string("Europe/Moscow").unwrap())
.to_timezone_aware_string(Some(TimeZone::from_tz_string("Europe/Moscow").unwrap()))
);
}
}
Loading

0 comments on commit 82e756d

Please sign in to comment.