I understand emacs lisp is great for handling dates and times, but does it have a function to convert strings to internal representation of integers using formats like %Y, %m, %d, %H, %M, %S, and so on? And also, in the emacs reference manual, it says that times are lists of two or three integers, but is there a more formal specification or description? ~ Thanks ~
Edit: Thanks for the responses - but guess I was wondering if there was a function that does format-time-string in reverse (like parse-time-string but with structure specifications for the input string)?
Edit2: I guess the answer is that there is nothing built in... but a partial implementation has been implemented here.
(defun encode-time-string (string)
(apply #'encode-time (parse-time-string string)))
The internal representation may change; I think it'd be better to use the provided documented API (encode-time, decode-time, etc) to access it.
The time is returned by most time related functions as a list of three integers. The first has the
most significant 16 bits of the seconds, while the second has the
least significant 16 bits. The third integer gives the microsecond
count.
The microsecond count is zero on systems that do not provide
resolution finer than a second.
As for the rest of your question have a look at this section of the manual in case you missed it.
If you need to parse a date (without a time), such as "July 7, 2022", then parse-time will return nil for the first 3 values. To avoid Wrong type argument: fixnump, nil, this possibility should be checked for:
(defun encode-time-string (string)
(let* ((dt (parse-time-string string))
(dtt (if (car dt)
dt
(append '(0 0 0) (nthcdr 3 dt))))
)
(apply #'encode-time dtt)))
Related
The Alexandria Manual
includes a boolean function for testing the length of sequences:
Function: length= &rest sequences
Takes any number of sequences or integers in any order. Returns true iff the length of all the sequences and the integers are equal. Hint: there’s a compiler macro that expands into more efficient code if the first argument is a literal integer.
The first sentence talks about "integers" (plural). Is this simply for testing whether several computed integers are the same, at the same time as testing for sequence lengths? Or is there some deeper significance?
The third sentence offers an optimization. Does this mean that counting over a list will stop when the literal index is reached, making it potentially more efficient than (= (length lst) 3) if lst is lengthy?
The first sentence talks about "integers" (plural). Is this simply for testing whether several computed integers are the same, at the same time as testing for sequence lengths? Or is there some deeper significance?
There is no deeper significance. It is probably just for symmetry. Basically, (length= ...) with only integer arguments is simply a slower =. But the primary use case for this is (length= 3 (some-list)), i.e., the test whether some sequence has a specific length ("has the sequence value produced by (some-list) a length of 3?").
The third sentence offers an optimization. Does this mean that counting over a list will stop when the literal index is reached, making it potentially more efficient than (= (length lst) 3) if lst is lengthy?
Yes, this is actually the case; the compiler macro expands into a call to sequence-of-length-p which (for lists) does something akin to that (via nthcdr).
How should I think of quote in the context of Category Theory? Is quote a monad? What kind of things is it?
I do not think it plays any role in category theory since it does not have anything to do with computation and everything to do with parsing and syntax. It is not a monad.
Imagine you want the string 5 + 1, what do you do? Well you enclose it in double quotes like "5 + 1" in the code and suddenly the result isn't 6 anymore but the string "5 + 1". Is "" something special in category theory? Is it a monad? Don't think so since it tells the compiler to create such a data structure that results in that string. In Haskell "hello" is just fancy syntax sugar for ['H', 'e', 'l', 'l', 'o']. In most languages a string is just a series of consecutive bytes, often an array.
The quote special form performs the same operation syck that '(+ 1 2) isn't an expression anymore, but data. The compiler does (cons '+ (cons '1 (cons '2 '()))) and store the pointer to that for everywhere you have some literal ending with (+ 1 2). Because of that (eq '(1 2) (cdr '(+ 1 2)) might be #t but #f is just as reasonable outcome since the compiler might not optimize for shared structure.
Moving forward you could imagine a fancy language that can dictate how the parser and compiler interpret literals. Almost all languages I know has string literals but if you made code to model complex numbers it would have been cool to say in the code that 3+5i should become tmp1 = make_complex 3 5 and that tmp1 is to be put everywhere the literal 3+5i exists in code. Why should numbers, strings, chars and perhaps regular expressions have special treatment?
I'm very very new in elisp and just started learning it. I have seen the following expressions in the document:
(1+ (buffer-size))
(+ 1 (buffer-size))
What do they mean? As I know elisp use prefix notation, so the second one should be correct one. But both of them can be executed without any errors. The first one is from the documentation of point-max function.
Thanks.
The token 1+ is an identifier which denotes a symbol. This symbol has a binding as a function, and so (1+ arg) means "call the 1+ function, with the value of arg as its argument). The 1+ function returns 1 plus the value of its argument.
The syntax (+ 1 arg) is a different way to achieve that effect. Here the function is named by the symbol +. The + function receives two arguments which it adds together.
In many mainstream programming languages popular today, the tokenization rules are such that there is no difference between 1+ and 1 +: both of these denote a numeric constant followed by a + token. Lisp tokenization is different. Languages in the Lisp family usually support tokens that can contain can contain digits and non-alpha-numeric characters. I'm looking at the Emacs Lisp reference manual and do not see a section about the logic which the read function uses to convert printed representations to objects. Typically, "Lispy" tokenizing behavior is something like this: token is scanned first without regard for what kind of token it is based on accumulating characters which are valid token constituents, stopping at a character which is not a token constituent. For instance when the input is abcde(f, the token that will be extracted is abcde. The ( character terminates the token (and stays in the input stream). Then, the resulting clump of characters abcde is re-examined and classified, converted to an object based on what it looks like, according to the rules of the given Lisp dialect. Across Lisp dialects, we can broadly depend on a token of all alphabetic characters to denote a symbol, and a token of all digits (possibly with a leading sign) to denote an integer. 1+ has a trailing + though, which is different!
Chapter 9.10 of Common Lisp: A Gentle Introduction To Symbolic Computation claims:
The primitive i/o functions TERPRI, PRIN1, PRINC and PRINT were defined in Lisp 1.5 (the ancestor of all modern Lisp systems) and are still found in Common Lisp today. They are included in the Advanced Topics section as a historical note; you can get the same effect with FORMAT.
This implies that you do not neet princ & co. any more and that, in modern code, you only should rely on format instead.
Are there any disadvantages when doing this? Respectively, are there any things one can not achieve with format that works with the other ones?
These functions correspond exactly to the following FORMAT operators:
TERPRI = ~%
FRESH-LINT = ~&
PRIN1 = ~S
PRINC = ~A
PRINT = ~%~S<space>
You can also use the more modern write. I'm not a huge fan of format because of its terse sub language, which usually is interpreted. Note that a good implementation might be able to compile format directives to more efficient code. I use FORMAT mostly when it makes complex code shorter, but not to output plain objects or things like single carriage returns...
Common Lisp includes three or more generations of text I/O APIs:
the old s-expression printing routines
the specialized and generalized stream IO functions
the complex formatter, based on earlier Fortran and/or Multics IO formatters
the Generic Function to print objects
the pretty printer
Additionally there are semi-standard CLOS-based IO implementations like Gray Streams.
Each might have its purpose and none is going away soon...
CL-USER 54 > (let ((label "Social security number")
(colon ": ")
(social-security-number '|7537 DD 459234957324 DE|))
(terpri)
(princ label)
(princ colon)
(princ social-security-number)
(write-char #\newline)
(write-string label)
(write-string colon)
(write social-security-number :escape nil)
(format t "~%~A~A~A" label colon social-security-number)
)
Social security number: 7537 DD 459234957324 DE
Social security number: 7537 DD 459234957324 DE
Social security number: 7537 DD 459234957324 DE
I am trying to calculate a person's age in Common Lisp using a given date of birth (a string of the form YYYY-MM-DD) but I got the following error:
Error: "2013-12-10"' is not of the expected typenumber'
My code is as follows
(defun current-date-string ()
"Returns current date as a string."
(multiple-value-bind (sec min hr day mon yr dow dst-p tz)
(get-decoded-time)
(declare (ignore sec min hr dow dst-p tz))
(format nil "~A-~A-~A" yr mon day)))
(defvar (dob 1944-01-01))
(let ((age (- (current-date-string) dob))))
Can anyone give me help in this regard? I suspect that the current date is in string format like my input, but how can we convert it into the same date format as my input?
Your code
There are two immediate problems:
The subtraction function - takes numbers as arguments. You're passing the result of (current-date-string), and that's a string produced by (format nil "~A-~A-~A" yr mon day).
This code doesn't make any sense:
(defvar (dob 1944-01-01))
(let ((age (- (current-date-string) dob))))
It's not indented properly, for one thing. It's two forms, the first of which is (defvar (dob 1944-01-01)). That's not the right syntax for defvar. 1944-01-01 is a symbol, and it's not bound, so even if you had done (defvar dob 1944-01-01), you'd get an error when the definition is evaluated. While (let ((age (- (current-date-string) dob)))) is syntactically correct, you're not doing anything after binding age. You probably want something like (let ((age (- (current-date-string) dob))) <do-something-here>).
Date arithmetic
At any rate, universal times are integer values that are seconds:
25.1.4.2 Universal Time
Universal time is an absolute time represented as a single
non-negative integer---the number of seconds since midnight, January
1, 1900 GMT (ignoring leap seconds). Thus the time 1 is 00:00:01 (that
is, 12:00:01 a.m.) on January 1, 1900 GMT.
This means that you can take two universal times and subtract one from the other to get the duration in seconds. Unfortunately, Common Lisp doesn't provide functions for manipulating those durations, and it's non-trivial to convert them because of leap years and the like. The Common Lisp Cookbook mentions this:
Dates and Times
Since universal times are simply numbers, they are easier and safer to
manipulate than calendar times. Dates and times should always be
stored as universal times if possibile, and only converted to string
representations for output purposes. For example, it is
straightforward to know which of two dates came before the other, by
simply comparing the two corresponding universal times with <. Another
typical problem is how to compute the "temporal distance" between two
given dates. Let's see how to do this with an example: specifically,
we will calculate the temporal distance between the first landing on
the moon (4:17pm EDT, July 20 1969) and the last takeoff of the space
shuttle Challenger (11:38 a.m. EST, January 28, 1986).
* (setq *moon* (encode-universal-time 0 17 16 20 7 1969 4))
2194805820
* (setq *takeoff* (encode-universal-time 0 38 11 28 1 1986 5))
2716303080
* (- *takeoff* *moon*)
521497260
That's a bit over 52 million seconds, corresponding to 6035 days, 20
hours and 21 minutes (you can verify this by dividing that number by
60, 60 and 24 in succession). Going beyond days is a bit harder
because months and years don't have a fixed length, but the above is
approximately 16 and a half years.
Reading date strings
It sounds like you're needing to also read dates from strings of the form YYYY-MM-DD. If you're confident that the value that you receive will be legal, you can do something as simple as
(defun parse-date-string (date)
"Read a date string of the form \"YYYY-MM-DD\" and return the
corresponding universal time."
(let ((year (parse-integer date :start 0 :end 4))
(month (parse-integer date :start 5 :end 7))
(date (parse-integer date :start 8 :end 10)))
(encode-universal-time 0 0 0 date month year)))
That will return a universal time, and you can do the arithmetic as described above.
Being lazy
It's generally good practice as a programmer to be lazy. In this case, we can be lazy by not implementing these kinds of functions ourselves, but instead using a library that will handle it for us. Some Common Lisp implementations may already provide date and time functions, and there are many time libraries listed on Cliki. I don't know which of these is most widely used, or has the coverage of the kinds of functions that you need, and library recommendations are off-topic for StackOverflow, so you may have to experiment with some to find one that works for you.
No, can't just subtract one string from another and expect the computer to magically know that these strings are a date and handle them accordingly. Whatever gave you that idea?
Use the library local-time.
Installation with Quicklisp:
(ql:quickload "local-time").
To parse a date:
(local-time:parse-timestring "2013-12-10")
This produces a timestamp object.
To get the current timestamp:
(local-time:now)
To get the difference in seconds:
(local-time:timestamp-difference one-timestamp another-timestamp)
To get the difference in years (rounded down):
(local-time:timestamp-whole-year-difference one-timestamp another-timestamp)