source: projects/emacsen-common/trunk/apel-sample/usr/share/emacs/site-lisp/apel/timezone.el @ 7238

Revision 7238, 17.2 KB checked in by daisuke, 12 years ago (diff)

import emacsen-common

Line 
1;;; timezone.el --- time zone package for GNU Emacs
2
3;; Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
4
5;; Author: Masanobu Umeda
6;; Maintainer: umerin@mse.kyutech.ac.jp
7;; Keywords: news
8
9;; This file is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation; either version 2, or (at your option)
12;; any later version.
13
14;; This file is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs; see the file COPYING.  If not, write to
21;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22
23;;; Commentary:
24
25;; Modified 1 February 1994 by Kyle Jones to fix broken
26;; timezone-floor function.
27
28;; Modified 25 January 1994 by Kyle Jones so that it will
29;; work under version 18 of Emacs.  Provided timezone-floor
30;; and timezone-abs functions.
31
32;; Modified 4 October 1999 by Yuuichi Teranishi so that it will
33;; work with old GNUS 3.14.4 under version 18 of Emacs.
34
35;;; Code:
36
37(defvar timezone-world-timezones
38  '(("PST" .  -800)
39    ("PDT" .  -700)
40    ("MST" .  -700)
41    ("MDT" .  -600)
42    ("CST" .  -600)
43    ("CDT" .  -500)
44    ("EST" .  -500)
45    ("EDT" .  -400)
46    ("AST" .  -400)                     ;by <clamen@CS.CMU.EDU>
47    ("NST" .  -330)                     ;by <clamen@CS.CMU.EDU>
48    ("UT"  .  +000)
49    ("GMT" .  +000)
50    ("BST" .  +100)
51    ("MET" .  +100)
52    ("EET" .  +200)
53    ("JST" .  +900)
54    ("GMT+1"  .  +100) ("GMT+2"  .  +200) ("GMT+3"  .  +300)
55    ("GMT+4"  .  +400) ("GMT+5"  .  +500) ("GMT+6"  .  +600)
56    ("GMT+7"  .  +700) ("GMT+8"  .  +800) ("GMT+9"  .  +900)
57    ("GMT+10" . +1000) ("GMT+11" . +1100) ("GMT+12" . +1200) ("GMT+13" . +1300)
58    ("GMT-1"  .  -100) ("GMT-2"  .  -200) ("GMT-3"  .  -300)
59    ("GMT-4"  .  -400) ("GMT-5"  .  -500) ("GMT-6"  .  -600)
60    ("GMT-7"  .  -700) ("GMT-8"  .  -800) ("GMT-9"  .  -900)
61    ("GMT-10" . -1000) ("GMT-11" . -1100) ("GMT-12" . -1200))
62  "*Time differentials of timezone from GMT in +-HHMM form.
63This list is obsolescent, and is present only for backwards compatibility,
64because time zone names are ambiguous in practice.
65Use `current-time-zone' instead.")
66
67(defvar timezone-months-assoc
68  '(("JAN" .  1)("FEB" .  2)("MAR" .  3)
69    ("APR" .  4)("MAY" .  5)("JUN" .  6)
70    ("JUL" .  7)("AUG" .  8)("SEP" .  9)
71    ("OCT" . 10)("NOV" . 11)("DEC" . 12))
72  "Alist of first three letters of a month and its numerical representation.")
73
74(defun timezone-make-date-arpa-standard (date &optional local timezone)
75  "Convert DATE to an arpanet standard date.
76Optional 2nd argument LOCAL specifies the default local timezone of the DATE;
77if nil, GMT is assumed.
78Optional 3rd argument TIMEZONE specifies a time zone to be represented in;
79if nil, the local time zone is assumed."
80  (let ((new (timezone-fix-time date local timezone)))
81    (timezone-make-arpa-date (aref new 0) (aref new 1) (aref new 2)
82                             (timezone-make-time-string
83                              (aref new 3) (aref new 4) (aref new 5))
84                             (aref new 6))
85    ))
86
87(defun timezone-make-date-sortable (date &optional local timezone)
88  "Convert DATE to a sortable date string.
89Optional 2nd argument LOCAL specifies the default local timezone of the DATE;
90if nil, GMT is assumed.
91Optional 3rd argument TIMEZONE specifies a timezone to be represented in;
92if nil, the local time zone is assumed."
93  (let ((new (timezone-fix-time date local timezone)))
94    (timezone-make-sortable-date (aref new 0) (aref new 1) (aref new 2)
95                                 (timezone-make-time-string
96                                  (aref new 3) (aref new 4) (aref new 5)))
97    ))
98
99
100;;
101;; Parsers and Constructors of Date and Time
102;;
103
104(defun timezone-make-arpa-date (year month day time &optional timezone)
105  "Make arpanet standard date string from YEAR, MONTH, DAY, and TIME.
106Optional argument TIMEZONE specifies a time zone."
107  (let ((zone
108         (if (listp timezone)
109             (let* ((m (timezone-zone-to-minute timezone))
110                    (absm (if (< m 0) (- m) m)))
111               (format "%c%02d%02d"
112                       (if (< m 0) ?- ?+) (/ absm 60) (% absm 60)))
113           timezone)))
114    (format "%02d %s %04d %s %s"
115            day
116            (capitalize (car (rassq month timezone-months-assoc)))
117            year
118            time
119            zone)))
120
121(defun timezone-make-sortable-date (year month day time)
122  "Make sortable date string from YEAR, MONTH, DAY, and TIME."
123  (format "%4d%02d%02d%s"
124          year month day time))
125
126(defun timezone-make-time-string (hour minute second)
127  "Make time string from HOUR, MINUTE, and SECOND."
128  (format "%02d:%02d:%02d" hour minute second))
129
130(defun timezone-parse-date (date)
131  "Parse DATE and return a vector [YEAR MONTH DAY TIME TIMEZONE].
13219 is prepended to year if necessary.  Timezone may be nil if nothing.
133Understands the following styles:
134 (1) 14 Apr 89 03:20[:12] [GMT]
135 (2) Fri, 17 Mar 89 4:01[:33] [GMT]
136 (3) Mon Jan 16 16:12[:37] [GMT] 1989
137 (4) 6 May 1992 1641-JST (Wednesday)
138 (5) 22-AUG-1993 10:59:12.82
139 (6) Thu, 11 Apr 16:17:12 91 [MET]
140 (7) Mon, 6  Jul 16:47:20 T 1992 [MET]"
141  (condition-case nil
142      (progn
143  ;; Get rid of any text properties.
144  (and (stringp date)
145       (or (text-properties-at 0 date)
146           (next-property-change 0 date))
147       (setq date (copy-sequence date))
148       (set-text-properties 0 (length date) nil date))
149  (let ((date (or date ""))
150        (year nil)
151        (month nil)
152        (day nil)
153        (time nil)
154        (zone nil))                     ;This may be nil.
155    (cond ((string-match
156            "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\'" date)
157           ;; Styles: (6) and (7) without timezone
158           (setq year 6 month 3 day 2 time 4 zone nil))
159          ((string-match
160            "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
161           ;; Styles: (6) and (7) with timezone and buggy timezone
162           (setq year 6 month 3 day 2 time 4 zone 7))
163          ((string-match
164            "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]*\\'" date)
165           ;; Styles: (1) and (2) without timezone
166           (setq year 3 month 2 day 1 time 4 zone nil))
167          ((string-match
168            "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
169           ;; Styles: (1) and (2) with timezone and buggy timezone
170           (setq year 3 month 2 day 1 time 4 zone 5))
171          ((string-match
172            "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([0-9]+\\)" date)
173           ;; Styles: (3) without timezone
174           (setq year 4 month 1 day 2 time 3 zone nil))
175          ((string-match
176            "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)[ \t]+\\([0-9]+\\)" date)
177           ;; Styles: (3) with timezone
178           (setq year 5 month 1 day 2 time 3 zone 4))
179          ((string-match
180            "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
181           ;; Styles: (4) with timezone
182           (setq year 3 month 2 day 1 time 4 zone 5))
183          ((string-match
184            "\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)\\.[0-9]+" date)
185           ;; Styles: (5) without timezone.
186           (setq year 3 month 2 day 1 time 4 zone nil))
187          )
188    (if year
189        (progn
190          (setq year
191                (substring date (match-beginning year) (match-end year)))
192          ;; It is now Dec 1992.  8 years before the end of the World.
193          (if (< (length year) 4)
194              ;; 2 digit years are bogus, so guess the century
195              (let ((yr (string-to-int year)))
196                (when (>= yr 100)
197                  ;; What does a three digit year mean?
198                  (setq yr (- yr 100)))
199                (setq year (format "%d%02d"
200                                   (if (< yr 70)
201                                       20
202                                     19)
203                                   yr))))
204          (let ((string (substring date
205                                   (match-beginning month)
206                                   (+ (match-beginning month) 3))))
207            (setq month
208                  (int-to-string
209                   (cdr (assoc (upcase string) timezone-months-assoc)))))
210
211          (setq day
212                (substring date (match-beginning day) (match-end day)))
213          (setq time
214                (substring date (match-beginning time) (match-end time)))))
215    (if zone
216        (setq zone
217              (substring date (match-beginning zone) (match-end zone))))
218    ;; Return a vector.
219    (if year
220        (vector year month day time zone)
221      (vector "0" "0" "0" "0" nil))
222    )
223  )
224    (t (signal 'error (list "Invalid date string" date)))))
225
226(defun timezone-parse-time (time)
227  "Parse TIME (HH:MM:SS) and return a vector [hour minute second].
228Recognize HH:MM:SS, HH:MM, HHMMSS, HHMM."
229  (let ((time (or time ""))
230        (hour nil)
231        (minute nil)
232        (second nil))
233    (cond ((string-match "\\`\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)\\'" time)
234           ;; HH:MM:SS
235           (setq hour 1 minute 2 second 3))
236          ((string-match "\\`\\([0-9]+\\):\\([0-9]+\\)\\'" time)
237           ;; HH:MM
238           (setq hour 1 minute 2 second nil))
239          ((string-match "\\`\\([0-9][0-9]\\)\\([0-9][0-9]\\)\\([0-9][0-9]\\)\\'" time)
240           ;; HHMMSS
241           (setq hour 1 minute 2 second 3))
242          ((string-match "\\`\\([0-9][0-9]\\)\\([0-9][0-9]\\)\\'" time)
243           ;; HHMM
244           (setq hour 1 minute 2 second nil))
245          )
246    ;; Return [hour minute second]
247    (vector
248     (if hour
249         (substring time (match-beginning hour) (match-end hour)) "0")
250     (if minute
251         (substring time (match-beginning minute) (match-end minute)) "0")
252     (if second
253         (substring time (match-beginning second) (match-end second)) "0"))
254    ))
255
256
257;; Miscellaneous
258
259(defun timezone-zone-to-minute (timezone)
260  "Translate TIMEZONE to an integer minute offset from GMT.
261TIMEZONE can be a cons cell containing the output of `current-time-zone',
262or an integer of the form +-HHMM, or a time zone name."
263  (cond
264     ((consp timezone)
265      (/ (car timezone) 60))
266     (timezone
267      (progn
268        (setq timezone
269              (or (and (stringp timezone) (cdr (assoc (upcase timezone) timezone-world-timezones)))
270                  ;; +900
271                  timezone))
272        (if (stringp timezone)
273            (setq timezone (string-to-int timezone)))
274        ;; Taking account of minute in timezone.
275        ;; HHMM -> MM
276        (let* ((abszone (abs timezone))
277               (minutes (+ (* 60 (/ abszone 100)) (% abszone 100))))
278          (if (< timezone 0) (- minutes) minutes))))
279     (t 0)))
280
281(defun timezone-time-from-absolute (date seconds)
282  "Compute the UTC time equivalent to DATE at time SECONDS after midnight.
283Return a list suitable as an argument to `current-time-zone',
284or nil if the date cannot be thus represented.
285DATE is the number of days elapsed since the (imaginary)
286Gregorian date Sunday, December 31, 1 BC."
287  (let* ((current-time-origin 719163)
288            ;; (timezone-absolute-from-gregorian 1 1 1970)
289         (days (- date current-time-origin))
290         (days-1 (/ days 65536))
291         (days-2 (% (/ days 256) 256))
292         (days-3 (% days 256))
293         ;; (seconds-per-day (float 86400))
294         (seconds-per-day-1 1)
295         (seconds-per-day-2 81)
296         (seconds-per-day-3 128)
297         ;; (seconds (+ seconds (* days seconds-per-day)))
298         ;; (current-time-arithmetic-base (float 65536))
299         ;; (hi (floor (/ seconds current-time-arithmetic-base)))
300         ;; (hibase (* hi current-time-arithmetic-base))
301         ;; (lo (floor (- seconds hibase)))
302         (seconds-1 (/ seconds 65536))
303         (seconds-2 (% (/ seconds 256) 256))
304         (seconds-3 (% seconds 256))
305         hi lo
306         r
307         seconds-per-day*days-1
308         seconds-per-day*days-2
309         seconds-per-day*days-3)
310    (setq r (* days-3 seconds-per-day-3)
311          seconds-per-day*days-3 (% r 256))
312    (setq r (+ (/ r 256)
313               (* days-2 seconds-per-day-3)
314               (* days-3 seconds-per-day-2))
315          seconds-per-day*days-2 (% r 256))
316    (setq seconds-per-day*days-1 (+ (/ r 256)
317                                    (* days-1 seconds-per-day-3)
318                                    (* (/ days 256) seconds-per-day-2)
319                                    (* days seconds-per-day-1)))
320    (setq r (+ seconds-2 seconds-per-day*days-2)
321          seconds-2 (% r 256)
322          seconds-1 (+ seconds-1 (/ r 256)))
323    (setq lo (+ (* seconds-2 256)
324                seconds-3 seconds-per-day*days-3))
325    (setq hi (+ seconds-1 seconds-per-day*days-1))
326    ;; (and (< (abs (- seconds (+ hibase lo))) 2) ; Check for integer overflow.
327    ;;      (cons hi lo))
328    (cons hi lo)
329    ))
330
331(defun timezone-time-zone-from-absolute (date seconds)
332  "Compute the local time zone for DATE at time SECONDS after midnight.
333Return a list in the same format as current-time-zone's result,
334or nil if the local time zone could not be computed.
335DATE is the number of days elapsed since the (imaginary)
336Gregorian date Sunday, December 31, 1 BC."
337   (and (fboundp 'current-time-zone)
338        (let ((utc-time (timezone-time-from-absolute date seconds)))
339          (and utc-time
340               (let ((zone (current-time-zone utc-time)))
341                 (and (car zone) zone))))))
342
343(defsubst timezone-fix-time-1 (year month day hour minute second)
344  "Fix date and time.
345For old `timezone-fix-time' function.
346Arguments are YEAR, MONTH, DAY, HOUR, MINUTE and SECOND."
347  ;; MINUTE may be larger than 60 or smaller than -60.
348  (let ((hour-fix
349         (if (< minute 0)
350             ;;(/ (- minute 59) 60) (/ minute 60)
351             ;; ANSI C compliance about truncation of integer division
352             ;; by eggert@twinsun.com (Paul Eggert)
353             (- (/ (- 59 minute) 60)) (/ minute 60))))
354    (setq hour (+ hour hour-fix))
355    (setq minute (- minute (* 60 hour-fix))))
356  ;; HOUR may be larger than 24 or smaller than 0.
357  (cond ((<= 24 hour)                   ;24 -> 00
358         (setq hour (- hour 24))
359         (setq day  (1+ day))
360         (if (< (timezone-last-day-of-month month year) day)
361             (progn
362               (setq month (1+ month))
363               (setq day 1)
364               (if (< 12 month)
365                   (progn
366                     (setq month 1)
367                     (setq year (1+ year))
368                     ))
369               )))
370        ((> 0 hour)
371         (setq hour (+ hour 24))
372         (setq day  (1- day))
373         (if (> 1 day)
374             (progn
375               (setq month (1- month))
376               (if (> 1 month)
377                   (progn
378                     (setq month 12)
379                     (setq year (1- year))
380                     ))
381               (setq day (timezone-last-day-of-month month year))
382               )))
383        )
384  (vector year month day hour minute second))
385
386(defsubst timezone-fix-time-2 (date local timezone)
387  "Convert DATE (default timezone LOCAL) to YYYY-MM-DD-HH-MM-SS-ZONE vector.
388If LOCAL is nil, it is assumed to be GMT.
389If TIMEZONE is nil, use the local time zone."
390  (let* ((date   (timezone-parse-date date))
391         (year   (string-to-int (aref date 0)))
392         (year   (cond ((< year 50)
393                        (+ year 2000))
394                       ((< year 100)
395                        (+ year 1900))
396                       (t year)))
397         (month  (string-to-int (aref date 1)))
398         (day    (string-to-int (aref date 2)))
399         (time   (timezone-parse-time (aref date 3)))
400         (hour   (string-to-int (aref time 0)))
401         (minute (string-to-int (aref time 1)))
402         (second (string-to-int (aref time 2)))
403         (local  (or (aref date 4) local)) ;Use original if defined
404         (timezone
405          (or timezone
406              (timezone-time-zone-from-absolute
407               (timezone-absolute-from-gregorian month day year)
408               (+ second (* 60 (+ minute (* 60 hour)))))))
409         (diff   (- (timezone-zone-to-minute timezone)
410                    (timezone-zone-to-minute local)))
411         (minute (+ minute diff))
412         (hour-fix (floor minute 60)))
413    (setq hour (+ hour hour-fix))
414    (setq minute (- minute (* 60 hour-fix)))
415    ;; HOUR may be larger than 24 or smaller than 0.
416    (cond ((<= 24 hour)                 ;24 -> 00
417           (setq hour (- hour 24))
418           (setq day  (1+ day))
419           (if (< (timezone-last-day-of-month month year) day)
420               (progn
421                 (setq month (1+ month))
422                 (setq day 1)
423                 (if (< 12 month)
424                     (progn
425                       (setq month 1)
426                       (setq year (1+ year))
427                       ))
428                 )))
429          ((> 0 hour)
430           (setq hour (+ hour 24))
431           (setq day  (1- day))
432           (if (> 1 day)
433               (progn
434                 (setq month (1- month))
435                 (if (> 1 month)
436                     (progn
437                       (setq month 12)
438                       (setq year (1- year))
439                       ))
440                 (setq day (timezone-last-day-of-month month year))
441                 )))
442          )
443    (vector year month day hour minute second timezone)))
444
445(defun timezone-fix-time (a1 a2 a3 &optional a4 a5 a6)
446  "Fix date and time.
447(Old API: A1=YEAR A2=MONTH A3=DAY A4=HOUR A5=MINUTE A6=SECOND).
448Convert DATE (default timezone LOCAL) to YYYY-MM-DD-HH-MM-SS-ZONE vector.
449If LOCAL is nil, it is assumed to be GMT.
450If TIMEZONE is nil, use the local time zone.
451(New API: A1=DATE A2=LOCAL A3=TIMEZONE)"
452  (if a4
453      (timezone-fix-time-1 a1 a2 a3 a4 a5 a6)
454    (timezone-fix-time-2 a1 a2 a3)))
455
456;; Partly copied from Calendar program by Edward M. Reingold.
457;; Thanks a lot.
458
459(defun timezone-last-day-of-month (month year)
460  "The last day in MONTH during YEAR."
461  (if (and (= month 2) (timezone-leap-year-p year))
462      29
463    (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month))))
464
465(defun timezone-leap-year-p (year)
466  "Return t if YEAR is a Gregorian leap year."
467  (or (and (zerop  (% year 4))
468           (not (zerop (% year 100))))
469      (zerop (% year 400))))
470
471(defun timezone-day-number (month day year)
472  "Return the day number within the year of the date MONTH/DAY/YEAR."
473  (let ((day-of-year (+ day (* 31 (1- month)))))
474    (if (> month 2)
475        (progn
476          (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10)))
477          (if (timezone-leap-year-p year)
478              (setq day-of-year (1+ day-of-year)))))
479    day-of-year))
480
481(defun timezone-absolute-from-gregorian (month day year)
482  "The number of days between the Gregorian date 12/31/1 BC and MONTH/DAY/YEAR.
483The Gregorian date Sunday, December 31, 1 BC is imaginary."
484  (+ (timezone-day-number month day year);; Days this year
485     (* 365 (1- year));;        + Days in prior years
486     (/ (1- year) 4);;          + Julian leap years
487     (- (/ (1- year) 100));;    - century years
488     (/ (1- year) 400)));;      + Gregorian leap years
489
490;;; @ End.
491;;;
492
493(require 'product)
494(product-provide (provide 'timezone) (require 'apel-ver))
495
496;;; timezone.el ends here
Note: See TracBrowser for help on using the repository browser.