Skip to content

Commit 36c40e0

Browse files
Merge #1146
1146: Include timezone when serializing JSON responses r=carols10cents This adds back the timezone information to JSON responses. Note that the format is slightly different than we provided from the old `time` crate. Previously, responses would end in `Z` and now the timezone is provided as `+00:00`. However, both representations are compliant with RFC3339. Because of this regression, cargo-info has applied a patch to handle responses that may or may not include the timezone information. I've locally tested cargo-info both with and without this patch to confirm that both versions correctly handle the format with `+00:00`. The code for handling Option<NaiveDateTime> is a bit more complicated that I would like (given that we only have one instance of this), but I couldn't find a way to direct serde to handle this automatically. Fixes #1123
2 parents e811a4d + 99c459b commit 36c40e0

File tree

8 files changed

+61
-12
lines changed

8 files changed

+61
-12
lines changed

src/category.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub struct EncodableCategory {
3535
pub category: String,
3636
pub slug: String,
3737
pub description: String,
38-
pub created_at: NaiveDateTime,
38+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
3939
pub crates_cnt: i32,
4040
}
4141

@@ -45,7 +45,7 @@ pub struct EncodableCategoryWithSubcategories {
4545
pub category: String,
4646
pub slug: String,
4747
pub description: String,
48-
pub created_at: NaiveDateTime,
48+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
4949
pub crates_cnt: i32,
5050
pub subcategories: Vec<EncodableCategory>,
5151
}

src/crate_owner_invitation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub struct EncodableCrateOwnerInvitation {
6161
pub invited_by_username: String,
6262
pub crate_name: String,
6363
pub crate_id: i32,
64-
pub created_at: NaiveDateTime,
64+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
6565
}
6666

6767
/// Handles the `GET /me/crate_owner_invitations` route.

src/keyword.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub struct CrateKeyword {
3535
pub struct EncodableKeyword {
3636
pub id: String,
3737
pub keyword: String,
38-
pub created_at: NaiveDateTime,
38+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
3939
pub crates_cnt: i32,
4040
}
4141

src/krate/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ type ByName<'a> = diesel::dsl::Filter<All, WithName<'a>>;
100100
pub struct EncodableCrate {
101101
pub id: String,
102102
pub name: String,
103-
pub updated_at: NaiveDateTime,
103+
#[serde(with = "::util::rfc3339")] pub updated_at: NaiveDateTime,
104104
pub versions: Option<Vec<i32>>,
105105
pub keywords: Option<Vec<String>>,
106106
pub categories: Option<Vec<String>>,
107107
pub badges: Option<Vec<EncodableBadge>>,
108-
pub created_at: NaiveDateTime,
108+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
109109
pub downloads: i32,
110110
pub recent_downloads: Option<i64>,
111111
pub max_version: String,

src/token.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ pub struct ApiToken {
1818
#[serde(skip)] pub user_id: i32,
1919
#[serde(skip)] pub token: String,
2020
pub name: String,
21-
pub created_at: NaiveDateTime,
22-
pub last_used_at: Option<NaiveDateTime>,
21+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
22+
#[serde(with = "::util::rfc3339::option")] pub last_used_at: Option<NaiveDateTime>,
2323
}
2424

2525
/// The serialization format for the `ApiToken` model with its token value.
@@ -30,8 +30,8 @@ pub struct EncodableApiTokenWithToken {
3030
pub id: i32,
3131
pub name: String,
3232
pub token: String,
33-
pub created_at: NaiveDateTime,
34-
pub last_used_at: Option<NaiveDateTime>,
33+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
34+
#[serde(with = "::util::rfc3339::option")] pub last_used_at: Option<NaiveDateTime>,
3535
}
3636

3737
impl ApiToken {

src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub use self::io_util::{read_fill, LimitErrorReader, read_le_u32};
1919
pub use self::request_proxy::RequestProxy;
2020

2121
pub mod errors;
22+
pub mod rfc3339;
2223
mod hasher;
2324
mod head;
2425
mod io_util;

src/util/rfc3339.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Convenience functions for serializing and deserializing times in RFC 3339 format.
2+
//! Used for returning time values in JSON API responses.
3+
//! Example: `2012-02-22T14:53:18+00:00`.
4+
5+
use chrono::{DateTime, NaiveDateTime, Utc};
6+
use serde::{self, Deserialize, Deserializer, Serializer};
7+
8+
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
9+
where
10+
S: Serializer,
11+
{
12+
let s = DateTime::<Utc>::from_utc(*dt, Utc).to_rfc3339();
13+
serializer.serialize_str(&s)
14+
}
15+
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
16+
where
17+
D: Deserializer<'de>,
18+
{
19+
let s = String::deserialize(deserializer)?;
20+
let dt = DateTime::parse_from_rfc3339(&s).map_err(serde::de::Error::custom)?;
21+
Ok(dt.naive_utc())
22+
}
23+
24+
/// Wrapper for dealing with Option<NaiveDateTime>
25+
pub mod option {
26+
use chrono::NaiveDateTime;
27+
use serde::{Deserializer, Serializer};
28+
29+
pub fn serialize<S>(dt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
30+
where
31+
S: Serializer,
32+
{
33+
match *dt {
34+
Some(dt) => super::serialize(&dt, serializer),
35+
None => serializer.serialize_none(),
36+
}
37+
}
38+
39+
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NaiveDateTime>, D::Error>
40+
where
41+
D: Deserializer<'de>,
42+
{
43+
match super::deserialize(deserializer) {
44+
Ok(dt) => Ok(Some(dt)),
45+
Err(_) => Ok(None),
46+
}
47+
}
48+
}

src/version/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ pub struct EncodableVersion {
5252
pub num: String,
5353
pub dl_path: String,
5454
pub readme_path: String,
55-
pub updated_at: NaiveDateTime,
56-
pub created_at: NaiveDateTime,
55+
#[serde(with = "::util::rfc3339")] pub updated_at: NaiveDateTime,
56+
#[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime,
5757
pub downloads: i32,
5858
pub features: HashMap<String, Vec<String>>,
5959
pub yanked: bool,

0 commit comments

Comments
 (0)