From 816ba3e427ce31fd3c6c4b1925f3b8b7da8ba964 Mon Sep 17 00:00:00 2001 From: Logan Hunt Date: Thu, 2 Jun 2022 22:38:43 -0700 Subject: [PATCH] Went back to milliseconds to benefit accuracy over formatting ease. Start and end timestamps for run splits are now based on internal timestamps as well. --- database/run.lisp | 19 +++++++++---------- speedrun.lisp | 19 +++++++++++-------- time.lisp | 16 +++++++++------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/database/run.lisp b/database/run.lisp index eba8b81..d474426 100644 --- a/database/run.lisp +++ b/database/run.lisp @@ -1,13 +1,14 @@ (mito:deftable run () - ((category :col-type category)) + ((category :col-type category) + (end-date :col-type (or :datetime :null))) (:record-timestamps nil) (:conc-name run-)) (mito:deftable run-split () ((run :col-type run) (category-split :col-type category-split) - (start-time :col-type (or :datetime :null)) - (end-time :col-type (or :datetime :null))) + (start-timestamp :col-type (or :bigint :null)) + (end-timestamp :col-type (or :bigint :null))) (:record-timestamps nil) (:conc-name run-split-)) @@ -21,22 +22,20 @@ ;; Returns the elapsed time in milliseconds since split started to either ;; current time or the split's end time (defun run-split-elapsed-time (run-split) - (let ((start (ignore-errors (run-split-start-time run-split))) - (end (or (ignore-errors (run-split-end-time run-split)) (local-time:now)))) + (let ((start (ignore-errors (run-split-start-timestamp run-split))) + (end (or (ignore-errors (run-split-end-timestamp run-split)) (get-internal-real-time)))) (if start - (floor (* 100 (local-time:timestamp-difference end start)))))) + (- end start)))) (defun run-split-format-elapsed-time (run-split) (let ((elapsed (run-split-elapsed-time run-split))) (if elapsed - (format-time (make-time-alist elapsed)) + (format-time (make-time-alist (millis-since-internal-timestamp 0 elapsed))) "-"))) - - (defmacro query-with-runs-elapsed (&rest body) `(mito:retrieve-by-sql - (sxql:select (:* (:as (:raw "sum(julianday(end_time) - julianday(start_time))*24*60*60") :elapsed)) + (sxql:select (:* (:as (:sum (:* (:/ (:raw "end_timestamp - CAST(start_timestamp AS REAL)") ,internal-time-units-per-second) 1000)) :elapsed)) (sxql:from :run_split) (sxql:group-by :run_id) ,@body))) diff --git a/speedrun.lisp b/speedrun.lisp index ba2d199..f1238f1 100644 --- a/speedrun.lisp +++ b/speedrun.lisp @@ -44,13 +44,14 @@ ;; Updates the current total elapsed time of the speedrun if it's running (defun update-time (speedrun) (if (eq (speedrun-state speedrun) 'RUNNING) - (setf (speedrun-elapsed speedrun) (floor (* 100 (/ (- (get-internal-real-time) (speedrun-start-timestamp speedrun)) internal-time-units-per-second)))))) + (setf (speedrun-elapsed speedrun) (millis-since-internal-timestamp (speedrun-start-timestamp speedrun))))) ;; Initializes a speedrun to start running the timer (defun start-speedrun (speedrun) - (setf (speedrun-state speedrun) 'RUNNING - (speedrun-start-timestamp speedrun) (get-internal-real-time) - (run-split-start-time (current-split speedrun)) (local-time:now))) + (let ((now (get-internal-real-time))) + (setf (speedrun-state speedrun) 'RUNNING + (speedrun-start-timestamp speedrun) now + (run-split-start-timestamp (current-split speedrun)) now))) ;; Saves the speedrun into the database (defun save-speedrun (speedrun) @@ -59,16 +60,18 @@ ;; Set the state of the speedrun to be stopped if there are no more splits. ;; Or, set the current split to the next one in the list. (defun next-split (speedrun) - (let ((now (local-time:now))) + (let ((now (get-internal-real-time))) (unless (equal (speedrun-state speedrun) 'STOPPED) - (setf (run-split-end-time (current-split speedrun)) now) + (setf (run-split-end-timestamp (current-split speedrun)) now) (if (equal (speedrun-current-split-index speedrun) (1- (length (speedrun-splits speedrun)))) (progn (setf - (speedrun-elapsed speedrun) (apply '+ (mapcar 'run-split-elapsed-time (speedrun-splits speedrun))) + (run-end-date (speedrun-run-dao speedrun)) (local-time:now) + ;; Since timer can get +-0.02 seconds out of sync of splits, just set it to the sum of the splits' elapsed + (speedrun-elapsed speedrun) (millis-since-internal-timestamp 0 (apply '+ (mapcar 'run-split-elapsed-time (speedrun-splits speedrun)))) (speedrun-state speedrun) 'STOPPED) (save-speedrun speedrun)) (progn (inc (speedrun-current-split-index speedrun)) - (setf (run-split-start-time (current-split speedrun)) now)))))) + (setf (run-split-start-timestamp (current-split speedrun)) now)))))) diff --git a/time.lisp b/time.lisp index 009e9a5..18ed345 100644 --- a/time.lisp +++ b/time.lisp @@ -1,9 +1,11 @@ -;; Makes a-list with '((hours . HOURS) (minutes . MINUTES) (seconds . SECONDS) (cs . CENTISECONDS)) -(defun make-time-alist (cs) - `((hours . ,(floor (/ cs (* 100 60 60)))) - (minutes . ,(floor (mod (/ cs (* 100 60)) 60))) - (seconds . ,(floor (mod (/ cs 100) 60))) - (cs . ,(mod cs 100)))) +(defun millis-since-internal-timestamp (start-timestamp &optional (end-timestamp (get-internal-real-time))) + (ceiling (* 1000 (/ (- end-timestamp start-timestamp) internal-time-units-per-second)))) + +(defun make-time-alist (millis) + `((hours . ,(floor (/ millis (* 1000 60 60)))) + (minutes . ,(floor (mod (/ millis (* 1000 60)) 60))) + (seconds . ,(floor (mod (/ millis 1000) 60))) + (millis . ,(mod millis 1000)))) ;; Formats, disregarding min/hour if they shouldn't be shown, a time alist to "H:M:S.ms" (defun format-time (time-alist) @@ -11,7 +13,7 @@ ((hours (cdr (assoc 'hours time-alist))) (minutes (cdr (assoc 'minutes time-alist))) (seconds (cdr (assoc 'seconds time-alist))) - (centis (cdr (assoc 'cs time-alist)))) + (centis (floor (/ (cdr (assoc 'millis time-alist)) 10)))) (concatenate 'string (unless (zerop hours) (format nil "~2,'0d:" hours)) (unless (and (zerop minutes) (zerop hours)) (format nil "~2,'0d:" minutes))