1;;; timezone.el --- time zone package for GNU Emacs 2 3;; Copyright (C) 1990, 1991, 1992, 1993, 1996, 1999, 2001, 2002, 2003, 4;; 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 5 6;; Author: Masanobu Umeda 7;; Maintainer: umerin@mse.kyutech.ac.jp 8;; Keywords: news 9 10;; This file is part of GNU Emacs. 11 12;; GNU Emacs is free software; you can redistribute it and/or modify 13;; it under the terms of the GNU General Public License as published by 14;; the Free Software Foundation; either version 2, or (at your option) 15;; any later version. 16 17;; GNU Emacs is distributed in the hope that it will be useful, 18;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20;; GNU General Public License for more details. 21 22;; You should have received a copy of the GNU General Public License 23;; along with GNU Emacs; see the file COPYING. If not, write to the 24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25;; Boston, MA 02110-1301, USA. 26 27;;; Commentary: 28 29;;; Code: 30 31(defvar timezone-world-timezones 32 '(("PST" . -800) 33 ("PDT" . -700) 34 ("MST" . -700) 35 ("MDT" . -600) 36 ("CST" . -600) 37 ("CDT" . -500) 38 ("EST" . -500) 39 ("EDT" . -400) 40 ("AST" . -400) ;by <clamen@CS.CMU.EDU> 41 ("NST" . -330) ;by <clamen@CS.CMU.EDU> 42 ("UT" . +000) 43 ("GMT" . +000) 44 ("BST" . +100) 45 ("MET" . +100) 46 ("EET" . +200) 47 ("JST" . +900) 48 ("GMT+1" . +100) ("GMT+2" . +200) ("GMT+3" . +300) 49 ("GMT+4" . +400) ("GMT+5" . +500) ("GMT+6" . +600) 50 ("GMT+7" . +700) ("GMT+8" . +800) ("GMT+9" . +900) 51 ("GMT+10" . +1000) ("GMT+11" . +1100) ("GMT+12" . +1200) ("GMT+13" . +1300) 52 ("GMT-1" . -100) ("GMT-2" . -200) ("GMT-3" . -300) 53 ("GMT-4" . -400) ("GMT-5" . -500) ("GMT-6" . -600) 54 ("GMT-7" . -700) ("GMT-8" . -800) ("GMT-9" . -900) 55 ("GMT-10" . -1000) ("GMT-11" . -1100) ("GMT-12" . -1200)) 56 "*Time differentials of timezone from GMT in +-HHMM form. 57This list is obsolescent, and is present only for backwards compatibility, 58because time zone names are ambiguous in practice. 59Use `current-time-zone' instead.") 60 61(defvar timezone-months-assoc 62 '(("JAN" . 1)("FEB" . 2)("MAR" . 3) 63 ("APR" . 4)("MAY" . 5)("JUN" . 6) 64 ("JUL" . 7)("AUG" . 8)("SEP" . 9) 65 ("OCT" . 10)("NOV" . 11)("DEC" . 12)) 66 "Alist of first three letters of a month and its numerical representation.") 67 68(defun timezone-make-date-arpa-standard (date &optional local timezone) 69 "Convert DATE to an arpanet standard date. 70Optional 2nd argument LOCAL specifies the default local timezone of the DATE; 71if nil, GMT is assumed. 72Optional 3rd argument TIMEZONE specifies a time zone to be represented in; 73if nil, the local time zone is assumed." 74 (let ((new (timezone-fix-time date local timezone))) 75 (timezone-make-arpa-date (aref new 0) (aref new 1) (aref new 2) 76 (timezone-make-time-string 77 (aref new 3) (aref new 4) (aref new 5)) 78 (aref new 6)) 79 )) 80 81(defun timezone-make-date-sortable (date &optional local timezone) 82 "Convert DATE to a sortable date string. 83Optional 2nd argument LOCAL specifies the default local timezone of the DATE; 84if nil, GMT is assumed. 85Optional 3rd argument TIMEZONE specifies a timezone to be represented in; 86if nil, the local time zone is assumed." 87 (let ((new (timezone-fix-time date local timezone))) 88 (timezone-make-sortable-date (aref new 0) (aref new 1) (aref new 2) 89 (timezone-make-time-string 90 (aref new 3) (aref new 4) (aref new 5))) 91 )) 92 93 94;; 95;; Parsers and Constructors of Date and Time 96;; 97 98(defun timezone-make-arpa-date (year month day time &optional timezone) 99 "Make arpanet standard date string from YEAR, MONTH, DAY, and TIME. 100Optional argument TIMEZONE specifies a time zone." 101 (let ((zone 102 (if (listp timezone) 103 (let* ((m (timezone-zone-to-minute timezone)) 104 (absm (if (< m 0) (- m) m))) 105 (format "%c%02d%02d" 106 (if (< m 0) ?- ?+) (/ absm 60) (% absm 60))) 107 timezone))) 108 (format "%02d %s %04d %s %s" 109 day 110 (capitalize (car (rassq month timezone-months-assoc))) 111 year 112 time 113 zone))) 114 115(defun timezone-make-sortable-date (year month day time) 116 "Make sortable date string from YEAR, MONTH, DAY, and TIME." 117 (format "%4d%02d%02d%s" 118 year month day time)) 119 120(defun timezone-make-time-string (hour minute second) 121 "Make time string from HOUR, MINUTE, and SECOND." 122 (format "%02d:%02d:%02d" hour minute second)) 123 124(defun timezone-parse-date (date) 125 "Parse DATE and return a vector [YEAR MONTH DAY TIME TIMEZONE]. 126Two-digit dates are `windowed'. Those <69 have 2000 added; otherwise 1900 127is added. Three-digit dates have 1900 added. 128TIMEZONE is nil for DATEs without a zone field. 129 130Understands the following styles: 131 (1) 14 Apr 89 03:20[:12] [GMT] 132 (2) Fri, 17 Mar 89 4:01[:33] [GMT] 133 (3) Mon Jan 16 16:12[:37] [GMT] 1989 134 (4) 6 May 1992 1641-JST (Wednesday) 135 (5) 22-AUG-1993 10:59:12.82 136 (6) Thu, 11 Apr 16:17:12 91 [MET] 137 (7) Mon, 6 Jul 16:47:20 T 1992 [MET] 138 (8) 1996-06-24 21:13:12 [GMT] 139 (9) 1996-06-24 21:13-ZONE" 140 ;; Get rid of any text properties. 141 (and (stringp date) 142 (or (text-properties-at 0 date) 143 (next-property-change 0 date)) 144 (setq date (copy-sequence date)) 145 (set-text-properties 0 (length date) nil date)) 146 (let ((date (or date "")) 147 (year nil) 148 (month nil) 149 (day nil) 150 (time nil) 151 (zone nil)) ;This may be nil. 152 (cond ((string-match 153 "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)" date) 154 ;; Styles: (1) and (2) with timezone and buggy timezone 155 ;; This is most common in mail and news, 156 ;; so it is worth trying first. 157 (setq year 3 month 2 day 1 time 4 zone 5)) 158 ((string-match 159 "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]*\\'" date) 160 ;; Styles: (1) and (2) without timezone 161 (setq year 3 month 2 day 1 time 4 zone nil)) 162 ((string-match 163 "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\'" date) 164 ;; Styles: (6) and (7) without timezone 165 (setq year 6 month 3 day 2 time 4 zone nil)) 166 ((string-match 167 "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date) 168 ;; Styles: (6) and (7) with timezone and buggy timezone 169 (setq year 6 month 3 day 2 time 4 zone 7)) 170 ((string-match 171 "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([0-9]+\\)" date) 172 ;; Styles: (3) without timezone 173 (setq year 4 month 1 day 2 time 3 zone nil)) 174 ((string-match 175 "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)[ \t]+\\([0-9]+\\)" date) 176 ;; Styles: (3) with timezone 177 (setq year 5 month 1 day 2 time 3 zone 4)) 178 ((string-match 179 "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date) 180 ;; Styles: (4) with timezone 181 (setq year 3 month 2 day 1 time 4 zone 5)) 182 ((string-match 183 "\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)\\(\\.[0-9]+\\)?[ \t]+\\([-+a-zA-Z0-9]+\\)" date) 184 ;; Styles: (5) with timezone. 185 (setq year 3 month 2 day 1 time 4 zone 6)) 186 ((string-match 187 "\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)\\(\\.[0-9]+\\)?" date) 188 ;; Styles: (5) without timezone. 189 (setq year 3 month 2 day 1 time 4 zone nil)) 190 ((string-match 191 "\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)" date) 192 ;; Styles: (8) with timezone. 193 (setq year 1 month 2 day 3 time 4 zone 5)) 194 ((string-match 195 "\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)[T \t]+\\([0-9]+:[0-9]+\\)[ \t]+\\([-+a-zA-Z0-9:]+\\)" date) 196 ;; Styles: (8) with timezone with a colon in it. 197 (setq year 1 month 2 day 3 time 4 zone 5)) 198 ((string-match 199 "\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)[T \t]+\\([0-9]+:[0-9]+:[0-9]+\\)" date) 200 ;; Styles: (8) without timezone. 201 (setq year 1 month 2 day 3 time 4 zone nil)) 202 ) 203 (when year 204 (setq year (match-string year date)) 205 ;; Guess ambiguous years. Assume years < 69 don't predate the 206 ;; Unix Epoch, so are 2000+. Three-digit years are assumed to 207 ;; be relative to 1900. 208 (if (< (length year) 4) 209 (let ((y (string-to-number year))) 210 (if (< y 69) 211 (setq y (+ y 100))) 212 (setq year (int-to-string (+ 1900 y))))) 213 (setq month 214 (if (= (aref date (+ (match-beginning month) 2)) ?-) 215 ;; Handle numeric months, spanning exactly two digits. 216 (substring date 217 (match-beginning month) 218 (+ (match-beginning month) 2)) 219 (let* ((string (substring date 220 (match-beginning month) 221 (+ (match-beginning month) 3))) 222 (monthnum 223 (cdr (assoc (upcase string) timezone-months-assoc)))) 224 (if monthnum 225 (int-to-string monthnum))))) 226 (setq day (match-string day date)) 227 (setq time (match-string time date))) 228 (if zone (setq zone (match-string zone date))) 229 ;; Return a vector. 230 (if (and year month) 231 (vector year month day time zone) 232 (vector "0" "0" "0" "0" nil)))) 233 234(defun timezone-parse-time (time) 235 "Parse TIME (HH:MM:SS) and return a vector [hour minute second]. 236Recognize HH:MM:SS, HH:MM, HHMMSS, HHMM." 237 (let ((time (or time "")) 238 hour minute second) 239 (cond ((string-match "\\`\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)\\'" time) 240 ;; HH:MM:SS 241 (setq hour 1 minute 2 second 3)) 242 ((string-match "\\`\\([0-9]+\\):\\([0-9]+\\)\\'" time) 243 ;; HH:MM 244 (setq hour 1 minute 2 second nil)) 245 ((string-match "\\`\\([0-9][0-9]\\)\\([0-9][0-9]\\)\\([0-9][0-9]\\)\\'" time) 246 ;; HHMMSS 247 (setq hour 1 minute 2 second 3)) 248 ((string-match "\\`\\([0-9][0-9]\\)\\([0-9][0-9]\\)\\'" time) 249 ;; HHMM 250 (setq hour 1 minute 2 second nil)) 251 ) 252 ;; Return [hour minute second] 253 (vector 254 (if hour (match-string hour time) "0") 255 (if minute (match-string minute time) "0") 256 (if second (match-string second time) "0")))) 257 258 259;; Miscellaneous 260 261(defun timezone-zone-to-minute (timezone) 262 "Translate TIMEZONE to an integer minute offset from GMT. 263TIMEZONE can be a cons cell containing the output of current-time-zone, 264or an integer of the form +-HHMM, or a time zone name." 265 (cond 266 ((consp timezone) 267 (/ (car timezone) 60)) 268 (timezone 269 (progn 270 (setq timezone 271 (or (cdr (assoc (upcase timezone) timezone-world-timezones)) 272 ;; +900 273 timezone)) 274 (if (stringp timezone) 275 (setq timezone (string-to-number timezone))) 276 ;; Taking account of minute in timezone. 277 ;; HHMM -> MM 278 (let* ((abszone (abs timezone)) 279 (minutes (+ (* 60 (/ abszone 100)) (% abszone 100)))) 280 (if (< timezone 0) (- minutes) minutes)))) 281 (t 0))) 282 283(defun timezone-time-from-absolute (date seconds) 284 "Compute the UTC time equivalent to DATE at time SECONDS after midnight. 285Return a list suitable as an argument to current-time-zone, 286or nil if the date cannot be thus represented. 287DATE is the number of days elapsed since the (imaginary) 288Gregorian date Sunday, December 31, 1 BC." 289 (let* ((current-time-origin 719163) 290 ;; (timezone-absolute-from-gregorian 1 1 1970) 291 (days (- date current-time-origin)) 292 (seconds-per-day (float 86400)) 293 (seconds (+ seconds (* days seconds-per-day))) 294 (current-time-arithmetic-base (float 65536)) 295 (hi (floor (/ seconds current-time-arithmetic-base))) 296 (hibase (* hi current-time-arithmetic-base)) 297 (lo (floor (- seconds hibase)))) 298 (and (< (abs (- seconds (+ hibase lo))) 2) ;; Check for integer overflow. 299 (cons hi lo)))) 300 301(defun timezone-time-zone-from-absolute (date seconds) 302 "Compute the local time zone for DATE at time SECONDS after midnight. 303Return a list in the same format as current-time-zone's result, 304or nil if the local time zone could not be computed. 305DATE is the number of days elapsed since the (imaginary) 306Gregorian date Sunday, December 31, 1 BC." 307 (and (fboundp 'current-time-zone) 308 (let ((utc-time (timezone-time-from-absolute date seconds))) 309 (and utc-time 310 (let ((zone (current-time-zone utc-time))) 311 (and (car zone) zone)))))) 312 313(defun timezone-fix-time (date local timezone) 314 "Convert DATE (default timezone LOCAL) to YYYY-MM-DD-HH-MM-SS-ZONE vector. 315If LOCAL is nil, it is assumed to be GMT. 316If TIMEZONE is nil, use the local time zone." 317 (let* ((date (timezone-parse-date date)) 318 (year (string-to-number (aref date 0))) 319 (year (cond ((< year 69) 320 (+ year 2000)) 321 ((< year 100) 322 (+ year 1900)) 323 ((< year 1000) ; possible 3-digit years. 324 (+ year 1900)) 325 (t year))) 326 (month (string-to-number (aref date 1))) 327 (day (string-to-number (aref date 2))) 328 (time (timezone-parse-time (aref date 3))) 329 (hour (string-to-number (aref time 0))) 330 (minute (string-to-number (aref time 1))) 331 (second (string-to-number (aref time 2))) 332 (local (or (aref date 4) local)) ;Use original if defined 333 (timezone 334 (or timezone 335 (timezone-time-zone-from-absolute 336 (timezone-absolute-from-gregorian month day year) 337 (+ second (* 60 (+ minute (* 60 hour))))))) 338 (diff (- (timezone-zone-to-minute timezone) 339 (timezone-zone-to-minute local))) 340 (minute (+ minute diff)) 341 (hour-fix (floor minute 60))) 342 (setq hour (+ hour hour-fix)) 343 (setq minute (- minute (* 60 hour-fix))) 344 ;; HOUR may be larger than 24 or smaller than 0. 345 (cond ((<= 24 hour) ;24 -> 00 346 (setq hour (- hour 24)) 347 (setq day (1+ day)) 348 (when (< (timezone-last-day-of-month month year) day) 349 (setq month (1+ month)) 350 (setq day 1) 351 (when (< 12 month) 352 (setq month 1) 353 (setq year (1+ year))))) 354 ((> 0 hour) 355 (setq hour (+ hour 24)) 356 (setq day (1- day)) 357 (when (> 1 day) 358 (setq month (1- month)) 359 (when (> 1 month) 360 (setq month 12) 361 (setq year (1- year))) 362 (setq day (timezone-last-day-of-month month year))))) 363 (vector year month day hour minute second timezone))) 364 365;; Partly copied from Calendar program by Edward M. Reingold. 366;; Thanks a lot. 367 368(defun timezone-last-day-of-month (month year) 369 "The last day in MONTH during YEAR." 370 (if (and (= month 2) (timezone-leap-year-p year)) 371 29 372 (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month)))) 373 374(defun timezone-leap-year-p (year) 375 "Returns t if YEAR is a Gregorian leap year." 376 (or (and (zerop (% year 4)) 377 (not (zerop (% year 100)))) 378 (zerop (% year 400)))) 379 380(defun timezone-day-number (month day year) 381 "Return the day number within the year of the date month/day/year." 382 (let ((day-of-year (+ day (* 31 (1- month))))) 383 (if (> month 2) 384 (progn 385 (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10))) 386 (if (timezone-leap-year-p year) 387 (setq day-of-year (1+ day-of-year))))) 388 day-of-year)) 389 390(defun timezone-absolute-from-gregorian (month day year) 391 "The number of days between the Gregorian date 12/31/1 BC and month/day/year. 392The Gregorian date Sunday, December 31, 1 BC is imaginary." 393 (+ (timezone-day-number month day year);; Days this year 394 (* 365 (1- year));; + Days in prior years 395 (/ (1- year) 4);; + Julian leap years 396 (- (/ (1- year) 100));; - century years 397 (/ (1- year) 400)));; + Gregorian leap years 398 399(provide 'timezone) 400 401;;; arch-tag: e23d5bc6-f32d-48ba-8996-323e9d654b3f 402;;; timezone.el ends here 403