Skip to content

Commit

Permalink
Use chrono for struct timestamp fields
Browse files Browse the repository at this point in the history
Chrono is easier to use than timestamped strings, so they should be
automatically deserialized and available for the user, instead of having
the user deserialize the strings themselves.

These fields have been changed to use a type of `DateTime<FixedOffset>`:

- `ChannelPinsUpdateEvent.last_pin_timestamp`
- `Group.last_pin_timestamp`
- `Guild.joined_at`
- `GuildChannel.last_pin_timestamp`
- `Invite.created_at`
- `Member.joined_at`
- `Message.edited_timestamp
- `Message.timestamp`
- `MessageUpdateEvent.edited_timestamp`
- `MessageUpdateEvent.timestamp`
- `PrivateChannel.last_pin_timestamp`

`Member.joined_at` is now also an `Option`. Previously, if a Guild
Member Update was received for a member not in the cache, a new Member
would be instantiated with a default String value. This is incorrect
behaviour, and has now been replaced with being set to `None` in that
case.

Id methods' `created_at()` method now return a `chrono::NaiveDateTime`
instead of a `time::Timespec`, and `User::created_at` has been updated
to reflect that.

Additionally, drop `time` as a direct dependency and use chrono for
internals.
  • Loading branch information
Zeyla Hellyer committed Jun 6, 2017
1 parent d033909 commit 990e611
Show file tree
Hide file tree
Showing 22 changed files with 151 additions and 79 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Expand Up @@ -18,12 +18,15 @@ log = "~0.3"
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
time = "~0.1"

[dependencies.byteorder]
optional = true
version = "1.0"

[dependencies.chrono]
features = ["serde"]
version = "~0.3"

[dependencies.cookie]
default-features = false
version = "0.2"
Expand Down
88 changes: 73 additions & 15 deletions src/builder/create_embed.rs
Expand Up @@ -15,9 +15,10 @@
//! [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
//! [here]: https://discordapp.com/developers/docs/resources/channel#embed-object

use chrono::{DateTime, TimeZone};
use serde_json::Value;
use std::default::Default;
use time::Tm;
use std::fmt::Display;
use ::internal::prelude::*;
use ::model::Embed;

Expand Down Expand Up @@ -177,26 +178,75 @@ impl CreateEmbed {

/// Set the timestamp.
///
/// **Note**: This timestamp must be in ISO-8601 format. It must also be
/// in UTC format.
///
/// # Examples
///
/// You may pass a direct string:
///
/// - `2017-01-03T23:00:00`
/// - `2004-06-08T16:04:23`
/// - `2004-06-08T16:04:23`
///
/// Or a `time::Tm`:
/// This timestamp must be in ISO-8601 format. It must also be in UTC format.
///
/// ```rust,ignore
/// extern crate time;
/// You can also pass anything that implements `chrono::TimeZone`.
///
/// let now = time::now();
/// # Examples
///
/// Passing a string timestamp:
///
/// embed = embed.timestamp(now);
/// // ...
/// ```rust,no_run
/// # use serenity::Client;
/// #
/// # let mut client = Client::login("");
/// #
/// client.on_message(|_, msg| {
/// if msg.content == "~embed" {
/// let _ = msg.channel_id.send_message(|m| m
/// .embed(|e| e
/// .title("hello")
/// .timestamp("2004-06-08T16:04:23")));
/// }
/// });
/// ```
///
/// Creating a join-log:
///
/// Note: this example isn't efficient and is for demonstrative purposes.
///
/// ```rust,no_run
/// # use serenity::Client;
/// #
/// # let mut client = Client::login("");
/// #
/// use serenity::CACHE;
///
/// client.on_guild_member_add(|_, guild_id, member| {
/// let cache = CACHE.read().unwrap();
///
/// if let Some(guild) = cache.guild(guild_id) {
/// let guild = guild.read().unwrap();
///
/// let channel_search = guild
/// .channels
/// .values()
/// .find(|c| c.read().unwrap().name == "join-log");
///
/// if let Some(channel) = channel_search {
/// let user = member.user.read().unwrap();
///
/// let _ = channel.read().unwrap().send_message(|m| m
/// .embed(|e| {
/// let mut e = e
/// .author(|a| a.icon_url(&user.face()).name(&user.name))
/// .title("Member Join");
///
/// if let Some(ref joined_at) = member.joined_at {
/// e = e.timestamp(joined_at);
/// }
///
/// e
/// }));
/// }
/// }
/// });
/// ```
pub fn timestamp<T: Into<Timestamp>>(mut self, timestamp: T) -> Self {
self.0.insert("timestamp".to_owned(), Value::String(timestamp.into().ts));
Expand Down Expand Up @@ -406,10 +456,18 @@ impl From<String> for Timestamp {
}
}

impl From<Tm> for Timestamp {
fn from(tm: Tm) -> Self {
impl<'a> From<&'a str> for Timestamp {
fn from(ts: &'a str) -> Self {
Timestamp {
ts: ts.to_owned(),
}
}
}

impl<'a, Tz: TimeZone> From<&'a DateTime<Tz>> for Timestamp where Tz::Offset: Display {
fn from(dt: &'a DateTime<Tz>) -> Self {
Timestamp {
ts: tm.to_utc().rfc3339().to_string(),
ts: dt.to_rfc3339(),
}
}
}
2 changes: 1 addition & 1 deletion src/cache/mod.rs
Expand Up @@ -960,7 +960,7 @@ impl Cache {
guild.members.insert(event.user.id, Member {
deaf: false,
guild_id: Some(event.guild_id),
joined_at: String::default(),
joined_at: None,
mute: false,
nick: event.nick.clone(),
roles: event.roles.clone(),
Expand Down
4 changes: 2 additions & 2 deletions src/framework/buckets.rs
@@ -1,6 +1,6 @@
use chrono::UTC;
use std::collections::HashMap;
use std::default::Default;
use time;

#[doc(hidden)]
pub struct Ratelimit {
Expand Down Expand Up @@ -33,7 +33,7 @@ pub struct Bucket {

impl Bucket {
pub fn take(&mut self, user_id: u64) -> i64 {
let time = time::get_time().sec;
let time = UTC::now().timestamp();
let user = self.users.entry(user_id)
.or_insert_with(MemberRatelimit::default);

Expand Down
6 changes: 3 additions & 3 deletions src/framework/mod.rs
Expand Up @@ -591,7 +591,7 @@ impl Framework {
/// ```rust
/// # #[macro_use] extern crate serenity;
/// command!(ping(_ctx, msg) {
/// msg.channel_id.say("pong!");
/// msg.channel_id.say("pong!");
/// });
/// #
/// # fn main() {
Expand Down Expand Up @@ -700,7 +700,7 @@ impl Framework {
/// # use serenity::Client;
/// # let mut client = Client::login("token");
/// use serenity::framework::DispatchError::{NotEnoughArguments, TooManyArguments};
///
///
/// client.with_framework(|f| f
/// .on_dispatch_error(|ctx, msg, error| {
/// match error {
Expand All @@ -711,7 +711,7 @@ impl Framework {
/// msg.channel_id.say(&format!("Max arguments allowed is {}, but got {}.", max, given));
/// }
/// _ => println!("Unhandled dispatch error.")
/// }
/// }
/// }));
/// ```
pub fn on_dispatch_error<F>(mut self, f: F) -> Self
Expand Down
6 changes: 3 additions & 3 deletions src/gateway/prep.rs
@@ -1,3 +1,4 @@
use chrono::{Duration, UTC};
use serde_json::Value;
use std::sync::mpsc::{
Receiver as MpscReceiver,
Expand All @@ -8,7 +9,6 @@ use std::sync::{Arc, Mutex};
use std::time::{Duration as StdDuration, Instant};
use std::{env, thread};
use super::{GatewayError, GatewayStatus};
use time::{self, Duration};
use websocket::client::request::Url as RequestUrl;
use websocket::client::{Receiver, Sender};
use websocket::result::WebSocketError as WsError;
Expand Down Expand Up @@ -81,7 +81,7 @@ pub fn keepalive(interval: u64,
mut sender: Sender<WebSocketStream>,
channel: &MpscReceiver<GatewayStatus>) {
let mut base_interval = Duration::milliseconds(interval as i64);
let mut next_tick = time::get_time() + base_interval;
let mut next_tick = UTC::now() + base_interval;

let mut last_sequence = 0;
let mut last_successful = false;
Expand Down Expand Up @@ -110,7 +110,7 @@ pub fn keepalive(interval: u64,
}
}

if time::get_time() >= next_tick {
if UTC::now() >= next_tick {
// If the last heartbeat didn't receive an acknowledgement, then
// shutdown and auto-reconnect.
if !*last_ack.lock().unwrap() {
Expand Down
4 changes: 2 additions & 2 deletions src/gateway/shard.rs
@@ -1,3 +1,4 @@
use chrono::UTC;
use std::io::Write;
use std::net::Shutdown;
use std::sync::mpsc::{self, Sender as MpscSender};
Expand All @@ -6,7 +7,6 @@ use std::thread::{self, Builder as ThreadBuilder};
use std::time::{Duration as StdDuration, Instant};
use std::mem;
use super::{GatewayError, GatewayStatus, prep};
use time;
use websocket::client::{Client as WsClient, Sender, Receiver};
use websocket::message::Message as WsMessage;
use websocket::result::WebSocketError;
Expand Down Expand Up @@ -788,7 +788,7 @@ impl Shard {

fn update_presence(&self) {
let (ref game, status, afk) = self.current_presence;
let now = time::get_time().sec as u64;
let now = UTC::now().timestamp() as u64;

let msg = json!({
"op": OpCode::StatusUpdate.num(),
Expand Down
4 changes: 2 additions & 2 deletions src/http/ratelimiting.rs
Expand Up @@ -40,6 +40,7 @@
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits
#![allow(zero_ptr)]

use chrono::UTC;
use hyper::client::{RequestBuilder, Response};
use hyper::header::Headers;
use hyper::status::StatusCode;
Expand All @@ -48,7 +49,6 @@ use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::{i64, str, thread};
use super::{HttpError, LightMethod};
use time;
use ::internal::prelude::*;

lazy_static! {
Expand Down Expand Up @@ -441,7 +441,7 @@ impl RateLimit {
return;
}

let current_time = time::get_time().sec;
let current_time = UTC::now().timestamp();

// The reset was in the past, so we're probably good.
if current_time > self.reset {
Expand Down
25 changes: 16 additions & 9 deletions src/internal/timer.rs
@@ -1,9 +1,9 @@
use chrono::{DateTime, Duration, UTC};
use std::thread;
use std::time::Duration as StdDuration;
use time::{self, Duration, Timespec};

pub struct Timer {
due: Timespec,
due: DateTime<UTC>,
duration: Duration,
}

Expand All @@ -12,25 +12,32 @@ impl Timer {
let duration = Duration::milliseconds(duration_in_ms as i64);

Timer {
due: time::get_time() + duration,
due: UTC::now() + duration,
duration: duration,
}
}

pub fn await(&mut self) {
let diff = self.due - time::get_time();
let due_time = (self.due.timestamp() * 1000) + self.due.timestamp_subsec_millis() as i64;
let now_time = {
let now = UTC::now();

if diff > time::Duration::zero() {
let amount = diff.num_milliseconds() as u64;
(now.timestamp() * 1000) + now.timestamp_subsec_millis() as i64
};

thread::sleep(StdDuration::from_millis(amount));
if due_time > now_time {
let sleep_time = due_time - now_time;

if sleep_time > 0 {
thread::sleep(StdDuration::from_millis(sleep_time as u64));
}
}

self.due = self.due + self.duration;
}

pub fn check(&mut self) -> bool {
if time::get_time() >= self.due {
if UTC::now() >= self.due {
self.due = self.due + self.duration;

true
Expand All @@ -40,6 +47,6 @@ impl Timer {
}

pub fn reset(&mut self) {
self.due = time::get_time() + self.duration;
self.due = UTC::now() + self.duration;
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -103,9 +103,9 @@ extern crate serde_json;
extern crate lazy_static;

extern crate base64;
extern crate chrono;
extern crate flate2;
extern crate serde;
extern crate time;

#[cfg(feature="voice")]
extern crate byteorder;
Expand Down
3 changes: 2 additions & 1 deletion src/model/channel/group.rs
@@ -1,3 +1,4 @@
use chrono::{DateTime, FixedOffset};
use ::model::*;

#[cfg(feature="model")]
Expand Down Expand Up @@ -26,7 +27,7 @@ pub struct Group {
/// The Id of the last message sent.
pub last_message_id: Option<MessageId>,
/// Timestamp of the latest pinned message.
pub last_pin_timestamp: Option<String>,
pub last_pin_timestamp: Option<DateTime<FixedOffset>>,
/// The name of the group channel.
pub name: Option<String>,
/// The Id of the group owner.
Expand Down
3 changes: 2 additions & 1 deletion src/model/channel/guild_channel.rs
@@ -1,3 +1,4 @@
use chrono::{DateTime, FixedOffset};
use ::model::*;

#[cfg(feature="model")]
Expand Down Expand Up @@ -46,7 +47,7 @@ pub struct GuildChannel {
/// The timestamp of the time a pin was most recently made.
///
/// **Note**: This is only available for text channels.
pub last_pin_timestamp: Option<String>,
pub last_pin_timestamp: Option<DateTime<FixedOffset>>,
/// The name of the channel.
pub name: String,
/// Permission overwrites for [`Member`]s and for [`Role`]s.
Expand Down

0 comments on commit 990e611

Please sign in to comment.