| 1 | /* Time support for VMS. |
| 2 | Copyright (C) 1993 Free Software Foundation. |
| 3 | |
| 4 | This file is part of GNU Emacs. |
| 5 | |
| 6 | GNU Emacs is free software; you can redistribute it and/or modify |
| 7 | it under the terms of the GNU General Public License as published by |
| 8 | the Free Software Foundation; either version 2, or (at your option) |
| 9 | any later version. |
| 10 | |
| 11 | GNU Emacs is distributed in the hope that it will be useful, |
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | GNU General Public License for more details. |
| 15 | |
| 16 | You should have received a copy of the GNU General Public License |
| 17 | along with GNU Emacs; see the file COPYING. If not, write to |
| 18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ |
| 19 | |
| 20 | #include <config.h> |
| 21 | #include "vmstime.h" |
| 22 | |
| 23 | long timezone=0; |
| 24 | int daylight=0; |
| 25 | |
| 26 | static char tzname_default[20]=""; |
| 27 | static char tzname_dst[20]=""; |
| 28 | |
| 29 | char *tzname[2] = { tzname_default, tzname_dst }; |
| 30 | |
| 31 | static long internal_daylight=0; |
| 32 | static char daylight_set=0; |
| 33 | |
| 34 | static long read_time(const char *nptr, const char **endptr, |
| 35 | int sign_allowed_p) |
| 36 | { |
| 37 | int t; |
| 38 | |
| 39 | *endptr = nptr; |
| 40 | |
| 41 | /* This routine trusts the user very much, and does no checks! |
| 42 | The only exception is this: */ |
| 43 | if (!sign_allowed_p && (*nptr == '-' || *nptr == '+')) |
| 44 | return 0; |
| 45 | |
| 46 | t = strtol(*endptr, endptr, 10) * 3600; |
| 47 | if (**endptr != ':' || **endptr == '+' || **endptr == '-') |
| 48 | return t; |
| 49 | (*endptr)++; |
| 50 | |
| 51 | t = t + strtol(*endptr, endptr, 10) * 60; |
| 52 | if (**endptr != ':' || **endptr == '+' || **endptr == '-') |
| 53 | return t; |
| 54 | (*endptr)++; |
| 55 | |
| 56 | return t + strtol(*endptr, endptr, 10); |
| 57 | } |
| 58 | |
| 59 | static void read_dst_time(const char *nptr, const char **endptr, |
| 60 | int *m, int *n, int *d, |
| 61 | int *leap_p) |
| 62 | { |
| 63 | time_t bintim = time(0); |
| 64 | struct tm *lc = localtime(&bintim); |
| 65 | |
| 66 | *leap_p = 1; |
| 67 | *m = 0; /* When m and n are 0, a Julian */ |
| 68 | *n = 0; /* date has been inserted in d */ |
| 69 | |
| 70 | switch(*nptr) |
| 71 | { |
| 72 | case 'M': |
| 73 | { |
| 74 | /* This routine counts on the user to have specified "Mm.n.d", |
| 75 | where 1 <= n <= 5, 1 <= m <= 12, 0 <= d <= 6 */ |
| 76 | |
| 77 | *m = strtol(++nptr, endptr, 10); |
| 78 | (*endptr)++; /* Skip the dot */ |
| 79 | *n = strtol(*endptr, endptr, 10); |
| 80 | (*endptr)++; /* Skip the dot */ |
| 81 | *d = strtol(*endptr, endptr, 10); |
| 82 | |
| 83 | return; |
| 84 | } |
| 85 | case 'J': |
| 86 | *leap_p = 0; /* Never count with leap years */ |
| 87 | default: /* trust the user to have inserted a number! */ |
| 88 | *d = strtol(++nptr, endptr, 10); |
| 89 | return; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | struct vms_vectim |
| 94 | { |
| 95 | short year, month, day, hour, minute, second, centi_second; |
| 96 | }; |
| 97 | static void find_dst_time(int m, int n, long d, |
| 98 | int hour, int minute, int second, |
| 99 | int leap_p, |
| 100 | long vms_internal_time[2]) |
| 101 | { |
| 102 | long status = SYS$GETTIM(vms_internal_time); |
| 103 | struct vms_vectim vms_vectime; |
| 104 | status = SYS$NUMTIM(&vms_vectime, vms_internal_time); |
| 105 | |
| 106 | if (m == 0 && n == 0) |
| 107 | { |
| 108 | long tmp_vms_internal_time[2][2]; |
| 109 | long day_of_year; |
| 110 | long tmp_operation = LIB$K_DAY_OF_YEAR; |
| 111 | |
| 112 | status = LIB$CVT_FROM_INTERNAL_TIME(&tmp_operation, &day_of_year, |
| 113 | vms_internal_time); |
| 114 | |
| 115 | vms_vectime.month = 2; |
| 116 | vms_vectime.day = 29; |
| 117 | status = LIB$CVT_VECTIM(&vms_vectime, tmp_vms_internal_time[0]); |
| 118 | if (status & 1) /* This is a leap year */ |
| 119 | { |
| 120 | if (!leap_p && d > 59) |
| 121 | d ++; /* If we don't count with 29th Feb, |
| 122 | and this is a leap year, count up, |
| 123 | to make day 60 really become the |
| 124 | 1st March. */ |
| 125 | } |
| 126 | /* 1st January, at midnight */ |
| 127 | vms_vectime.month = 1; |
| 128 | vms_vectime.day = 1; |
| 129 | vms_vectime.hour = hour; |
| 130 | vms_vectime.minute = minute; |
| 131 | vms_vectime.second = second; |
| 132 | vms_vectime.centi_second = 0; |
| 133 | status = LIB$CVT_VECTIM(&vms_vectime, tmp_vms_internal_time[0]); |
| 134 | tmp_operation = LIB$K_DELTA_DAYS; |
| 135 | status = LIB$CVT_TO_INTERNAL_TIME(&tmp_operation, &d, |
| 136 | tmp_vms_internal_time[1]); |
| 137 | /* now, tmp_vms_interval_time[0] contains 1st Jan, 00:00:00, |
| 138 | and tmp_vms_interval_time[1] contains delta time +d days. |
| 139 | Let's just add them together */ |
| 140 | status = LIB$ADD_TIMES(tmp_vms_internal_time[0], |
| 141 | tmp_vms_internal_time[1], |
| 142 | vms_internal_time); |
| 143 | } |
| 144 | else |
| 145 | { |
| 146 | long tmp_vms_internal_time[2]; |
| 147 | long day_of_week; |
| 148 | long tmp_operation = LIB$K_DAY_OF_YEAR; |
| 149 | |
| 150 | if (d == 0) /* 0 is Sunday, which isn't compatible with VMS, |
| 151 | where day_of_week is 1 -- 7, and 1 is Monday */ |
| 152 | { |
| 153 | d = 7; /* So a simple conversion is required */ |
| 154 | } |
| 155 | vms_vectime.month = m; |
| 156 | vms_vectime.day = 1; |
| 157 | vms_vectime.hour = hour; |
| 158 | vms_vectime.minute = minute; |
| 159 | vms_vectime.second = second; |
| 160 | vms_vectime.centi_second = 0; |
| 161 | status = LIB$CVT_VECTIM(&vms_vectime, tmp_vms_internal_time); |
| 162 | tmp_operation = LIB$K_DAY_OF_WEEK; |
| 163 | status = LIB$CVT_FROM_INTERNAL_TIME(&tmp_operation, &day_of_week, |
| 164 | tmp_vms_internal_time); |
| 165 | d -= day_of_week; |
| 166 | if (d < 0) |
| 167 | { |
| 168 | d += 7; |
| 169 | } |
| 170 | vms_vectime.day += (n-1)*7 + d; |
| 171 | status = LIB$CVT_VECTIM(&vms_vectime, vms_internal_time); |
| 172 | if (!(status & 1)) |
| 173 | { |
| 174 | vms_vectime.day -= 7; /* n was probably 5 */ |
| 175 | status = LIB$CVT_VECTIM(&vms_vectime, vms_internal_time); |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | static cmp_vms_internal_times(long vms_internal_time1[2], |
| 181 | long vms_internal_time2[2]) |
| 182 | { |
| 183 | if (vms_internal_time1[1] < vms_internal_time2[1]) |
| 184 | return -1; |
| 185 | else |
| 186 | if (vms_internal_time1[1] > vms_internal_time2[1]) |
| 187 | return 1; |
| 188 | |
| 189 | if (vms_internal_time1[0] < vms_internal_time2[0]) |
| 190 | return -1; |
| 191 | else |
| 192 | if (vms_internal_time1[0] > vms_internal_time2[0]) |
| 193 | return 1; |
| 194 | |
| 195 | return 0; |
| 196 | } |
| 197 | |
| 198 | /* -------------------------- Global routines ------------------------------ */ |
| 199 | |
| 200 | #ifdef tzset |
| 201 | #undef tzset |
| 202 | #endif |
| 203 | void sys_tzset() |
| 204 | { |
| 205 | char *TZ; |
| 206 | char *p, *q; |
| 207 | |
| 208 | if (daylight_set) |
| 209 | return; |
| 210 | |
| 211 | daylight = 0; |
| 212 | |
| 213 | if ((TZ = getenv("TZ")) == 0) |
| 214 | return; |
| 215 | |
| 216 | p = TZ; |
| 217 | q = tzname[0]; |
| 218 | |
| 219 | while(*p != '\0' |
| 220 | && (*p <'0' || *p > '9') && *p != '-' && *p != '+' && *p != ',') |
| 221 | *q++ = *p++; |
| 222 | *q = '\0'; |
| 223 | |
| 224 | /* This is special for VMS, so I don't care if it doesn't exist anywhere |
| 225 | else */ |
| 226 | |
| 227 | timezone = read_time(p, &p, 1); |
| 228 | |
| 229 | q = tzname[1]; |
| 230 | |
| 231 | while(*p != '\0' |
| 232 | && (*p <'0' || *p > '9') && *p != '-' && *p != '+' && *p != ',') |
| 233 | *q++ = *p++; |
| 234 | *q = '\0'; |
| 235 | |
| 236 | if (*p != '-' && *p != '+' && !(*p >='0' && *p <= '9')) |
| 237 | internal_daylight = timezone - 3600; |
| 238 | else |
| 239 | internal_daylight = read_time(p, &p, 1); |
| 240 | |
| 241 | if (*p == ',') |
| 242 | { |
| 243 | int start_m; |
| 244 | int start_n; |
| 245 | int start_d; |
| 246 | int start_leap_p; |
| 247 | int start_hour=2, start_minute=0, start_second=0; |
| 248 | |
| 249 | p++; |
| 250 | read_dst_time(p, &p, &start_m, &start_n, &start_d, &start_leap_p); |
| 251 | if (*p == '/') |
| 252 | { |
| 253 | long tmp = read_time (++p, &p, 0); |
| 254 | start_hour = tmp / 3600; |
| 255 | start_minute = (tmp % 3600) / 60; |
| 256 | start_second = tmp % 60; |
| 257 | } |
| 258 | if (*p == ',') |
| 259 | { |
| 260 | int end_m; |
| 261 | int end_n; |
| 262 | int end_d; |
| 263 | int end_leap_p; |
| 264 | int end_hour=2, end_minute=0, end_second=0; |
| 265 | |
| 266 | p++; |
| 267 | read_dst_time(p, &p, &end_m, &end_n, &end_d, &end_leap_p); |
| 268 | if (*p == '/') |
| 269 | { |
| 270 | long tmp = read_time (++p, &p, 0); |
| 271 | end_hour = tmp / 3600; |
| 272 | end_minute = (tmp % 3600) / 60; |
| 273 | end_second = tmp % 60; |
| 274 | } |
| 275 | { |
| 276 | long vms_internal_time[3][2]; |
| 277 | find_dst_time(start_m, start_n, start_d, |
| 278 | start_hour, start_minute, start_second, |
| 279 | start_leap_p, |
| 280 | vms_internal_time[0]); |
| 281 | SYS$GETTIM(&vms_internal_time[1]); |
| 282 | find_dst_time(end_m, end_n, end_d, |
| 283 | end_hour, end_minute, end_second, |
| 284 | end_leap_p, |
| 285 | vms_internal_time[2]); |
| 286 | if (cmp_vms_internal_times(vms_internal_time[0], |
| 287 | vms_internal_time[1]) < 0 |
| 288 | && cmp_vms_internal_times(vms_internal_time[1], |
| 289 | vms_internal_time[2]) < 0) |
| 290 | daylight = 1; |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | #ifdef localtime |
| 297 | #undef localtime |
| 298 | #endif |
| 299 | struct tm *sys_localtime(time_t *clock) |
| 300 | { |
| 301 | struct tm *tmp = localtime(clock); |
| 302 | |
| 303 | sys_tzset(); |
| 304 | tmp->tm_isdst = daylight; |
| 305 | |
| 306 | return tmp; |
| 307 | } |
| 308 | |
| 309 | #ifdef gmtime |
| 310 | #undef gmtime |
| 311 | #endif |
| 312 | struct tm *sys_gmtime(time_t *clock) |
| 313 | { |
| 314 | static struct tm gmt; |
| 315 | struct vms_vectim tmp_vectime; |
| 316 | long vms_internal_time[3][2]; |
| 317 | long tmp_operation = LIB$K_DELTA_SECONDS; |
| 318 | long status; |
| 319 | long tmp_offset; |
| 320 | char tmp_o_sign; |
| 321 | |
| 322 | sys_tzset(); |
| 323 | |
| 324 | if (daylight) |
| 325 | tmp_offset = internal_daylight; |
| 326 | else |
| 327 | tmp_offset = timezone; |
| 328 | |
| 329 | if (tmp_offset < 0) |
| 330 | { |
| 331 | tmp_o_sign = -1; |
| 332 | tmp_offset = -tmp_offset; |
| 333 | } |
| 334 | else |
| 335 | tmp_o_sign = 1; |
| 336 | |
| 337 | status = LIB$CVT_TO_INTERNAL_TIME(&tmp_operation, &tmp_offset, |
| 338 | vms_internal_time[1]); |
| 339 | status = SYS$GETTIM(vms_internal_time[0]); |
| 340 | if (tmp_o_sign < 0) |
| 341 | { |
| 342 | status = LIB$SUB_TIMES(vms_internal_time[0], |
| 343 | vms_internal_time[1], |
| 344 | vms_internal_time[2]); |
| 345 | } |
| 346 | else |
| 347 | { |
| 348 | status = LIB$ADD_TIMES(vms_internal_time[0], |
| 349 | vms_internal_time[1], |
| 350 | vms_internal_time[2]); |
| 351 | } |
| 352 | |
| 353 | status = SYS$NUMTIM(&tmp_vectime, vms_internal_time[2]); |
| 354 | gmt.tm_sec = tmp_vectime.second; |
| 355 | gmt.tm_min = tmp_vectime.minute; |
| 356 | gmt.tm_hour = tmp_vectime.hour; |
| 357 | gmt.tm_mday = tmp_vectime.day; |
| 358 | gmt.tm_mon = tmp_vectime.month - 1; |
| 359 | gmt.tm_year = tmp_vectime.year % 100; |
| 360 | |
| 361 | tmp_operation = LIB$K_DAY_OF_WEEK; |
| 362 | status = LIB$CVT_FROM_INTERNAL_TIME(&tmp_operation, |
| 363 | &gmt.tm_wday, |
| 364 | vms_internal_time[2]); |
| 365 | if (gmt.tm_wday == 7) gmt.tm_wday = 0; |
| 366 | |
| 367 | tmp_operation = LIB$K_DAY_OF_YEAR; |
| 368 | status = LIB$CVT_FROM_INTERNAL_TIME(&tmp_operation, |
| 369 | &gmt.tm_yday, |
| 370 | vms_internal_time[2]); |
| 371 | gmt.tm_yday--; |
| 372 | gmt.tm_isdst = daylight; |
| 373 | |
| 374 | return &gmt; |
| 375 | } |
| 376 | |