Browse Source

Merge pull request #1267 from jjlin/datetime-cleanup

Clean up datetime output and code
Daniel García 5 years ago
parent
commit
e38771bbbd
4 changed files with 42 additions and 29 deletions
  1. 5 4
      src/api/admin.rs
  2. 3 19
      src/mail.rs
  3. 2 2
      src/static/templates/admin/diagnostics.hbs
  4. 32 4
      src/util.rs

+ 5 - 4
src/api/admin.rs

@@ -18,7 +18,7 @@ use crate::{
     db::{backup_database, models::*, DbConn, DbConnType},
     error::{Error, MapResult},
     mail,
-    util::get_display_size,
+    util::{get_display_size, format_naive_datetime_local},
     CONFIG,
 };
 
@@ -293,6 +293,7 @@ fn get_users_json(_token: AdminToken, conn: DbConn) -> JsonResult {
 #[get("/users/overview")]
 fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
     let users = User::get_all(&conn);
+    let dt_fmt = "%Y-%m-%d %H:%M:%S %Z";
     let users_json: Vec<Value> = users.iter()
         .map(|u| {
             let mut usr = u.to_json(&conn);
@@ -300,9 +301,9 @@ fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
             usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &conn));
             usr["attachment_size"] = json!(get_display_size(Attachment::size_by_user(&u.uuid, &conn) as i32));
             usr["user_enabled"] = json!(u.enabled);
-            usr["created_at"] = json!(&u.created_at.format("%Y-%m-%d %H:%M:%S").to_string());
+            usr["created_at"] = json!(format_naive_datetime_local(&u.created_at, dt_fmt));
             usr["last_active"] = match u.last_active(&conn) {
-                Some(timestamp) => json!(timestamp.format("%Y-%m-%d %H:%M:%S").to_string()),
+                Some(dt) => json!(format_naive_datetime_local(&dt, dt_fmt)),
                 None => json!("Never")
             };
             usr
@@ -446,7 +447,7 @@ fn diagnostics(_token: AdminToken, _conn: DbConn) -> ApiResult<Html<String>> {
     // Run the date check as the last item right before filling the json.
     // This should ensure that the time difference between the browser and the server is as minimal as possible.
     let dt = Utc::now();
-    let server_time = dt.format("%Y-%m-%d %H:%M:%S").to_string();
+    let server_time = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string();
 
     let diagnostics_json = json!({
         "dns_resolved": dns_resolved,

+ 3 - 19
src/mail.rs

@@ -1,7 +1,6 @@
-use std::{env, str::FromStr};
+use std::{str::FromStr};
 
 use chrono::{DateTime, Local};
-use chrono_tz::Tz;
 use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
 
 use lettre::{
@@ -107,22 +106,6 @@ fn get_template(template_name: &str, data: &serde_json::Value) -> Result<(String
     Ok((subject, body))
 }
 
-pub fn format_datetime(dt: &DateTime<Local>) -> String {
-    let fmt = "%A, %B %_d, %Y at %r %Z";
-
-    // With a DateTime<Local>, `%Z` formats as the time zone's UTC offset
-    // (e.g., `+00:00`). If the `TZ` environment variable is set, try to
-    // format as a time zone abbreviation instead (e.g., `UTC`).
-    if let Ok(tz) = env::var("TZ") {
-        if let Ok(tz) = tz.parse::<Tz>() {
-            return dt.with_timezone(&tz).format(fmt).to_string();
-        }
-    }
-
-    // Otherwise, fall back to just displaying the UTC offset.
-    dt.format(fmt).to_string()
-}
-
 pub fn send_password_hint(address: &str, hint: Option<String>) -> EmptyResult {
     let template_name = if hint.is_some() {
         "email/pw_hint_some"
@@ -257,13 +240,14 @@ pub fn send_new_device_logged_in(address: &str, ip: &str, dt: &DateTime<Local>,
     use crate::util::upcase_first;
     let device = upcase_first(device);
 
+    let fmt = "%A, %B %_d, %Y at %r %Z";
     let (subject, body_html, body_text) = get_text(
         "email/new_device_logged_in",
         json!({
             "url": CONFIG.domain(),
             "ip": ip,
             "device": device,
-            "datetime": format_datetime(dt),
+            "datetime": crate::util::format_datetime_local(dt, fmt),
         }),
     )?;
 

+ 2 - 2
src/static/templates/admin/diagnostics.hbs

@@ -72,7 +72,7 @@
         const hour = String(d.getUTCHours()).padStart(2, '0');
         const minute = String(d.getUTCMinutes()).padStart(2, '0');
         const seconds = String(d.getUTCSeconds()).padStart(2, '0');
-        const browserUTC = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + seconds;
+        const browserUTC = `${year}-${month}-${day} ${hour}:${minute}:${seconds} UTC`;
         document.getElementById("time-browser-string").innerText = browserUTC;
 
         const serverUTC = document.getElementById("time-server-string").innerText;
@@ -147,4 +147,4 @@
             }
         }
     })();
-</script>
+</script>

+ 32 - 4
src/util.rs

@@ -322,12 +322,40 @@ pub fn get_env_bool(key: &str) -> Option<bool> {
 // Date util methods
 //
 
-use chrono::NaiveDateTime;
+use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
+use chrono_tz::Tz;
 
-const DATETIME_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.6fZ";
+/// Formats a UTC-offset `NaiveDateTime` in the format used by Bitwarden API
+/// responses with "date" fields (`CreationDate`, `RevisionDate`, etc.).
+pub fn format_date(dt: &NaiveDateTime) -> String {
+    dt.format("%Y-%m-%dT%H:%M:%S%.6fZ").to_string()
+}
+
+/// Formats a `DateTime<Local>` using the specified format string.
+///
+/// For a `DateTime<Local>`, the `%Z` specifier normally formats as the
+/// time zone's UTC offset (e.g., `+00:00`). In this function, if the
+/// `TZ` environment variable is set, then `%Z` instead formats as the
+/// abbreviation for that time zone (e.g., `UTC`).
+pub fn format_datetime_local(dt: &DateTime<Local>, fmt: &str) -> String {
+    // Try parsing the `TZ` environment variable to enable formatting `%Z` as
+    // a time zone abbreviation.
+    if let Ok(tz) = env::var("TZ") {
+        if let Ok(tz) = tz.parse::<Tz>() {
+            return dt.with_timezone(&tz).format(fmt).to_string();
+        }
+    }
+
+    // Otherwise, fall back to formatting `%Z` as a UTC offset.
+    dt.format(fmt).to_string()
+}
 
-pub fn format_date(date: &NaiveDateTime) -> String {
-    date.format(DATETIME_FORMAT).to_string()
+/// Formats a UTC-offset `NaiveDateTime` as a datetime in the local time zone.
+///
+/// This function basically converts the `NaiveDateTime` to a `DateTime<Local>`,
+/// and then calls [format_datetime_local](crate::util::format_datetime_local).
+pub fn format_naive_datetime_local(dt: &NaiveDateTime, fmt: &str) -> String {
+    format_datetime_local(&Local.from_utc_datetime(dt), fmt)
 }
 
 //