mu-scm: add string->time and time->string

Replace the iso-date->time-t and v.v. functions with something more
customizable. Add more tests.

Use some (internal for now) %preferences variable for the defaults. TBD... maybe
should become a fluid?
This commit is contained in:
Dirk-Jan C. Binnema
2025-06-30 22:11:46 +03:00
parent f2699a4b95
commit 9360a641a9
3 changed files with 172 additions and 94 deletions

View File

@ -49,7 +49,8 @@
(let ((msg (car (mfind "" #:sort-field 'date #:reverse? #t)))) (let ((msg (car (mfind "" #:sort-field 'date #:reverse? #t))))
(test-equal "test with multi to and cc" (subject msg) ) (test-equal "test with multi to and cc" (subject msg) )
(test-equal "2016-05-15T16:57:25" (time-t->iso-date (date msg)))) (test-equal "2016-05-15 16:57:25"
(time->string (date msg) #:format "%F %T" #:utc? #t)))
(test-end "test-mfind")) (test-end "test-mfind"))
@ -92,7 +93,7 @@
(define (test-options) (define (test-options)
(test-begin "test-options") (test-begin "test-options")
(let ((opts (options))) (let ((opts %options))
(test-assert (>= (length opts) 4)) (test-assert (>= (length opts) 4))
(test-equal (assoc-ref opts 'quiet) #f) (test-equal (assoc-ref opts 'quiet) #f)
(test-equal (assoc-ref opts 'debug) #f) (test-equal (assoc-ref opts 'debug) #f)
@ -102,11 +103,22 @@
(define (test-helpers) (define (test-helpers)
(test-begin "test-helpers") (test-begin "test-helpers")
(test-equal 1750077792 (iso-date->time-t "2025-06-16T12:43:12")) (setenv "TZ" "Europe/Helsinki")
(test-equal 1750075200 (iso-date->time-t "2025-06-16T12")) (tzset)
(test-equal 1750077792 (string->time "2025-06-16T15:43:12" #:utc? #f))
(test-equal 1750077792 (string->time "2025-06-16 12:43:12" #:utc? #t))
(test-equal 1750075200 (string->time "2025-06-16 12" #:utc? #t))
(test-equal "2025-06-16T12:43:12" (time-t->iso-date 1750077792)) (test-equal "2025-06-16 12:43:12" (time->string 1750077792 #:utc? #t))
(test-equal " " (time-t->iso-date #f)) (test-equal "2025-06-16 15:43:12" (time->string 1750077792 #:utc? #f))
(test-equal "12:43:12" (time->string 1750077792 #:utc? #t #:format "%T"))
;; (define old-prefs %preferences)
;; (define %preferences '((utc? . #t) (short-date . "%T %F")))
;; (test-equal "12:43:12 2025-06-16" (time->string 1750077792))
;; (set! %preferences old-prefs)
(test-equal #f (time->string #f))
(test-end "test-helpers")) (test-end "test-helpers"))
(define* (main _ #:rest args) (define* (main _ #:rest args)

View File

@ -16,72 +16,72 @@
;; Note: this Scheme code depends on being loaded as part of "mu scm" ;; Note: this Scheme code depends on being loaded as part of "mu scm"
;; which does so automatically. It is not a general Guile module. ;; which does so automatically. It is not a general Guile module.
(define-module (mu) (define-module (mu)
:use-module (oop goops) :use-module (oop goops)
:use-module (system foreign) :use-module (system foreign)
:use-module (ice-9 optargs) :use-module (ice-9 optargs)
#:export ( #:export (
;; classes ;; classes
<store> <store>
mfind mfind
mcount mcount
cfind cfind
<message> <message>
sexp sexp
date date
last-change last-change
message-id message-id
path path
priority priority
subject subject
references references
thread-id thread-id
mailing-list mailing-list
language language
size size
;; message flags / predicates ;; message flags / predicates
flags flags
flag? flag?
draft? draft?
flagged? flagged?
passed? passed?
replied? replied?
seen? seen?
trashed? trashed?
new? new?
signed? signed?
encrypted? encrypted?
attach? attach?
unread? unread?
list? list?
personal? personal?
calendar? calendar?
;; contact fields ;; contact fields
from from
to to
cc cc
bcc bcc
;; message-body ;; message-body
body body
header header
;; misc ;; misc
options %options
;; %preferences
;; helpers ;; helpers
iso-date->time-t string->time
time-t->iso-date)) time->string))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; some helpers for dealing with plists / alists ;; some helpers for dealing with plists / alists
@ -416,39 +416,70 @@ The pattern is mandatory; the other (keyword) arguments are optional.
;;; Misc ;;; Misc
(define (options) ;; Get an alist with the general options this instance of \"mu\" started with.
"Get an alist with the general options this instance of \"mu\" started with. ;; These are based on the command-line arguments, environment etc., see the
These are based on the command-line arguments, environment etc., see ;; mu-scm(1) manpage for details.
the mu-scm(1) manpage for details. ;;
;; The alist maps symbols to values; a value of #f indicates that the value is at
;; its default.
%options ;; defined in c++
The alist maps symbols to values; a value of #f indicates that the value ;; Alist of user-preferences.
is at its default." ;;
%options) ;; - short-date: a strftime-compatibie string for the display
;; format of short dates.
;; - utc? : whether to assume use UTC for dates/times
(define %preferences
'( (short-date . "%F %T")
(utc? . #f)))
;; XXX; not exposed yet. Perhaps we need a "fluid" here?
(define (value-or-preference val key)
"If VAL is the symbol 'preference, return the value for KEY from %preferences.
Otherwise, return VAL."
(if (eq? val 'preference)
(assoc-ref %preferences key)
val))
;;; Helpers ;;; Helpers
(define* (iso-date->time-t isodate) (define* (string->time datestr #:key (utc? 'preference))
"Convert an ISO-8601 ISODATE to a number of seconds since epoch. "Convert an ISO-8601-style DATESTR to a number of seconds since epoch.
(like time_t, (current-time).
ISODATE is a string with the strftime format \"%FT%T\", i.e., ISODATE is a string with the strftime format \"%FT%T\", i.e.,
yyyy-mm-ddThh:mm:ss or any prefix there of. The 'T', ':', '-' or any non-numeric yyyy-mm-ddThh:mm:ss or any prefix there of. The 'T', ':', '-' or any non-numeric
characters re optional. characters re optional.
ISODATE is assumed to represent some UTC date." UTC? determines whether ISODATE should be interpreted as an UTC time.
(let* ((tmpl "00000101000000")
(isodate (string-filter char-numeric? isodate)) ;; filter out 'T' ':' '-' etc
(isodate ;; fill out isodate
(if (> (string-length tmpl) (string-length isodate))
(string-append isodate (substring tmpl (string-length isodate)))
isodate)))
;;(format #t "~a\n" isodate)
(car (mktime (car (strptime "%Y%m%d%H%M%S" isodate)) "Z"))))
(define-method (time-t->iso-date time-t) The input date format is fixed."
"Convert a time_t (second-since-epoch) value TIME-T to an ISO-8601 ;; XXX If not set, read the default from the %preferences variable.
string for the corresponding UTC time. (let* ((utc? (value-or-preference utc? 'utc?))
(tmpl "00000101000000")
(datestr (string-filter char-numeric? datestr)) ;; filter out 'T' ':' '-' etc
(datestr ;; fill out datestr
(if (> (string-length tmpl) (string-length datestr))
(string-append datestr (substring tmpl (string-length datestr)))
datestr))
(sbdtime (car (strptime "%Y%m%d%H%M%S" datestr))))
(car (if utc?
(mktime sbdtime "UTC")
(mktime sbdtime)))))
If TIME-T is #f, return an empty string of the same length."
(if time-t (define* (time->string time-t #:key (format 'preference) (utc? 'preference))
(strftime "%FT%T" (gmtime time-t)) "Convert a time_t (second-since-epoch) value TIME-T into a string.
" "))
FORMAT is the strftime-compatible format-string UTC? determines whether to use
UTC time.
If TIME-T is #f, return #f."
;; If not specified, both are determined from %preferences ('short-date
;; and 'utc?, respectively).
(let* ((format (value-or-preference format 'short-date))
(utc? (value-or-preference utc? 'utc?)))
(if time-t
(let ((t (if utc? (gmtime time-t) (localtime time-t))))
(strftime format t))
#f)))

View File

@ -94,6 +94,7 @@ Appendices
Indices Indices
* Procedure Index:: * Procedure Index::
* Variable Index::
@end menu @end menu
@ -681,30 +682,56 @@ For example:
@node Miscellaneous @node Miscellaneous
@section Miscellaneous @section Miscellaneous
@deffn {Scheme Procedure} options @defvar %options
@end deffn @end defvar
This yields an association-list (alist) of general options passed to @command{mu An association-list (alist) of general options passed to @command{mu scm} or
scm}. Values at @t{#f} indicate that the value is at its default. their default values.
@lisp @lisp
(options) %options
=> ((mu-home . #f) (quiet . #f) (debug . #f) (verbose . #f)) => ((mu-home . #f) (quiet . #f) (debug . #f) (verbose . #f))
@end lisp @end lisp
@c @defvar %preferences
@c @end defvar
@c An association list (alist) of user-preferences that influence interactive use.
@c E.g., the way how certain things are displayed. The alist maps symbols to values:
@c @itemize
@c @item @code{short-date}
@c a @code{strftime}-compatible string for the display format of short dates.
@c @item @code{utc?}
@c boolean, whether to assume UTC for dates/times, such as for @code{string->time} and @code{time->string}
@c @end itemize
@c @lisp
@c %preferences
@c ((short-date-format . "%F %T") (input-utc? . #f) (output-utc? . #f))
@c @end lisp
@node Helpers @node Helpers
@section Helpers @section Helpers
@deffn {Scheme Procedure} iso-date->time-t iso-date @deffn {Scheme Procedure} string->time timestr [#:utc? (assoc-ref %preferences 'utc?)]
@end deffn @end deffn
Convert some ISO-8601 compatible time-point (assuming UTC) to a Convert some ISO-8601-style time-string to a seconds-since-epoch @t{time_t}
seconds-since-epoch @t{time_t} value. @var{iso-date} is expected to be in the value. @var{timestr} is expected to be in the @t{strftime}-format @t{%F%T}, or a
@t{strftime}-format @t{%F%T}, or a prefix thereof. Non-numerical characters are prefix thereof. Non-numerical characters are ignored.
ignored.
@deffn {Scheme Procedure} time-t->iso-date time-t You can influence whether UTC is assumed using the optional @code{#:utc?}
parameter. The input time/date format is fixed.
@c which uses @code{%preferences} for its default.
@deffn {Scheme Procedure} time->string
[#:format (assoc-ref %preferences 'short-date)]
[#:utc? (assoc-ref %preferences 'utc?)]
@end deffn @end deffn
Convert a @t{time_t} value to an ISO-8601 compatible string (assuming UTC). If Convert a @t{time_t} value (``seconds-since-epoch'') to a string. The optional
@var{time_t} is @t{#f}, return an empty string of the same length. @code{#:format} parameter (an @code{strftime}-compatible string) determines the
output format, while the @code{#:utc?} determines whether to use UTC.
@c Defaults are determined by the @code{%preferences} variable.
If @var{time_t} is @t{#f}, return @code{#f}.
@node GNU Free Documentation License @node GNU Free Documentation License
@appendix GNU Free Documentation License @appendix GNU Free Documentation License
@ -715,8 +742,16 @@ Convert a @t{time_t} value to an ISO-8601 compatible string (assuming UTC). If
@node Procedure Index @node Procedure Index
@unnumbered Procedure Index @unnumbered Procedure Index
This is an alphabetical list of all the procedures and macros in @t{mu-scm}. This is an alphabetical list of all the public procedures and macros in @t{mu-scm}.
@printindex fn @printindex fn
@page
@node Variable Index
@unnumbered Variables Index
This is an alphabetical list of all the public variables @t{mu-scm}.
@printindex vr
@bye @bye