diff options
| author | Paul Eggert | 1993-08-28 04:14:25 +0000 |
|---|---|---|
| committer | Paul Eggert | 1993-08-28 04:14:25 +0000 |
| commit | 6bc457fea523673a7f898e9e14d3c652b3d66653 (patch) | |
| tree | 36977646fa6034db81405256cf507c9405212d85 | |
| parent | 04d5d338d295fa4f7c19f480f2e123c297f02f39 (diff) | |
| download | emacs-6bc457fea523673a7f898e9e14d3c652b3d66653.tar.gz emacs-6bc457fea523673a7f898e9e14d3c652b3d66653.zip | |
(calendar-time-zone-daylight-rules): Scan through the
next few years until at most one rule remains; if none remain, then
just use the first candidate rule; it's wrong in general, but it's
right for at least one year. This is a better heuristic in case the
underlying time zone implementation has bugs (which is all too
common). If possible, don't convert back and forth between gregorian
and absolute; this speeds things up noticeably. This uses the new
calendar-nth-named-absday function.
(calendar-current-time-zone): Some locales start DST at a different
time of day than they end; allow for this by yielding both times. The
performance speedups in calendar.el are great enough that we now no
longer need the "Checking time zone data..." message. If
current-time-zone yields nil, don't bother with
calendar-next-time-zone-transition. Use clearer names for local vars.
(calendar-time-zone, calendar-daylight-time-offset,
calendar-{standard,daylight}-time-zone-name,
calendar-daylight-savings-{starts,ends}): Default to US Eastern rules
for information that is not available.
(calendar-daylight-savings-{starts,ends}-time): New vars, replacing
calendar-daylight-savings-switchover-time, to support locales that
start DST at a different time of day than they end.
(calendar-absolute-from-time):
Fix typo by interchanging floor and mod.
| -rw-r--r-- | lisp/calendar/cal-dst.el | 169 |
1 files changed, 94 insertions, 75 deletions
diff --git a/lisp/calendar/cal-dst.el b/lisp/calendar/cal-dst.el index 62ca6b089a1..2608a15c17c 100644 --- a/lisp/calendar/cal-dst.el +++ b/lisp/calendar/cal-dst.el | |||
| @@ -62,9 +62,9 @@ absolute date ABS-DATE is the equivalent moment to X." | |||
| 62 | ;; Overflow is a terrible thing! | 62 | ;; Overflow is a terrible thing! |
| 63 | (cons (+ calendar-system-time-basis | 63 | (cons (+ calendar-system-time-basis |
| 64 | ;; floor((2^16 h +l) / (60*60*24)) | 64 | ;; floor((2^16 h +l) / (60*60*24)) |
| 65 | (* 512 (mod h 675)) (floor u 675)) | 65 | (* 512 (floor h 675)) (floor u 675)) |
| 66 | ;; (2^16 h +l) % (60*60*24) | 66 | ;; (2^16 h +l) % (60*60*24) |
| 67 | (+ (* (mod u 675) 128) (floor l 128))))) | 67 | (+ (* (mod u 675) 128) (mod l 128))))) |
| 68 | 68 | ||
| 69 | (defun calendar-time-from-absolute (abs-date s) | 69 | (defun calendar-time-from-absolute (abs-date s) |
| 70 | "Time of absolute date ABS-DATE, S seconds after midnight. | 70 | "Time of absolute date ABS-DATE, S seconds after midnight. |
| @@ -169,32 +169,44 @@ The result has the proper form for calendar-daylight-savings-starts'." | |||
| 169 | (- (calendar-absolute-from-hebrew | 169 | (- (calendar-absolute-from-hebrew |
| 170 | (list 7 1 (+ year 3761))) 3)))))))) | 170 | (list 7 1 (+ year 3761))) 3)))))))) |
| 171 | (prevday-sec (- -1 utc-diff)) ;; last sec of previous local day | 171 | (prevday-sec (- -1 utc-diff)) ;; last sec of previous local day |
| 172 | last-surviving-rule | 172 | (year (1+ y))) |
| 173 | (i 1)) | 173 | ;; Scan through the next few years until only one rule remains. |
| 174 | ;; Scan through the next few years; take the rule that explains them best. | 174 | (while |
| 175 | (while (and candidate-rules (cdr candidate-rules) (<= i 28)) | 175 | (let ((rules candidate-rules) |
| 176 | (let ((year (+ y i)) | 176 | new-rules) |
| 177 | new-rules) | 177 | (while |
| 178 | (while candidate-rules | 178 | (let* |
| 179 | (let* ((rule (car candidate-rules)) | 179 | ((rule (car rules)) |
| 180 | (date (calendar-absolute-from-gregorian (eval rule)))) | 180 | (date |
| 181 | (or (equal (current-time-zone | 181 | ;; The following is much faster than |
| 182 | (calendar-time-from-absolute date prevday-sec)) | 182 | ;; (calendar-absolute-from-gregorian (eval rule)). |
| 183 | (current-time-zone | 183 | (cond ((eq (car rule) 'calendar-nth-named-day) |
| 184 | (calendar-time-from-absolute (1+ date) prevday-sec))) | 184 | (eval (cons 'calendar-nth-named-absday (cdr rule)))) |
| 185 | (progn | 185 | ((eq (car rule) 'calendar-gregorian-from-absolute) |
| 186 | (setq new-rules (cons rule new-rules)) | 186 | (eval (car (cdr rule)))) |
| 187 | (setq last-surviving-rule rule)))) | 187 | (t (let ((g (eval rule))) |
| 188 | (setq candidate-rules (cdr candidate-rules))) | 188 | (calendar-absolute-from-gregorian g)))))) |
| 189 | (setq candidate-rules (nreverse new-rules))) | 189 | (or (equal |
| 190 | (setq i (1+ i))) | 190 | (current-time-zone |
| 191 | last-surviving-rule)) | 191 | (calendar-time-from-absolute date prevday-sec)) |
| 192 | (current-time-zone | ||
| 193 | (calendar-time-from-absolute (1+ date) prevday-sec))) | ||
| 194 | (setq new-rules (cons rule new-rules))) | ||
| 195 | (setq rules (cdr rules)))) | ||
| 196 | ;; If no rules remain, just use the first candidate rule; | ||
| 197 | ;; it's wrong in general, but it's right for at least one year. | ||
| 198 | (setq candidate-rules (if new-rules (nreverse new-rules) | ||
| 199 | (list (car candidate-rules)))) | ||
| 200 | (setq year (1+ year)) | ||
| 201 | (cdr candidate-rules))) | ||
| 202 | (car candidate-rules))) | ||
| 192 | 203 | ||
| 193 | (defun calendar-current-time-zone () | 204 | (defun calendar-current-time-zone () |
| 194 | "Return UTC difference, dst offset, names and rules for current time zone. | 205 | "Return UTC difference, dst offset, names and rules for current time zone. |
| 195 | 206 | ||
| 196 | Returns (UTC-DIFF DST-OFFSET STD-ZONE DST-ZONE DST-STARTS DST-ENDS DST-SWITCH), | 207 | Returns (UTC-DIFF DST-OFFSET STD-ZONE DST-ZONE DST-STARTS DST-ENDS |
| 197 | based on a heuristic probing of what the system knows: | 208 | DST-STARTS-TIME DST-ENDS-TIME), based on a heuristic probing of what the |
| 209 | system knows: | ||
| 198 | 210 | ||
| 199 | UTC-DIFF is an integer specifying the number of minutes difference between | 211 | UTC-DIFF is an integer specifying the number of minutes difference between |
| 200 | standard time in the current time zone and Coordinated Universal Time | 212 | standard time in the current time zone and Coordinated Universal Time |
| @@ -205,87 +217,88 @@ STD-ZONE is a string giving the name of the time zone when no seasonal time | |||
| 205 | DST-ZONE is a string giving the name of the time zone when there is a seasonal | 217 | DST-ZONE is a string giving the name of the time zone when there is a seasonal |
| 206 | time adjustment in effect. | 218 | time adjustment in effect. |
| 207 | DST-STARTS and DST-ENDS are sexps in the variable `year' giving the daylight | 219 | DST-STARTS and DST-ENDS are sexps in the variable `year' giving the daylight |
| 208 | savings time start rules, in the form expected by | 220 | savings time start and end rules, in the form expected by |
| 209 | `calendar-daylight-savings-starts'. | 221 | `calendar-daylight-savings-starts'. |
| 210 | DST-SWITCH is an integer giving the number of minutes after midnight that | 222 | DST-STARTS-TIME and DST-ENDS-TIME are integers giving the number of minutes |
| 211 | daylight savings time starts or ends. | 223 | after midnight that daylight savings time starts and ends. |
| 212 | 224 | ||
| 213 | If the local area does not use a seasonal time adjustment, DST-OFFSET and | 225 | If the local area does not use a seasonal time adjustment, STD-ZONE and |
| 214 | DST-SWITCH are 0, STD-ZONE and DST-ZONE are equal, and DST-STARTS and DST-ENDS | 226 | DST-ZONE are equal, and all the DST-* integer variables are 0. |
| 215 | are nil. | ||
| 216 | 227 | ||
| 217 | Some operating systems cannot provide all this information to Emacs; in this | 228 | Some operating systems cannot provide all this information to Emacs; in this |
| 218 | case, `calendar-current-time-zone' returns a list containing nil for the data | 229 | case, `calendar-current-time-zone' returns a list containing nil for the data |
| 219 | it can't find." | 230 | it can't find." |
| 220 | (or | 231 | (or |
| 221 | calendar-current-time-zone-cache | 232 | calendar-current-time-zone-cache |
| 222 | (progn | 233 | (setq |
| 223 | (message "Checking time zone data...") | 234 | calendar-current-time-zone-cache |
| 224 | (setq | 235 | (let* ((t0 (current-time)) |
| 225 | calendar-current-time-zone-cache | 236 | (t0-zone (current-time-zone t0)) |
| 226 | (let* ((now (current-time)) | 237 | (t0-utc-diff (car t0-zone)) |
| 227 | (now-zone (current-time-zone now)) | 238 | (t0-name (car (cdr t0-zone)))) |
| 228 | (now-utc-diff (car now-zone)) | 239 | (if (not t0-utc-diff) |
| 229 | (now-name (car (cdr now-zone))) | 240 | ;; Little or no time zone information is available. |
| 230 | (next (calendar-next-time-zone-transition now))) | 241 | (list nil nil t0-name t0-name nil nil nil nil) |
| 231 | (if (null next) | 242 | (let* ((t1 (calendar-next-time-zone-transition t0)) |
| 232 | (list (and now-utc-diff (/ now-utc-diff 60)) | 243 | (t2 (and t1 (calendar-next-time-zone-transition t1)))) |
| 233 | 0 now-name now-name nil nil 0) | 244 | (if (not t2) |
| 234 | (let* ((next-zone (current-time-zone next)) | 245 | ;; This locale does not have daylight savings time. |
| 235 | (next-utc-diff (car next-zone)) | 246 | (list (/ t0-utc-diff 60) 0 t0-name t0-name nil nil 0 0) |
| 236 | (next-name (car (cdr next-zone))) | 247 | ;; Use heuristics to find daylight savings parameters. |
| 237 | (next-absdate-seconds | 248 | (let* ((t1-zone (current-time-zone t1)) |
| 238 | (calendar-absolute-from-time next now-utc-diff)) | 249 | (t1-utc-diff (car t1-zone)) |
| 239 | (next-transitions | 250 | (t1-name (car (cdr t1-zone))) |
| 240 | (calendar-time-zone-daylight-rules | 251 | (t1-date-sec (calendar-absolute-from-time t1 t0-utc-diff)) |
| 241 | (car next-absdate-seconds) now-utc-diff)) | 252 | (t2-date-sec (calendar-absolute-from-time t2 t1-utc-diff)) |
| 242 | (nextnext (calendar-next-time-zone-transition next)) | 253 | (t1-rules (calendar-time-zone-daylight-rules |
| 243 | (now-transitions | 254 | (car t1-date-sec) t0-utc-diff)) |
| 244 | (calendar-time-zone-daylight-rules | 255 | (t2-rules (calendar-time-zone-daylight-rules |
| 245 | (car (calendar-absolute-from-time nextnext next-utc-diff)) | 256 | (car t2-date-sec) t1-utc-diff)) |
| 246 | next-utc-diff)) | 257 | (t1-time (/ (cdr t1-date-sec) 60)) |
| 247 | (now-is-std (< now-utc-diff next-utc-diff))) | 258 | (t2-time (/ (cdr t2-date-sec) 60))) |
| 248 | (list (/ (min now-utc-diff next-utc-diff) 60) | 259 | (cons |
| 249 | (/ (abs (- now-utc-diff next-utc-diff)) 60) | 260 | (/ (min t0-utc-diff t1-utc-diff) 60) |
| 250 | (if now-is-std now-name next-name) | 261 | (cons |
| 251 | (if now-is-std next-name now-name) | 262 | (/ (abs (- t0-utc-diff t1-utc-diff)) 60) |
| 252 | (if now-is-std next-transitions now-transitions) | 263 | (if (< t0-utc-diff t1-utc-diff) |
| 253 | (if now-is-std now-transitions next-transitions) | 264 | (list t0-name t1-name t1-rules t2-rules t2-time t1-time) |
| 254 | (/ (cdr next-absdate-seconds) 60)))))) | 265 | (list t1-name t0-name t2-rules t1-rules t1-time t2-time) |
| 255 | (message "Checking time zone data...done"))) | 266 | ))))))))))) |
| 256 | calendar-current-time-zone-cache) | ||
| 257 | 267 | ||
| 258 | ;;; The following six defvars relating to daylight savings time should NOT be | 268 | ;;; The following six defvars relating to daylight savings time should NOT be |
| 259 | ;;; marked to go into loaddefs.el where they would be evaluated when Emacs is | 269 | ;;; marked to go into loaddefs.el where they would be evaluated when Emacs is |
| 260 | ;;; dumped. These variables' appropriate values depend on the conditions under | 270 | ;;; dumped. These variables' appropriate values depend on the conditions under |
| 261 | ;;; which the code is INVOKED; so it's inappropriate to initialize them when | 271 | ;;; which the code is INVOKED; so it's inappropriate to initialize them when |
| 262 | ;;; Emacs is dumped---they should be initialized when calendar.el is loaded. | 272 | ;;; Emacs is dumped---they should be initialized when calendar.el is loaded. |
| 273 | ;;; They default to US Eastern time if time zone info is not available. | ||
| 263 | 274 | ||
| 264 | (calendar-current-time-zone) | 275 | (calendar-current-time-zone) |
| 265 | 276 | ||
| 266 | (defvar calendar-time-zone (car calendar-current-time-zone-cache) | 277 | (defvar calendar-time-zone (or (car calendar-current-time-zone-cache) -300) |
| 267 | "*Number of minutes difference between local standard time at | 278 | "*Number of minutes difference between local standard time at |
| 268 | `calendar-location-name' and Coordinated Universal (Greenwich) Time. For | 279 | `calendar-location-name' and Coordinated Universal (Greenwich) Time. For |
| 269 | example, -300 for New York City, -480 for Los Angeles.") | 280 | example, -300 for New York City, -480 for Los Angeles.") |
| 270 | 281 | ||
| 271 | (defvar calendar-daylight-time-offset | 282 | (defvar calendar-daylight-time-offset |
| 272 | (car (cdr calendar-current-time-zone-cache)) | 283 | (or (car (cdr calendar-current-time-zone-cache)) 60) |
| 273 | "*Number of minutes difference between daylight savings and standard time. | 284 | "*Number of minutes difference between daylight savings and standard time. |
| 274 | 285 | ||
| 275 | If the locale never uses daylight savings time, set this to 0.") | 286 | If the locale never uses daylight savings time, set this to 0.") |
| 276 | 287 | ||
| 277 | (defvar calendar-standard-time-zone-name | 288 | (defvar calendar-standard-time-zone-name |
| 278 | (car (nthcdr 2 calendar-current-time-zone-cache)) | 289 | (or (car (nthcdr 2 calendar-current-time-zone-cache)) "EST") |
| 279 | "*Abbreviated name of standard time zone at `calendar-location-name'. | 290 | "*Abbreviated name of standard time zone at `calendar-location-name'. |
| 280 | For example, \"EST\" in New York City, \"PST\" for Los Angeles.") | 291 | For example, \"EST\" in New York City, \"PST\" for Los Angeles.") |
| 281 | 292 | ||
| 282 | (defvar calendar-daylight-time-zone-name | 293 | (defvar calendar-daylight-time-zone-name |
| 283 | (car (nthcdr 3 calendar-current-time-zone-cache)) | 294 | (or (car (nthcdr 3 calendar-current-time-zone-cache)) "EDT") |
| 284 | "*Abbreviated name of daylight-savings time zone at `calendar-location-name'. | 295 | "*Abbreviated name of daylight-savings time zone at `calendar-location-name'. |
| 285 | For example, \"EDT\" in New York City, \"PDT\" for Los Angeles.") | 296 | For example, \"EDT\" in New York City, \"PDT\" for Los Angeles.") |
| 286 | 297 | ||
| 287 | (defvar calendar-daylight-savings-starts | 298 | (defvar calendar-daylight-savings-starts |
| 288 | (car (nthcdr 4 calendar-current-time-zone-cache)) | 299 | (or (car (nthcdr 4 calendar-current-time-zone-cache)) |
| 300 | (and (not (zerop calendar-daylight-time-offset)) | ||
| 301 | '(calendar-nth-named-day 1 0 4 year))) | ||
| 289 | "*Sexp giving the date on which daylight savings time starts. | 302 | "*Sexp giving the date on which daylight savings time starts. |
| 290 | This is an expression in the variable `year' whose value gives the Gregorian | 303 | This is an expression in the variable `year' whose value gives the Gregorian |
| 291 | date in the form (month day year) on which daylight savings time starts. It is | 304 | date in the form (month day year) on which daylight savings time starts. It is |
| @@ -310,7 +323,9 @@ because Nisan is the first month in the Hebrew calendar. | |||
| 310 | If the locale never uses daylight savings time, set this to nil.") | 323 | If the locale never uses daylight savings time, set this to nil.") |
| 311 | 324 | ||
| 312 | (defvar calendar-daylight-savings-ends | 325 | (defvar calendar-daylight-savings-ends |
| 313 | (car (nthcdr 5 calendar-current-time-zone-cache)) | 326 | (or (car (nthcdr 5 calendar-current-time-zone-cache)) |
| 327 | (and (not (zerop calendar-daylight-time-offset)) | ||
| 328 | '(calendar-nth-named-day -1 0 10 year))) | ||
| 314 | "*Sexp giving the date on which daylight savings time ends. | 329 | "*Sexp giving the date on which daylight savings time ends. |
| 315 | This is an expression in the variable `year' whose value gives the Gregorian | 330 | This is an expression in the variable `year' whose value gives the Gregorian |
| 316 | date in the form (month day year) on which daylight savings time ends. It is | 331 | date in the form (month day year) on which daylight savings time ends. It is |
| @@ -327,10 +342,14 @@ begins: | |||
| 327 | 342 | ||
| 328 | If the locale never uses daylight savings time, set this to nil.") | 343 | If the locale never uses daylight savings time, set this to nil.") |
| 329 | 344 | ||
| 330 | (defvar calendar-daylight-savings-switchover-time | 345 | (defvar calendar-daylight-savings-starts-time |
| 331 | (car (nthcdr 6 calendar-current-time-zone-cache)) | 346 | (or (car (nthcdr 6 calendar-current-time-zone-cache)) 120) |
| 332 | "*Number of minutes after midnight that daylight savings time begins/ends. | 347 | "*Number of minutes after midnight that daylight savings time starts.") |
| 333 | If the locale never uses daylight savings time, set this to 0.") | 348 | |
| 349 | (defvar calendar-daylight-savings-ends-time | ||
| 350 | (or (car (nthcdr 7 calendar-current-time-zone-cache)) | ||
| 351 | calendar-daylight-savings-starts-time) | ||
| 352 | "*Number of minutes after midnight that daylight savings time ends.") | ||
| 334 | 353 | ||
| 335 | (provide 'cal-dst) | 354 | (provide 'cal-dst) |
| 336 | 355 | ||