use chrono::{DateTime, Timelike, Utc};
use rat_logger::{LevelFilter, LoggerBuilder, handler::term::TermConfig};
use rat_quickdb::manager::health_check;
use rat_quickdb::types::{ConnectionConfig, DatabaseType, PoolConfig};
use rat_quickdb::*;
use rat_quickdb::{ModelManager, ModelOperations, datetime_with_tz_field};
use std::collections::HashMap;
define_model! {
struct TimeZoneTestModel {
id: String,
name: String,
created_at_utc: chrono::DateTime<chrono::Utc>,
local_time_cst: chrono::DateTime<chrono::FixedOffset>,
local_time_est: String,
}
collection = "timezone_test",
database = "main",
fields = {
id: string_field(None, None, None).required().unique(),
name: string_field(None, None, None).required(),
created_at_utc: datetime_field(), local_time_cst: datetime_with_tz_field("+08:00"), local_time_est: datetime_with_tz_field("-05:00"), }
}
#[tokio::main]
async fn main() -> QuickDbResult<()> {
LoggerBuilder::new()
.add_terminal_with_config(TermConfig::default())
.init()
.expect("日志初始化失败");
println!("🚀 测试带时区的DateTime字段");
println!("========================\n");
cleanup_test_files().await;
println!("1. 配置SQLite数据库...");
let db_config = DatabaseConfig {
alias: "main".to_string(),
db_type: DatabaseType::SQLite,
connection: ConnectionConfig::SQLite {
path: "./timezone_test.db".to_string(),
create_if_missing: true,
},
pool: PoolConfig::builder()
.max_connections(10)
.min_connections(1)
.connection_timeout(10)
.idle_timeout(300)
.max_lifetime(1800)
.max_retries(3)
.retry_interval_ms(1000)
.keepalive_interval_sec(60)
.health_check_timeout_sec(10)
.build()
.unwrap(),
id_strategy: IdStrategy::Uuid,
cache: None,
};
add_database(db_config).await?;
println!("✅ 数据库配置完成\n");
println!("2. 数据库健康检查...");
let health_results = health_check().await;
if let Some(&is_healthy) = health_results.get("main") {
if is_healthy {
println!("✅ 数据库连接正常");
} else {
println!("❌ 数据库连接异常");
return Err(QuickDbError::ConnectionError {
message: "数据库连接异常".to_string(),
});
}
} else {
println!("❌ 未找到main数据库配置");
return Err(QuickDbError::ConnectionError {
message: "未找到main数据库配置".to_string(),
});
}
println!();
println!("3. 🔧 测试字段定义");
println!("==================");
let utc_field = datetime_field(); let cst_field = datetime_with_tz_field("+08:00"); let est_field = datetime_with_tz_field("-05:00");
println!(
"✅ datetime_field() - 默认UTC时区: {:?}",
utc_field.field_type
);
println!(
"✅ datetime_with_tz_field(+08:00) - 北京时间: {:?}",
cst_field.field_type
);
println!(
"✅ datetime_with_tz_field(-05:00) - 美东时间: {:?}",
est_field.field_type
);
println!();
println!("4. ✅ 测试字段验证");
println!("==================");
let valid_timezones = vec!["+00:00", "+08:00", "-05:00", "+12:45", "-09:30"];
for tz in valid_timezones {
let field = datetime_with_tz_field(tz);
match field.validate(&DataValue::String("2024-06-15 12:00:00".to_string())) {
Ok(_) => println!("✅ 时区 {}: 验证通过", tz),
Err(e) => println!("❌ 时区 {}: 验证失败 - {}", tz, e),
}
}
let invalid_timezones = vec!["CST", "UTC", "+8:00", "+08:0", "25:00", "+24:00"];
for tz in invalid_timezones {
let field = datetime_with_tz_field(tz);
match field.validate(&DataValue::String("2024-06-15 12:00:00".to_string())) {
Ok(_) => println!("❌ 时区 {}: 应该失败但通过了", tz),
Err(_) => println!("✅ 时区 {}: 正确拒绝了无效格式", tz),
}
}
println!();
println!("5. 📝 创建测试数据");
println!("==================");
let now = Utc::now();
let test_model = TimeZoneTestModel {
id: String::new(), name: "时区测试".to_string(),
created_at_utc: now,
local_time_cst: now.into(), local_time_est: now.to_rfc3339(), };
match test_model.save().await {
Ok(id) => {
println!("✅ 成功创建测试模型,ID: {}", id);
}
Err(e) => {
println!("❌ 创建测试模型失败: {}", e);
return Err(e);
}
}
println!();
println!("6. 🔍 查询测试数据");
println!("==================");
match ModelManager::<TimeZoneTestModel>::find(vec![], None).await {
Ok(models) => {
println!("✅ 查询到 {} 条记录", models.len());
for (index, model) in models.iter().enumerate() {
println!("📋 第{}条记录:", index + 1);
println!(
" name: {} (实际类型: {})",
model.name,
std::any::type_name_of_val(&model.name)
);
println!(
" created_at_utc: {} (实际类型: {})",
model.created_at_utc,
std::any::type_name_of_val(&model.created_at_utc)
);
println!(
" local_time_cst: {} (实际类型: {})",
model.local_time_cst,
std::any::type_name_of_val(&model.local_time_cst)
);
println!(
" local_time_est: {} (实际类型: {})",
model.local_time_est,
std::any::type_name_of_val(&model.local_time_est)
);
println!(" ---");
if std::any::type_name_of_val(&model.created_at_utc).contains("DateTime") {
println!(
" created_at_utc.format(): {}",
model.created_at_utc.format("%Y-%m-%d %H:%M:%S UTC")
);
} else {
println!(" created_at_utc (直接输出): {}", model.created_at_utc);
}
if std::any::type_name_of_val(&model.local_time_cst).contains("DateTime") {
println!(
" local_time_cst.format(): {}",
model.local_time_cst.format("%Y-%m-%d %H:%M:%S")
);
} else {
println!(" local_time_cst (直接输出): {}", model.local_time_cst);
}
println!(" local_time_est (String类型): {}", model.local_time_est);
println!();
}
}
Err(e) => println!("❌ 查询失败: {}", e),
}
println!();
println!("7. 🔄 更新测试数据");
println!("==================");
let update_time = Utc::now() + chrono::Duration::hours(1);
let update_cst_time =
rat_quickdb::utils::timezone::utc_to_timezone(update_time, "+08:00").unwrap();
let update_est_time =
rat_quickdb::utils::timezone::utc_to_timezone(update_time, "-05:00").unwrap();
println!("更新数据准备:");
println!(" UTC时间: {}", update_time.to_rfc3339());
println!(" CST时间: {}", update_cst_time.to_rfc3339());
println!(" EST时间: {}", update_est_time.to_rfc3339());
let mut update_data = HashMap::new();
update_data.insert(
"name".to_string(),
DataValue::String("更新后的时区测试".to_string()),
);
update_data.insert(
"created_at_utc".to_string(),
DataValue::DateTimeUTC(update_time),
);
update_data.insert(
"local_time_cst".to_string(),
DataValue::DateTime(update_cst_time),
);
update_data.insert(
"local_time_est".to_string(),
DataValue::String(update_est_time.to_rfc3339()),
);
println!("\n开始执行更新操作...");
match ModelManager::<TimeZoneTestModel>::update_many(vec![], update_data).await {
Ok(affected_rows) => {
println!("✅ 更新成功,影响了 {} 行", affected_rows);
}
Err(e) => {
println!("❌ 更新失败: {}", e);
println!("错误详情: {:?}", e);
}
}
println!();
println!("8. 🔍 查询更新后的数据");
println!("======================");
match ModelManager::<TimeZoneTestModel>::find(vec![], None).await {
Ok(models) => {
println!("✅ 查询到 {} 条记录", models.len());
for (index, model) in models.iter().enumerate() {
println!("📋 更新后第{}条记录:", index + 1);
println!(
" name: {} (类型: {})",
model.name,
std::any::type_name_of_val(&model.name)
);
println!(
" created_at_utc: {} (类型: {})",
model.created_at_utc,
std::any::type_name_of_val(&model.created_at_utc)
);
println!(
" local_time_cst: {} (类型: {})",
model.local_time_cst,
std::any::type_name_of_val(&model.local_time_cst)
);
println!(
" local_time_est: {} (类型: {})",
model.local_time_est,
std::any::type_name_of_val(&model.local_time_est)
);
if std::any::type_name_of_val(&model.created_at_utc).contains("DateTime") {
println!(" created_at_utc (小时): {}", model.created_at_utc.hour());
}
if std::any::type_name_of_val(&model.local_time_cst).contains("DateTime") {
println!(" local_time_cst (小时): {}", model.local_time_cst.hour());
}
println!();
}
}
Err(e) => println!("❌ 查询更新后数据失败: {}", e),
}
println!("9. 💾 检查数据库存储");
println!("==================");
println!("⏸️ 跳过数据删除,保留数据库文件供调试分析");
println!("\n🎉 带时区DateTime字段测试完成!");
println!("💾 数据库文件已保留,可以检查: ./timezone_test.db");
println!("📋 总结:");
println!(" - 新增 datetime_with_tz_field() 函数支持时区偏移");
println!(" - 时区格式:+00:00, +08:00, -05:00");
println!(" - 自动验证时区格式有效性");
println!(" - 向后兼容:datetime_field() 等同于 datetime_with_tz_field(+00:00)");
Ok(())
}
async fn cleanup_test_files() {
let test_files = vec![
"./timezone_test.db",
"./timezone_test.db-wal",
"./timezone_test.db-shm",
];
for file in test_files {
if let Err(e) = tokio::fs::remove_file(file).await {
if !e.to_string().contains("No such file or directory") {
eprintln!("警告:无法删除测试文件 {}: {}", file, e);
}
}
}
}