4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1987-2000 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Time management functions for auditreduce.
37 int derive_date(char *, struct tm
*);
38 void derive_str(time_t, char *);
39 int parse_time(char *, int);
40 time_t tm_to_secs(struct tm
*);
42 static int check_time(struct tm
*);
43 static int days_in_year(int);
44 static char *do_invalid(void);
45 static time_t local_to_gm(struct tm
*);
47 static char *invalid_inter
= NULL
;
50 * Array of days per month.
52 static int days_month
[] = {
53 31, 28, 31, 30, 31, 30,
54 31, 31, 30, 31, 30, 31 };
59 if (invalid_inter
== NULL
)
60 invalid_inter
= gettext("invalid date/time format -");
61 return (invalid_inter
);
65 * .func local_to_gm - local time to gm time.
66 * .desc Convert a local time to Greenwhich Mean Time.
67 * The local time is in the struct tm (time.h) format, which
68 * is easily got from an ASCII input format (10:30:33 Jan 3, 1983).
69 * It works by assuming that the given local time is a GMT time and
70 * then asking the system for the corresponding local time. It then
71 * takes the difference between those two as the correction for
72 * time zones and daylight savings time. This is accurate unless
73 * the time the user asked for is near a DST switch. Then a
74 * correction is applied - it is assumed that if we can produce
75 * a GMT that, when run through localtime(), is equivalent to the
76 * user's original input, we have an accurate GMT. The applied
77 * correction simply adjusts the GMT by the amount that the derived
78 * localtime was off. See?
79 * It should be noted that when there is DST there is one local hour
80 * a year when time occurs twice (in the fall) and one local hour a
81 * year when time never occurs (in the spring).
82 * memcpy() is used because the calls to gmtime() and localtime()
83 * return pointers to static structures that are overwritten at each
85 * .call ret = local_to_gm(tme).
86 * .arg tme - ptr to struct tm (see time.h) containing local time.
87 * .ret time_t - seconds since epoch of equivalent GMT.
90 local_to_gm(struct tm
*tme
)
92 time_t secs
, gsecs
, lsecs
, save_gsecs
;
93 time_t r1secs
, r2secs
;
94 struct tm ltime
, gtime
;
97 * Get the input time in local and gmtime assuming the input
98 * was GMT (which it probably wasn't).
100 r1secs
= secs
= tm_to_secs(tme
);
101 (void) memcpy((void *)>ime
, (void *)gmtime(&secs
), sizeof (gtime
));
102 (void) memcpy((void *)<ime
, (void *)localtime(&secs
), sizeof (ltime
));
105 * Get the local and gmtime in seconds, from the above tm structures.
106 * Calculate difference between local and GMT.
108 gsecs
= tm_to_secs(>ime
);
109 lsecs
= tm_to_secs(<ime
);
110 secs
= lsecs
- gsecs
;
112 (void) memcpy((void *)<ime
, (void *)localtime(&gsecs
),
116 * Now get a computed local time from the computed gmtime.
119 r2secs
= tm_to_secs(<ime
);
122 * If the user given local time is != computed local time then
123 * we need to try a correction.
125 if (r1secs
!= r2secs
) {
127 * Use the difference between give localtime and computed
128 * localtime as our correction.
130 if (r2secs
> r1secs
) {
131 gsecs
-= r2secs
- r1secs
;
133 gsecs
+= r1secs
- r2secs
;
136 * And try the comparison again...
138 (void) memcpy((void *)<ime
, (void *)localtime(&gsecs
),
140 r2secs
= tm_to_secs(<ime
);
142 * If the correction fails then we are on a DST line
143 * and the user-given local time never happened.
144 * Do the best we can.
146 if (r1secs
!= r2secs
) {
155 * .func tm_to_secs - convert to seconds.
156 * .desc Convert a tm time structure (time.h) into seconds since
157 * Jan 1, 1970 00:00:00. The time is assumed to be GMT and
158 * so no daylight savings time correction is applied. That
159 * is left up to the system calls (localtime(), gmtime()).
160 * .call ret = tm_to_secs(tme).
161 * .arg tme - ptr to tm structure.
162 * .ret time_t - number of seconds.
165 tm_to_secs(struct tm
*tme
)
167 int leap_year
= FALSE
;
171 int sec
= tme
->tm_sec
;
172 int min
= tme
->tm_min
;
173 int hour
= tme
->tm_hour
;
174 int day
= tme
->tm_mday
;
175 int month
= tme
->tm_mon
;
176 int year
= tme
->tm_year
+ 1900;
178 if (days_in_year(year
) == 366)
181 while (year
> 1970) {
182 num_sec
+= days_in_year(--year
) * 24 * 60 * 60;
185 days
= days_month
[--month
];
186 if (leap_year
&& month
== 1) { /* 1 is February */
189 num_sec
+= days
* 24 * 60 * 60;
191 num_sec
+= --day
* 24 * 60 * 60;
192 num_sec
+= hour
* 60 * 60;
201 * .func check_time - check tm structure.
202 * .desc Check the time in a tm structure to see if all of the fields
204 * .call err = check_time(tme).
205 * .arg tme - ptr to struct tm (see time.h).
206 * .ret 0 - time is ok.
207 * .ret -1 - time had a problem (description in error_str).
210 check_time(struct tm
*tme
)
214 if (tme
->tm_sec
< 0 || tme
->tm_sec
> 59) {
215 (void) sprintf(errbuf
,
216 gettext("seconds out of range (%d)"), tme
->tm_sec
+ 1);
218 } else if (tme
->tm_min
< 0 || tme
->tm_min
> 59) {
219 (void) sprintf(errbuf
,
220 gettext("minutes out of range (%d)"), tme
->tm_min
+ 1);
222 } else if (tme
->tm_hour
< 0 || tme
->tm_hour
> 23) {
223 (void) sprintf(errbuf
,
224 gettext("hours out of range (%d)"), tme
->tm_hour
+ 1);
226 } else if (tme
->tm_mon
< 0 || tme
->tm_mon
> 11) {
227 (void) sprintf(errbuf
,
228 gettext("months out of range (%d)"), tme
->tm_mon
+ 1);
230 } else if (tme
->tm_year
< 0) {
231 (void) sprintf(errbuf
,
232 gettext("years out of range (%d)"), tme
->tm_year
);
234 } else if (tme
->tm_mday
< 1 || tme
->tm_mday
> days_month
[tme
->tm_mon
]) {
235 if (!(days_in_year(tme
->tm_year
+ 1900) == 366 &&
237 tme
->tm_mday
== 29)) { /* leap year and February */
238 (void) sprintf(errbuf
,
239 gettext("days out of range (%d)"), tme
->tm_mday
);
242 } else if (tme
->tm_wday
< 0 || tme
->tm_wday
> 6) {
243 (void) sprintf(errbuf
,
244 gettext("weekday out of range (%d)"), tme
->tm_wday
);
246 } else if (tme
->tm_yday
< 0 || tme
->tm_yday
> 365) {
247 (void) sprintf(errbuf
,
248 gettext("day of year out of range (%d)"), tme
->tm_yday
);
252 if (error_str
== NULL
)
261 * .desc Parse a user time from the command line. The user time is assumed
263 * Supported formats currently are:
264 * 1. +xt - where x is a number and t is a type.
265 * types are - 's' second, 'm' minute, 'h' hour, and 'd' day.
266 * 2. yymmdd - yyyymmdd.
267 * yymmddhh - yyyymmddhh.
268 * yymmddhhmm - yyyymmddhhmm.
269 * yymmddhhmmss - yyyymmddhhmmss.
270 * .call err = parse_time(str, opt).
271 * .arg str - ptr to user input string.
272 * .arg opt - time option being processed.
273 * .ret 0 - succesful.
274 * .ret -1 - failure (error message in error_str).
277 parse_time(char *str
, int opt
)
279 int ret
, len
, factor
;
286 * If the strlen < 6 then in the "-b +2d" type of format.
290 (void) sprintf(errbuf
, gettext("%s needs '+' (%s)"),
296 (void) sprintf(errbuf
,
297 gettext("%s only allowed with 'b' option (%s)"),
303 (void) sprintf(errbuf
,
304 gettext("must have -a to use -b +nx form (%s)"),
310 * Find out what type of offset it is - 's' 'm' 'h' or 'd'.
311 * Make sure that the offset is all numbers.
313 if ((strxx
= strpbrk(str
, "dhms")) == NULL
) {
314 (void) sprintf(errbuf
,
315 gettext("%s needs 'd', 'h', 'm', or 's' (%s)"),
323 if (strlen(str
) != strspn(str
, "0123456789")) {
324 (void) sprintf(errbuf
,
325 gettext("%s non-numeric offset (%s)"),
330 factor
= 1; /* seconds is default */
331 if (ret
== 'd') /* days */
332 factor
= 24 * 60 * 60;
333 else if (ret
== 'h') /* hours */
335 else if (ret
== 'm') /* minutes */
338 m_before
= m_after
+ (lnum
* factor
);
342 * Must be a specific date/time format.
344 if (derive_date(str
, &thentime
))
347 * For 'd' option clear out the hh:mm:ss to get to the start of the day.
348 * Then add one day's worth of seconds to get the 'b' time.
353 thentime
.tm_hour
= 0;
354 m_after
= local_to_gm(&thentime
);
355 m_before
= m_after
+ (24 * 60 * 60);
356 } else if (opt
== 'a') {
357 m_after
= local_to_gm(&thentime
);
358 } else if (opt
== 'b') {
359 m_before
= local_to_gm(&thentime
);
367 * .desc Derive a date/time structure (tm) from a string.
368 * String is in one of these formats:
373 * .call ret = derive_date(str, tme).
374 * .arg str - ptr to input string.
375 * .arg tme - ptr to tm structure (time.h).
376 * .ret 0 - no errors in string.
377 * .ret -1 - errors in string (description in error_str).
380 derive_date(char *str
, struct tm
*tme
)
383 char *digits
= "0123456789";
389 if (len
!= strspn(str
, digits
)) {
390 (void) sprintf(errbuf
, gettext("%s not all digits (%s)"),
396 (void) sprintf(errbuf
, gettext("%s odd number of digits (%s)"),
402 * May need larger string storage to add '19' or '20'.
404 strs
= (char *)a_calloc(1, len
+ 4);
407 * Get current time to see what century it is.
409 (void) memcpy((char *)&nowtime
, (char *)gmtime(&time_now
),
412 * If the year does not begin with '19' or '20', then report
413 * an error and abort.
415 if ((str
[0] != '1' || str
[1] != '9') && /* 19XX */
416 (str
[0] != '2' || str
[1] != '0')) { /* 20XX */
417 (void) sprintf(errbuf
, gettext("invalid year (%c%c%c%c)"),
418 str
[0], str
[1], str
[2], str
[3]);
424 len
= strlen(str
); /* may have changed */
425 if (len
< 8 || len
> 14) {
426 (void) sprintf(errbuf
,
427 gettext("invalid date/time length (%s)"), str
);
432 /* unspecified values go to 0 */
433 (void) memset((void *) tme
, 0, (size_t)sizeof (*tme
));
434 (void) strncpy(strs
, str
, 4);
436 tme
->tm_year
= atoi(strs
) - 1900; /* get the year */
437 (void) strncpy(strs
, str
+ 4, 2);
439 tme
->tm_mon
= atoi(strs
) - 1; /* get months */
440 (void) strncpy(strs
, str
+ 6, 2);
442 tme
->tm_mday
= atoi(strs
); /* get days */
443 if (len
>= 10) { /* yyyymmddhh */
444 (void) strncpy(strs
, str
+ 8, 2);
446 tme
->tm_hour
= atoi(strs
); /* get hours */
448 if (len
>= 12) { /* yyyymmddhhmm */
449 (void) strncpy(strs
, str
+ 10, 2);
451 tme
->tm_min
= atoi(strs
); /* get minutes */
453 if (len
>= 14) { /* yyyymmddhhmmss */
454 (void) strncpy(strs
, str
+ 12, 2);
456 tme
->tm_sec
= atoi(strs
); /* get seconds */
459 return (check_time(tme
)); /* lastly check the ranges */
464 * .func derive_str - derive string.
465 * .desc Derive a string representation of a time for a filename.
466 * The output is in the 14 character format yyyymmddhhmmss.
467 * .call derive_str(clock, buf).
468 * .arg clock - seconds since epoch.
469 * .arg buf - place to put resultant string.
473 derive_str(time_t clock
, char *buf
)
477 (void) memcpy((void *) & gtime
, (void *)gmtime(&clock
), sizeof (gtime
));
479 (void) sprintf(buf
, "%4d", gtime
.tm_year
+ 1900);
480 (void) sprintf(buf
+ 4, "%.2d", gtime
.tm_mon
+ 1);
481 (void) sprintf(buf
+ 6, "%.2d", gtime
.tm_mday
);
482 (void) sprintf(buf
+ 8, "%.2d", gtime
.tm_hour
);
483 (void) sprintf(buf
+ 10, "%.2d", gtime
.tm_min
);
484 (void) sprintf(buf
+ 12, "%.2d", gtime
.tm_sec
);
490 days_in_year(int year
)