schemegimpscript-fu

gimp scheme formatting result with leading zeroes


I'm trying to get timestamp for filename. I use procedure (time) which gives current timestamp:

(120 11 29 17 32 25)

year-1900 month-1 day hour minute second

(+ 1900 (car (time)) )(+ 1 (cadr (time)))(caddr(time))(cadddr(time))(cadr(cdddr(time)))(caddr(cdddr(time)))

this is almost good but... if any of element is lesser then 10 (e.g. first of january about 1 a.m.:

(121 0 1 1 2 3)

it gaves incorrect timestamp, because I need leading zeroes

202111123 should be 20210101010203

I wonder if there's a simplier method than checking the length of element and adding "0"


Solution

  • Script-Fu is based on Tiny Scheme, which is itself a subset of R5RS Scheme. There is no built-in functionality in R5RS Scheme to format numbers or strings like this, and none added to Script-Fu as far as I know, so you will need to create your own functions for this.

    It would be easiest to work with strings, and you probably want strings anyway for adding a timestamp to a filename.

    Here is a function that left-pads a string with zeroes. Note that the result is at least width characters in length, but the input str is not trimmed if it is already longer than width:

    (define (left-pad str width pad)
      (let add-padding ((s str))
        (if (< (- width (string-length s)) 1)
            s
            (add-padding (string-append pad s)))))
    

    You can use this function in a time-stamp function that returns a string in the desired format:

    (define (time-stamp)
      (let* ((curr (time))
             (year (number->string (+ 1900 (list-ref curr 0))))
             (month (left-pad (number->string (+ 1 (list-ref curr 1))) 2 "0"))
             (day (left-pad (number->string (list-ref curr 2)) 2 "0"))
             (hour (left-pad (number->string (list-ref curr 3)) 2 "0"))
             (minute (left-pad (number->string (list-ref curr 4)) 2 "0"))
             (second (left-pad (number->string (list-ref curr 5)) 2 "0")))
        (string-append year month day hour minute second)))
    

    Here, list-ref is used instead of cadr, caddr, cadddr, etc.; this is easier to read and less error-prone. Note that list-ref uses zero-based indices into a list.

    Then just call time-stamp to get a string containing the current time-stamp information. You can use string->number if you really want a number from this, either calling on the result of time-stamp, or calling string->number in the definition of time-stamp before the result is returned:

    > (time-stamp)
    "20201229133118"
    > (string->number (time-stamp))
    20201229133150
    

    I ran the above test in GIMP, so it is showing my system time at that moment. Here is an example using your mock test:

    > (define (time) '(121 0 1 1 2 3))
    > (time-stamp)
    "20210101010203"
    > (string->number (time-stamp))
    20210101010203
    

    There is a lot of code duplication in the first version of time-stamp above. That version may look more obvious to someone not familiar with Scheme programming idioms, but here is a version in a more functional style that removes a lot of that code duplication:

    (define (time-stamp)
      (let* ((curr (time))
             (y (+ 1900 (car curr)))
             (m (+ 1 (cadr curr)))
             (tstamp-nums (apply list y m (cddr curr))))
        (apply string-append
               (map (lambda (x) (left-pad (number->string x) 2 "0"))
                    tstamp-nums))))