When I use Emacs calendar/diary with org-mode(however I do not think this is the reason of the problem), I encounter the following problem.
The default calendar is like this:
I add the following codes to display week number:
(copy-face font-lock-constant-face 'calendar-iso-week-face)
(set-face-attribute 'calendar-iso-week-face nil
:height 1)
(setq calendar-intermonth-text
'(propertize
(format "%2d"
(car
(calendar-iso-from-absolute
(calendar-absolute-from-gregorian (list month day year)))))
'font-lock-face 'calendar-iso-week-face))
; Title for week number
(copy-face 'default 'calendar-iso-week-header-face)
(set-face-attribute 'calendar-iso-week-header-face nil
:height 0.7)
(setq calendar-intermonth-header
(propertize "Wk" ; or e.g. "KW" in Germany
'font-lock-face 'calendar-iso-week-header-face))
After inserting these codes, the week numbers can be displayed. However, the alignment is a little bit strange like this:
The last line of the calendar is not aligned. How to deal with this problem?
Thanks.
It's your height settings that are causing the problem. You may wish to try experimenting with the other variables such as: calendar-left-margin, calendar-column-width, calendar-day-header-width, calendar-day-digit-width, calendar-intermonth-header, calendar-intermonth-text.
NOTE: For anyone wondering how to highlight Saturday instead of Monday, the function calendar-generate-month has a line of code that contains (if (memq i '(0 6)) -- if calendar-week-start-day is changed from 0 to 1, then it would be necessary to alter the above-code from 0 6 to 5 6. Alternatively, the font-lock settings can be trumped by adding new keywords for the days of the week.
(setq calendar-week-start-day 1)
(setq calendar-intermonth-text
'(propertize
(format "%2d"
(car
(calendar-iso-from-absolute
(calendar-absolute-from-gregorian (list month day year)))))
'font-lock-face 'font-lock-warning-face))
(setq calendar-intermonth-header
(propertize "Wk" ; or e.g. "KW" in Germany
'font-lock-face 'font-lock-keyword-face))
(source: lawlist.com)
Related
It's there any function to determine current system date is holiday or not in elisp.
function like this.
(is-holiday (current-time))
The answer requires that the user set up a calendar of predefined holidays, like this example. I have included a test holiday for May 9 -- if the user wishes to test out this function on any day other than May 9, the user may wish to change the Arbitrary Test Holiday to whatever day the test is being performed -- after the function has been tested, the test entry can be removed.
For examples of how to format the holidays, please refer to the doc-string for the variable calendar-holidays within the library holidays.el -- e.g., holiday-fixed; holiday-float; holiday-sexp; (lunar-phases); (solar-equinoxes-solstices); holiday-hebrew; holiday-islamic; holiday-bahai; holiday-julian; holiday-chinese; etc.
How can you try out this example?: Block/copy/paste the code into your *scratch* buffer; and type M-x eval-buffer RET; and then type M-x is-holiday RET. It is a fully functional working draft. If you decide that you don't like it after you try it, just restart Emacs and you'll be back to where you were before you tried it.
The testing that was performed was done with the most recent public release of Emacs: GNU Emacs 24.4.1 (x86_64-apple-darwin10.8.0, NS apple-appkit-1038.36) of 2014-10-20 on builder10-6.porkrind.org.
(require 'holidays)
(defcustom my-custom-holiday-list (mapcar 'purecopy '(
(holiday-fixed 1 1 "New Year's Day")
(holiday-float 1 1 3 "Martin Luther King Day")
(holiday-float 2 1 3 "President's Day")
(holiday-float 5 1 -1 "Memorial Day")
;; ARBITRARY TEST HOLIDAY -- MAY 9
(holiday-fixed 5 9 "Arbitrary Test Holiday -- May 9")
(holiday-fixed 7 4 "Independence Day")
(holiday-float 9 1 1 "Labor Day")
(holiday-float 10 1 2 "Columbus Day")
(holiday-fixed 11 11 "Veteran's Day")
(holiday-float 11 4 4 "Thanksgiving")
(holiday-fixed 12 25 "Christmas")
(solar-equinoxes-solstices)
(holiday-sexp calendar-daylight-savings-starts
(format "Daylight Saving Time Begins %s"
(solar-time-string
(/ calendar-daylight-savings-starts-time (float 60))
calendar-standard-time-zone-name)))
(holiday-sexp calendar-daylight-savings-ends
(format "Daylight Saving Time Ends %s"
(solar-time-string
(/ calendar-daylight-savings-ends-time (float 60))
calendar-daylight-time-zone-name))) ))
"Custom holidays defined by the user."
:type 'sexp
:group 'holidays)
(defun is-holiday ()
"Is today a holiday?"
(interactive)
(let* (
(d1 (time-to-days (current-time)))
(date (calendar-gregorian-from-absolute d1))
ee
res-holidays
(displayed-month (nth 0 date))
(displayed-year (nth 2 date))
(holiday-list
(dolist (p my-custom-holiday-list res-holidays)
(let* (h)
(when (setq h (eval p))
(setq res-holidays (append h res-holidays)))))) )
(mapcar
(lambda (x)
(let ((txt (format "%s -- %s" (car x) (car (cdr x)))))
(when (eq d1 (calendar-absolute-from-gregorian (car x)))
(push txt ee))))
holiday-list)
(if ee
(message "The following holiday(s) is/are today: %s" (nreverse ee))
(message "Today is not a holiday."))))
I'm just starting to use ses-mode in emacs, and I plan to use it with timestamps, but I do not manage to have them parsed in a way that I can then use.
I'm taking measurements on three days of the week, so my distances between one measurement and the other is either 2 or 3 days. I chose to use ses-mode in emacs because it runs on all of my computers, including the phone.
my spreadsheet contains datestamp, conductivity, temperature, and gallon count, a couple of subsequent lines would look like this:
2014-10-03 2.95 33.4 4031070
2014-10-06 3.07 33.5 4086930
2014-10-08 2.97 33.6 4119590
I would add two more columns, the first with the difference of days between the readings, the second with the "gallon-per-day" value.
I do not manage to have the string timestamp parsed into a format where I can do computations, staying within a simple emacs spreadsheet (SES).
I've tried date-to-time, but it always returns the same value (14445 17280).
parse-time-string gives me a 9-tuple which I can't directly pass to format-time-string.
The function encode-time helps:
(let ((l (parse-time-string "2014-09-12")))
(format-time-string "%d %m %Y" (encode-time 0 0 0 (nth 3 l) (nth 4 l) (nth 5 l))))
The following version uses cl-flet to avoid doubling of code if the encoding is needed multiple times. If you need the encoding also in other functions you can use defun instead of cl-flet.
(eval-when (compile) (require 'cl)) ;; for cl-flet
(let ((A2 "2014-10-08") ;; just for testing
(A1 "2014-10-03")) ;; just for testing
(cl-flet ((encode (str)
(let ((l (parse-time-string str)))
(encode-time 0 0 0 (nth 3 l) (nth 4 l) (nth 5 l)))))
(let* ((t-prev (encode A1))
(t-this (encode A2)))
(/ (time-to-seconds (time-subtract t-this t-prev)) (* 24 60 60)))))
As a function:
(eval-when (compile) (require 'cl)) ;; for cl-flet
(defun day-diff (date1 date2)
"Calculate the difference of dates in days between DATE1-STR and DATE2-STR."
(interactive "sDate1:\nsDate2:")
(cl-flet ((encode (str)
(let ((l (parse-time-string str)))
(encode-time 0 0 0 (nth 3 l) (nth 4 l) (nth 5 l)))))
(setq date1 (encode date1)
date2 (encode date2))
(let ((ret (/ (time-to-seconds (time-subtract date1 date2)) (* 24 60 60))))
(when (called-interactively-p 'any)
(message "Day difference: %s" ret))
ret)))
(put 'day-diff 'safe-function t)
An alternative using calc would be:
(require 'calc)
(defun day-diff (date1 date2)
"Calculate the difference of dates in days between DATE1-STR and DATE2-STR."
(interactive "sDate1:\nsDate2:")
(let ((ret (string-to-number (calc-eval (format "<%s>-<%s>" date1 date2)))))
(when (called-interactively-p 'any)
(message "Day difference: %s" ret))
ret))
If you omit the nice-to-have features this becomes almost a simple cell formula: (string-to-number (calc-eval (format "<%s>-<%s>" A1 A2))).
If you want to save the stuff in the spreadsheet you can put the defun in table cell A1. A more simple example:
(progn (defun day-diff (date1 date2) (string-to-number (calc-eval (format "<%s>-<%s>" date1 date2)))) (put 'day 'safe-function t) "Actual header")
To have a more convenient editing possibility you can switch to M-x lisp-mode.
There you find
^L
(ses-cell A1 "Actual Header" (progn (defun day-diff (date1 date2) (string-to-number (calc-eval (format "<%s>-<%s>" date1 date2)))) (put 'day 'safe-function t) "Actual header") nil nil)
which you can edit. But do not insert linebreaks! ses identifies cell-positions with line numbers in that file!
Another nice alternative is to put the definition of your function into the file-local variable list.
Switch to lisp-interaction mode by M-x lisp-interaction-mode.
Go to the end of the file. There you find the lines:
;; Local Variables:
;; mode: ses
;; End:
Add your function definition as eval to this list:
;; Local Variables:
;; mode: ses
;; eval:
;; (progn
;; (defun day-diff (date1 date2)
;; (string-to-number (calc-eval (format "<%s>-<%s>" date1 date2))))
;; (put 'day-diff 'safe-function t))
;; End:
You can add the progn without the comment characters ;. In this case even indentation works. Afterwards you can call comment-region for the progn.
You can save the file and run M-x normal-mode. Afterwards the function is defined and you can use it in the spreadsheet.
In Emacs calendar, one can count days between two dates (including both the start and the end date) using the M-= which runs the command calendar-count-days-region. How can I count days excluding the weekends (Saturday and Sunday) and if defined holidays coming from the variables: holiday-general-holidays and holiday-local-holidays?
I think this essentially breaks down into three parts:
Count the days in a region
subtract the weekend days
subtract the holidays
Emacs already has the first part covered with M-= (calendar-count-days-region), so let's take a look at that function.
Helpful, but unfortunately it reads the buffer and sends the output directly. Let's make a generalized version which takes start and end date parameters and returns the number of days instead of printing them:
(defun my-calendar-count-days(d1 d2)
(let* ((days (- (calendar-absolute-from-gregorian d1)
(calendar-absolute-from-gregorian d2)))
(days (1+ (if (> days 0) days (- days)))))
days))
This is pretty much just a copy of the calendar-count-days-region function, but without the buffer reading & writing stuff. Some tests:
(ert-deftest test-count-days ()
"Test my-calendar-count-days function"
(should (equal (my-calendar-count-days '(5 1 2014) '(5 31 2014)) 31))
(should (equal (my-calendar-count-days '(12 29 2013) '(1 4 2014)) 7))
(should (equal (my-calendar-count-days '(2 28 2012) '(3 1 2012)) 3))
(should (equal (my-calendar-count-days '(2 28 2014) '(3 1 2014)) 2)))
Now, for step 2, I can't find any built-in function to calculate weekend days for a date range (surprisingly!). Luckily, this /might/ be pretty simple when working with absolute dates. Here's a very naive attempt which simply loops through all absolute dates in the range and looks for Saturdays & Sundays:
(defun my-calendar-count-weekend-days(date1 date2)
(let* ((tmp-date (if (< date1 date2) date1 date2))
(end-date (if (> date1 date2) date1 date2))
(weekend-days 0))
(while (<= tmp-date end-date)
(let ((day-of-week (calendar-day-of-week
(calendar-gregorian-from-absolute tmp-date))))
(if (or (= day-of-week 0)
(= day-of-week 6))
(incf weekend-days ))
(incf tmp-date)))
weekend-days))
That function should be optimized since it does a bunch of unnecessary looping (e.g. we know that the 5 days after Sunday won't be weekend days, so there is no need to convert & test them), but for the purpose of this example I think it's pretty clear and simple. Good Enough for now, indeed. Some tests:
(ert-deftest test-count-weekend-days ()
"Test my-calendar-count-weekend-days function"
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(5 1 2014))
(calendar-absolute-from-gregorian '(5 31 2014))) 9))
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(4 28 2014))
(calendar-absolute-from-gregorian '(5 2 2014))) 0))
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(2 27 2004))
(calendar-absolute-from-gregorian '(2 29 2004))) 2)))
Lastly, we need to know the holidays in the range, and emacs provides this in the holiday-in-range function! Note that this function calls calendar-holiday-list to determine which holidays to include, so if you really want to search only holiday-general-holidays and holiday-local-holidays you would need to set your calendar-holidays variable appropriately. See C-h v calendar-holidays for the details.
Now we can wrap all this up in a new interactive function which does the three steps above. This is essentially another modified version of calendar-count-days-region that subtracts weekends and holidays before printing the results (see edit below before running):
(defun calendar-count-days-region2 ()
"Count the number of days (inclusive) between point and the mark
excluding weekends and holidays."
(interactive)
(let* ((d1 (calendar-cursor-to-date t))
(d2 (car calendar-mark-ring))
(date1 (calendar-absolute-from-gregorian d1))
(date2 (calendar-absolute-from-gregorian d2))
(start-date (if (< date1 date2) date1 date2))
(end-date (if (> date1 date2) date1 date2))
(days (- (my-calendar-count-days d1 d2)
(+ (my-calendar-count-weekend-days start-date end-date)
(my-calendar-count-holidays-on-weekdays-in-range
start-date end-date)))))
(message "Region has %d workday%s (inclusive)"
days (if (> days 1) "s" ""))))
I'm sure someone more knowledgeable about lisp/elisp could simplify/improve these examples considerably, but I hope it at least serves as a starting point.
Actually, now that I've gone through it, I expect somebody to come along any minute and point out that there is an emacs package that already does this...
Edit: DOH!, Bug #001: If a holiday falls on a weekend, that day is removed twice...
Once solution would be to simply wrap holiday-in-range so we can eliminate holidays which were already removed for being on a weekend:
(defun my-calendar-count-holidays-on-weekdays-in-range (start end)
(let ((holidays (holiday-in-range start end))
(counter 0))
(dolist (element holidays)
(let ((day (calendar-day-of-week (car element))))
(if (and (> day 0)
(< day 6))
(incf counter))))
counter))
I've updated the calendar-count-days-region2 above to use this new function.
I'm looking for a way to select next or previous month in both agenda view and the calendar. I've written a concept/prototype function (below), but it doesn't calculate the next or previous months and I would also need to rewrite the function every month.
The date format for the org-agenda-month-view is different than the date format for calendar-other-month. Further down below are some functions that are related to what I'm trying to accomplish -- e.g., calendar already has the ability to move forward or backward by month.
I think what may be needed is a function that identifies the month being viewed and then adds plus-or-minus one month (in the proper format) when hitting the next or previous button.
(defun lawlist-org-agenda-view-mode-dispatch ()
"Select the month in agenda view."
(interactive)
(message "View: [7] JUL | [8] AUG | [9] SEP | [o]CT | [n]OV | [d]EC ")
(let ((a (read-char-exclusive)))
(case a
(?7
(org-agenda nil "a")
(org-agenda-month-view 201307)
(calendar)
(calendar-other-month 7 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?8
(org-agenda nil "a")
(org-agenda-month-view 201308)
(calendar)
(calendar-other-month 8 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?9
(org-agenda nil "a")
(org-agenda-month-view 201309)
(calendar)
(calendar-other-month 9 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?o
(org-agenda nil "a")
(org-agenda-month-view 201310)
(calendar)
(calendar-other-month 10 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?n
(org-agenda nil "a")
(org-agenda-month-view 201311)
(calendar)
(calendar-other-month 11 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?d
(org-agenda nil "a")
(org-agenda-month-view 201312)
(calendar)
(calendar-other-month 12 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?q (message "Abort"))
(otherwise (error "Either press \"q\" to quit, or select another option." )))))
Here are some related functions I've extracted from cal-move.el and calendar.el:
(defun calendar-other-month (month year &optional event)
"Display a three-month calendar centered around MONTH and YEAR.
EVENT is an event like `last-nonmenu-event'."
(interactive (let ((event (list last-nonmenu-event)))
(append (calendar-read-date 'noday) event)))
(save-selected-window
(and event
(setq event (event-start event))
(select-window (posn-window event)))
(unless (and (= month displayed-month)
(= year displayed-year))
(let ((old-date (calendar-cursor-to-date))
(today (calendar-current-date)))
(calendar-generate-window month year)
(calendar-cursor-to-visible-date
(cond
((calendar-date-is-visible-p old-date) old-date)
((calendar-date-is-visible-p today) today)
(t (list month 1 year))))))))
;;;###cal-autoload
(defun calendar-forward-month (arg)
"Move the cursor forward ARG months.
Movement is backward if ARG is negative."
(interactive "p")
(calendar-cursor-to-nearest-date)
(let* ((cursor-date (calendar-cursor-to-date t))
(month (calendar-extract-month cursor-date))
(day (calendar-extract-day cursor-date))
(year (calendar-extract-year cursor-date))
(last (progn
(calendar-increment-month month year arg)
(calendar-last-day-of-month month year)))
(day (min last day))
;; Put the new month on the screen, if needed, and go to the new date.
(new-cursor-date (list month day year)))
(if (not (calendar-date-is-visible-p new-cursor-date))
(calendar-other-month month year))
(calendar-cursor-to-visible-date new-cursor-date))
(run-hooks 'calendar-move-hook))
;;;###cal-autoload
(defun calendar-backward-month (arg)
"Move the cursor backward by ARG months.
Movement is forward if ARG is negative."
(interactive "p")
(calendar-forward-month (- arg)))
;;;###cal-autoload
(defun calendar-forward-year (arg)
"Move the cursor forward by ARG years.
Movement is backward if ARG is negative."
(interactive "p")
(calendar-forward-month (* 12 arg)))
;;;###cal-autoload
(defun calendar-backward-year (arg)
"Move the cursor backward ARG years.
Movement is forward is ARG is negative."
(interactive "p")
(calendar-forward-month (* -12 arg)))
;;;###cal-autoload
(defun calendar-scroll-left (&optional arg event)
"Scroll the displayed calendar left by ARG months.
If ARG is negative the calendar is scrolled right. Maintains the relative
position of the cursor with respect to the calendar as well as possible.
EVENT is an event like `last-nonmenu-event'."
(interactive (list (prefix-numeric-value current-prefix-arg)
last-nonmenu-event))
(unless arg (setq arg 1))
(save-selected-window
;; Nil if called from menu-bar.
(if (setq event (event-start event)) (select-window (posn-window event)))
(calendar-cursor-to-nearest-date)
(unless (zerop arg)
(let ((old-date (calendar-cursor-to-date))
(today (calendar-current-date))
(month displayed-month)
(year displayed-year))
(calendar-increment-month month year arg)
(calendar-generate-window month year)
(calendar-cursor-to-visible-date
(cond
((calendar-date-is-visible-p old-date) old-date)
((calendar-date-is-visible-p today) today)
(t (list month 1 year))))))
(run-hooks 'calendar-move-hook)))
(define-obsolete-function-alias
'scroll-calendar-left 'calendar-scroll-left "23.1")
;;;###cal-autoload
(defun calendar-scroll-right (&optional arg event)
"Scroll the displayed calendar window right by ARG months.
If ARG is negative the calendar is scrolled left. Maintains the relative
position of the cursor with respect to the calendar as well as possible.
EVENT is an event like `last-nonmenu-event'."
(interactive (list (prefix-numeric-value current-prefix-arg)
last-nonmenu-event))
(calendar-scroll-left (- (or arg 1)) event))
(define-obsolete-function-alias
'scroll-calendar-right 'calendar-scroll-right "23.1")
Here's my take:
(defvar lawlist-month)
(defun lawlist-org-agenda-view-mode-dispatch ()
"Select the month in agenda view."
(interactive)
(message "View: [1-9] [o]CT [n]OV [d]EC, j(next), k(prev).")
(let* ((a (read-char-exclusive))
(month (case a
(?o 10)
(?n 11)
(?d 12)
(?j (or (and lawlist-month (mod (1+ lawlist-month) 12)) 1))
(?k (or (and lawlist-month (mod (1- lawlist-month) 12)) 1))
(t (and (> a ?0) (<= a ?9) (- a ?0))))))
(if (setq lawlist-month month)
(let ((year (nth 5 (decode-time (current-time)))))
(org-agenda nil "a")
(org-agenda-month-view
(read (format "%d%02d" year month)))
(calendar)
(calendar-other-month month year)
(lawlist-org-agenda-view-mode-dispatch))
(message "Aborted"))))
It still misses some functionality like saving the window configuration and
recovering on abort.
UPD
The updated code can be found in this gist.
I've added other years besides current, with support for j/k, as well as h/l for years.
Can I let Emacs automatically load theme ? or do certain command at customized time ? Say what I want is to M-x load-theme RET solarized-light when I am at office at 9:00am and M-x laod-theme RET solarized-dark when I am back home and continued on emacs at 8:00pm.
To expand on #Anton Kovalenko's answer, you can get the current time using the current-time-string elisp function and extracting the current time of day in hours.
If you want to write a full implementation, you could do something like (Warning, not debugged):
;; <Color theme initialization code>
(setq current-theme '(color-theme-solarized-light))
(defun synchronize-theme ()
(setq hour
(string-to-number
(substring (current-time-string) 11 13)))
(if (member hour (number-sequence 6 17))
(setq now '(color-theme-solarized-light))
(setq now '(color-theme-solarized-dark)))
(if (equal now current-theme)
nil
(setq current-theme now)
(eval now) ) ) ;; end of (defun ...
(run-with-timer 0 3600 synchronize-theme)
For more info on the functions used, see the following sections of the emacs manual:
Time of day
Strings
String Conversions
Idle Timers
Contains
Number Sequence
Another (very elegant) solution is theme-changer.
Given a location and day/night color themes, this file provides a change-theme function that selects the appropriate theme based on whether it is day or night. It will continue to change themes at sunrise and sunset. To install:
Set the location:
(setq calendar-location-name "Dallas, TX")
(setq calendar-latitude 32.85)
(setq calendar-longitude -96.85)
Specify the day and night themes:
(require 'theme-changer)
(change-theme 'tango 'tango-dark)
The project is hosted on Github, and can be installed through melpa.
You can use this snippet of code to do what you want.
(defvar install-theme-loading-times nil
"An association list of time strings and theme names.
The themes will be loaded at the specified time every day.")
(defvar install-theme-timers nil)
(defun install-theme-loading-at-times ()
"Set up theme loading according to `install-theme-loading-at-times`"
(interactive)
(dolist (timer install-theme-timers)
(cancel-timer timer))
(setq install-theme-timers nil)
(dolist (time-theme install-theme-loading-times)
(add-to-list 'install-theme-timers
(run-at-time (car time-theme) (* 60 60 24) 'load-theme (cdr time-theme)))))
Just customize the variable install-theme-loading-times as desired:
(setq install-theme-loading-times '(("9:00am" . solarized-light)
("8:00pm" . solarized-dark)))
You can start with run-with-timer function:
(run-with-timer SECS REPEAT FUNCTION &rest ARGS)
Perform an action after a delay of SECS seconds.
Repeat the action every REPEAT seconds, if REPEAT is non-nil.
SECS and REPEAT may be integers or floating point numbers.
The action is to call FUNCTION with arguments ARGS.
This function returns a timer object which you can use in `cancel-timer'.
Schedule a function to run every minute or so, which will check
current time and call load-theme when appropriate (don't switch
theme every minute, even if it's reloading the current theme).
Found this simple code that works for doom emacs. Put this in the config file:
(load-theme 'solarized-light t t) ;;load light theme
(run-at-time "09:00" (* 60 60 24) (lambda () (enable-theme 'solarized-light)))
(load-theme 'solarized-dark t t) ;;load dark theme
(run-at-time "20:00" (* 60 60 24) (lambda () (enable-theme 'solarized-dark)))
replace light and dark themes with your choice. time can also be changed from 9am/8pm in 24-hour format.
source and credit: https://parasurv.neocities.org/emacs/change-emacs-theme-depending-on-time.html
This implementation changes theme based on sunrise and sunset times of the latitude and longitude you provide. The only dependency is solar.el which is released with Emacs (IIRC).
(I think the code can probably be shorter here.)
;; theme changing at sunrises and sunsets according to lat and long
(require 'solar)
(defun today-date-integer (offset)
"Returns today's date in a list of integers, i.e. month, date, and year, in system time."
(let* ((date (mapcar
(lambda (pattern)
(string-to-number (format-time-string pattern)))
'("%m" "%d" "%Y"))))
(setcar
(nthcdr
1
date)
(+ offset (nth 1 date)))
date))
(defun current-time-decimal ()
(let* ((current-min-fraction (/ (string-to-number (format-time-string "%M")) 60.0))
(current-hour (string-to-number (format-time-string "%H"))))
(+ current-hour current-min-fraction)))
(defun next-alarm-time (sunrise-time sunset-time)
(let* ((current-time (current-time-decimal)))
(cond ((< current-time sunrise-time)
(- sunrise-time current-time))
((and (>= current-time sunrise-time)
(< current-time sunset-time))
(- sunset-time current-time))
((>= current-time sunset-time)
(let ((tomorrow-sunrise-time (car (car (solar-sunrise-sunset (today-date-integer 1))))))
(- (+ 24 tomorrow-sunrise-time) current-time))))))
(defun to-seconds (hour) (* hour 60 60))
(defun change-theme (light-theme dark-theme coor)
(let* ((_ (setq calendar-latitude (car coor)))
( _ (setq calendar-longitude (nth 1 coor)))
(today-date (today-date-integer 0))
(sunrise-sunset-list (solar-sunrise-sunset today-date))
(sunrise-time (car (car sunrise-sunset-list)))
(sunset-time (car (nth 1 sunrise-sunset-list)))
(current-time (current-time-decimal))
(current-theme (if (or (< current-time sunrise-time) (> current-time sunset-time))
dark-theme
light-theme))
(next-alarm-t (next-alarm-time sunrise-time sunset-time)))
(cancel-function-timers 'change-theme)
(load-theme current-theme t)
(run-at-time
(to-seconds next-alarm-t) nil 'change-theme light-theme dark-theme coor)))
(change-theme 'solarized-gruvbox-light 'solarized-gruvbox-dark '(47.6062 -122.3321))