DateTime<Utc> 编译但不是 DateTime<Local> 查询表,其列定义为带时区的时间戳

Posted

技术标签:

【中文标题】DateTime<Utc> 编译但不是 DateTime<Local> 查询表,其列定义为带时区的时间戳【英文标题】:DateTime<Utc> compiles but not DateTime<Local> querying a table with a column defined as timestamp with time zone 【发布时间】:2021-07-06 03:25:54 【问题描述】:

我有一个 postgresql 表,其列定义为 timestamp with time zone。表映射到这个结构:

#[derive(Serialize, Queryable)]
pub struct Location 
    pub publication_time: DateTime<Utc>,
    pub id: i32,
    pub name: String,
    pub latitude: BigDecimal,
    pub longitude: BigDecimal,

架构有这样的定义:

table! 
    locations 
        publication_time -> Timestamptz,
        id -> Integer,
        name -> Text,
        latitude -> Numeric,
        longitude -> Numeric,
    

(部分)Cargo.toml:

serde = "1.0.125"
serde_json = "1.0.64"
serde_derive = "1.0.125"
diesel =  version = "1.4.6", features = ["postgres", "r2d2", "chrono", "numeric"] 
bigdecimal =  version = "0.1.0", features = ["serde"] 
chrono =  version = "0.4.19", features = ["serde"] 

查询数据库的函数:

fn get_all_locations(pool: web::Data<Pool>) -> Result<Vec<Location>, diesel::result::Error> 
    let conn = pool.get().unwrap();
    let items = locations.load::<Location>(&conn)?;
    Ok(items)

然后使用 serde_json 将其序列化为 JSON 数组。数据库中的日期时间是2021-04-08 15:02:02.514+02。当 DateTime 为 Utc 时,程序编译得很好,但以 UTC 显示的 DateTime 像 2021-04-08T13:02:02.514Z。我将 publication_time 更改为 DateTime&lt;Local&gt; 以保留时区信息,但随后 cargo build 失败:

error[E0277]: the trait bound `DateTime<Local>: FromSql<diesel::sql_types::Timestamptz, Pg>` is not satisfied
  --> src/controller.rs:21:27
   |
21 |     let items = locations.load::<Location>(&conn)?;
   |                           ^^^^ the trait `FromSql<diesel::sql_types::Timestamptz, Pg>` is not implemented for `DateTime<Local>`
   |
   = help: the following implementations were found:
             <DateTime<Utc> as FromSql<diesel::sql_types::Timestamptz, Pg>>
   = note: required because of the requirements on the impl of `diesel::Queryable<diesel::sql_types::Timestamptz, Pg>` for `DateTime<Local>`
   = note: 2 redundant requirements hidden
   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Timestamptz, diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Numeric, diesel::sql_types::Numeric), Pg>` for `models::Location`
   = note: required because of the requirements on the impl of `LoadQuery<_, models::Location>` for `locations::table`

我有另一个程序可以插入到这个表中,它可以工作,唯一的区别是派生(反序列化,可插入)。

#[derive(Deserialize, Insertable)]
pub struct Location 
    pub publication_time: DateTime<Local>,
    pub id: i32,
    pub name: String,
    pub latitude: BigDecimal,
    pub longitude: BigDecimal,

【问题讨论】:

如果我没记错的话 sql 没有本地时间戳,它每次都转换为 utc。 postgresql 将如何检索这些信息? 【参考方案1】:

柴油机本身不支持将Timestamptz 字段映射到DateTime&lt;Local&gt;,因为它只为DateTime&lt;Utc&gt; 提供the corresponding impl。 您可以通过在相应字段上使用 #[diesel(deserialize_as = "…")] attribute 并提供您自己的反序列化包装器来解决此问题:

#[derive(Serialize, Queryable)]
pub struct Location 
    #[diesel(deserialize_as = "MyDateTimeWrapper")]
    pub publication_time: DateTime<Local>,
    pub id: i32,
    pub name: String,
    pub latitude: BigDecimal,
    pub longitude: BigDecimal,


pub struct MyDatetimeWrapper(DateTime<Local>);

impl Into<DateTime<Local>> for MyDatetimeWrapper 
    fn into(self) -> DateTime<Local> 
        self.0
    


impl<DB, ST> Queryable<ST, DB> for MyDateTimeWrapper
where
    DB: Backend,
    DateTime<Utc>: Queryable<ST, DB>,

    type Row = <DateTime<Utc> as Queryable<ST, DB>>::Row;

    fn build(row: Self::Row) -> Self 
        Self(<DateTime<Utc> as Queryable<ST, DB>>::build(row).with_timezone(&Local))
    

【讨论】:

感谢您的建议。我实现了它,但收到错误“错误[E0599]:在当前范围内找不到关联类型&lt;DateTime&lt;Utc&gt; as diesel::Queryable&lt;ST, DB&gt;&gt;::Row 的名为with_timezone 的方法--> src/models.rs:43:18 | 43 | Self(row.with_timezone (本地))| ^^^^^^^^^^^^^ 在&lt;DateTime&lt;Utc&gt; as diesel::Queryable&lt;ST, DB&gt;&gt;::Row中找不到方法@ @kometen 我已经通过添加缺少的转换来修复代码。 谢谢。 :-) 那是一种乐趣。

以上是关于DateTime<Utc> 编译但不是 DateTime<Local> 查询表,其列定义为带时区的时间戳的主要内容,如果未能解决你的问题,请参考以下文章

使用 pandas.join 在 datetime64[ns, UTC] 上加入失败

如何在 Ruby 中更改 DateTime 的时区?

在填充的数据表中将 datetime 列从 utc 转换为本地时间

在 Python 中将 datetime.date 转换为 UTC 时间戳

Cache.Add 绝对过期 - 是不是基于 UTC?

time和datetime模块