2 First set of functions in this file are part of systemd, and were
3 copied to util-linux at August 2013.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with util-linux; If not, see <http://www.gnu.org/licenses/>.
31 #include "timeutils.h"
33 #define WHITESPACE " \t\n\r"
35 #define streq(a,b) (strcmp((a),(b)) == 0)
37 static int parse_sec(const char *t
, usec_t
*usec
)
43 { "seconds", USEC_PER_SEC
},
44 { "second", USEC_PER_SEC
},
45 { "sec", USEC_PER_SEC
},
46 { "s", USEC_PER_SEC
},
47 { "minutes", USEC_PER_MINUTE
},
48 { "minute", USEC_PER_MINUTE
},
49 { "min", USEC_PER_MINUTE
},
50 { "months", USEC_PER_MONTH
},
51 { "month", USEC_PER_MONTH
},
52 { "msec", USEC_PER_MSEC
},
53 { "ms", USEC_PER_MSEC
},
54 { "m", USEC_PER_MINUTE
},
55 { "hours", USEC_PER_HOUR
},
56 { "hour", USEC_PER_HOUR
},
57 { "hr", USEC_PER_HOUR
},
58 { "h", USEC_PER_HOUR
},
59 { "days", USEC_PER_DAY
},
60 { "day", USEC_PER_DAY
},
61 { "d", USEC_PER_DAY
},
62 { "weeks", USEC_PER_WEEK
},
63 { "week", USEC_PER_WEEK
},
64 { "w", USEC_PER_WEEK
},
65 { "years", USEC_PER_YEAR
},
66 { "year", USEC_PER_YEAR
},
67 { "y", USEC_PER_YEAR
},
70 { "", USEC_PER_SEC
}, /* default is sec */
75 int something
= FALSE
;
86 p
+= strspn(p
, WHITESPACE
);
96 l
= strtoll(p
, &e
, 10);
108 z
= strtoll(b
, &e
, 10);
123 e
+= strspn(e
, WHITESPACE
);
125 for (i
= 0; i
< ARRAY_SIZE(table
); i
++)
126 if (startswith(e
, table
[i
].suffix
)) {
127 usec_t k
= (usec_t
) z
* table
[i
].usec
;
132 r
+= (usec_t
) l
*table
[i
].usec
+ k
;
133 p
= e
+ strlen(table
[i
].suffix
);
139 if (i
>= ARRAY_SIZE(table
))
149 int parse_timestamp(const char *t
, usec_t
*usec
)
151 static const struct {
174 usec_t plus
= 0, minus
= 0, ret
;
181 * 2012-09-22 16:34:22
182 * 2012-09-22T16:34:22
183 * 2012-09-22 16:34 (seconds will be set to 0)
184 * 2012-09-22 (time will be set to 00:00:00)
185 * 16:34:22 (date will be set to today)
186 * 16:34 (date will be set to today, seconds to 0)
188 * yesterday (time is set to 00:00:00)
189 * today (time is set to 00:00:00)
190 * tomorrow (time is set to 00:00:00)
200 localtime_r(&x
, &tm
);
206 else if (streq(t
, "today")) {
207 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
210 } else if (streq(t
, "yesterday")) {
212 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
215 } else if (streq(t
, "tomorrow")) {
217 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
220 } else if (t
[0] == '+') {
222 r
= parse_sec(t
+ 1, &plus
);
227 } else if (t
[0] == '-') {
229 r
= parse_sec(t
+ 1, &minus
);
235 } else if (endswith(t
, " ago")) {
238 z
= strndup(t
, strlen(t
) - 4);
242 r
= parse_sec(z
, &minus
);
250 for (i
= 0; i
< ARRAY_SIZE(day_nr
); i
++) {
253 if (!startswith_no_case(t
, day_nr
[i
].name
))
256 skip
= strlen(day_nr
[i
].name
);
260 weekday
= day_nr
[i
].nr
;
266 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
271 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
276 k
= strptime(t
, "%Y-%m-%dT%H:%M:%S", &tm
);
281 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
288 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
295 k
= strptime(t
, "%y-%m-%d", &tm
);
297 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
302 k
= strptime(t
, "%Y-%m-%d", &tm
);
304 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
309 k
= strptime(t
, "%H:%M:%S", &tm
);
314 k
= strptime(t
, "%H:%M", &tm
);
321 k
= strptime(t
, "%Y%m%d%H%M%S", &tm
);
334 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
337 ret
= (usec_t
) x
*USEC_PER_SEC
;
350 /* Returns the difference in seconds between its argument and GMT. If if TP is
351 * invalid or no DST information is available default to UTC, that is, zero.
352 * tzset is called so, for example, 'TZ="UTC" hwclock' will work as expected.
353 * Derived from glibc/time/strftime_l.c
355 int get_gmtoff(const struct tm
*tp
)
357 if (tp
->tm_isdst
< 0)
361 return tp
->tm_gmtoff
;
370 /* Check if mktime returning -1 is an error or a valid time_t */
371 if (lt
== (time_t) -1) {
372 if (! localtime_r(<
, &tm
)
373 || ((ltm
.tm_sec
^ tm
.tm_sec
)
374 | (ltm
.tm_min
^ tm
.tm_min
)
375 | (ltm
.tm_hour
^ tm
.tm_hour
)
376 | (ltm
.tm_mday
^ tm
.tm_mday
)
377 | (ltm
.tm_mon
^ tm
.tm_mon
)
378 | (ltm
.tm_year
^ tm
.tm_year
)))
382 if (! gmtime_r(<
, >m
))
385 /* Calculate the GMT offset, that is, the difference between the
386 * TP argument (ltm) and GMT (gtm).
388 * Compute intervening leap days correctly even if year is negative.
389 * Take care to avoid int overflow in leap day calculations, but it's OK
390 * to assume that A and B are close to each other.
392 int a4
= (ltm
.tm_year
>> 2) + (1900 >> 2) - ! (ltm
.tm_year
& 3);
393 int b4
= (gtm
.tm_year
>> 2) + (1900 >> 2) - ! (gtm
.tm_year
& 3);
394 int a100
= a4
/ 25 - (a4
% 25 < 0);
395 int b100
= b4
/ 25 - (b4
% 25 < 0);
396 int a400
= a100
>> 2;
397 int b400
= b100
>> 2;
398 int intervening_leap_days
= (a4
- b4
) - (a100
- b100
) + (a400
- b400
);
400 int years
= ltm
.tm_year
- gtm
.tm_year
;
401 int days
= (365 * years
+ intervening_leap_days
402 + (ltm
.tm_yday
- gtm
.tm_yday
));
404 return (60 * (60 * (24 * days
+ (ltm
.tm_hour
- gtm
.tm_hour
))
405 + (ltm
.tm_min
- gtm
.tm_min
)) + (ltm
.tm_sec
- gtm
.tm_sec
));
409 static int format_iso_time(struct tm
*tm
, suseconds_t usec
, int flags
, char *buf
, size_t bufsz
)
414 if (flags
& ISO_DATE
) {
415 len
= snprintf(p
, bufsz
, "%4ld-%.2d-%.2d",
416 tm
->tm_year
+ (long) 1900,
417 tm
->tm_mon
+ 1, tm
->tm_mday
);
418 if (len
< 0 || (size_t) len
> bufsz
)
424 if ((flags
& ISO_DATE
) && (flags
& ISO_TIME
)) {
427 *p
++ = (flags
& ISO_T
) ? 'T' : ' ';
431 if (flags
& ISO_TIME
) {
432 len
= snprintf(p
, bufsz
, "%02d:%02d:%02d", tm
->tm_hour
,
433 tm
->tm_min
, tm
->tm_sec
);
434 if (len
< 0 || (size_t) len
> bufsz
)
440 if (flags
& ISO_DOTUSEC
) {
441 len
= snprintf(p
, bufsz
, ".%06ld", (long) usec
);
442 if (len
< 0 || (size_t) len
> bufsz
)
447 } else if (flags
& ISO_COMMAUSEC
) {
448 len
= snprintf(p
, bufsz
, ",%06ld", (long) usec
);
449 if (len
< 0 || (size_t) len
> bufsz
)
455 if (flags
& ISO_TIMEZONE
) {
456 int tmin
= get_gmtoff(tm
) / 60;
457 int zhour
= tmin
/ 60;
458 int zmin
= abs(tmin
% 60);
459 len
= snprintf(p
, bufsz
, "%+03d:%02d", zhour
,zmin
);
460 if (len
< 0 || (size_t) len
> bufsz
)
465 warnx(_("format_iso_time: buffer overflow."));
469 /* timeval to ISO 8601 */
470 int strtimeval_iso(struct timeval
*tv
, int flags
, char *buf
, size_t bufsz
)
475 if (flags
& ISO_GMTIME
)
476 rc
= gmtime_r(&tv
->tv_sec
, &tm
);
478 rc
= localtime_r(&tv
->tv_sec
, &tm
);
481 return format_iso_time(&tm
, tv
->tv_usec
, flags
, buf
, bufsz
);
483 warnx(_("time %ld is out of range."), tv
->tv_sec
);
487 /* struct tm to ISO 8601 */
488 int strtm_iso(struct tm
*tm
, int flags
, char *buf
, size_t bufsz
)
490 return format_iso_time(tm
, 0, flags
, buf
, bufsz
);
493 /* time_t to ISO 8601 */
494 int strtime_iso(const time_t *t
, int flags
, char *buf
, size_t bufsz
)
499 if (flags
& ISO_GMTIME
)
500 rc
= gmtime_r(t
, &tm
);
502 rc
= localtime_r(t
, &tm
);
505 return format_iso_time(&tm
, 0, flags
, buf
, bufsz
);
507 warnx(_("time %ld is out of range."), (long)t
);
511 /* relative time functions */
512 static inline int time_is_thisyear(struct tm
const *const tm
,
513 struct tm
const *const tmnow
)
515 return tm
->tm_year
== tmnow
->tm_year
;
518 static inline int time_is_today(struct tm
const *const tm
,
519 struct tm
const *const tmnow
)
521 return (tm
->tm_yday
== tmnow
->tm_yday
&&
522 time_is_thisyear(tm
, tmnow
));
525 int strtime_short(const time_t *t
, struct timeval
*now
, int flags
, char *buf
, size_t bufsz
)
530 if (now
->tv_sec
== 0)
531 gettimeofday(now
, NULL
);
534 localtime_r(&now
->tv_sec
, &tmnow
);
536 if (time_is_today(&tm
, &tmnow
)) {
537 rc
= snprintf(buf
, bufsz
, "%02d:%02d", tm
.tm_hour
, tm
.tm_min
);
538 if (rc
< 0 || (size_t) rc
> bufsz
)
542 } else if (time_is_thisyear(&tm
, &tmnow
)) {
543 if (flags
& UL_SHORTTIME_THISYEAR_HHMM
)
544 rc
= strftime(buf
, bufsz
, "%b%d/%H:%M", &tm
);
546 rc
= strftime(buf
, bufsz
, "%b%d", &tm
);
548 rc
= strftime(buf
, bufsz
, "%Y-%b%d", &tm
);
550 return rc
<= 0 ? -1 : 0;
554 time_t timegm(struct tm
*tm
)
556 const char *zone
= getenv("TZ");
563 setenv("TZ", zone
, 1);
569 #endif /* HAVE_TIMEGM */
571 #ifdef TEST_PROGRAM_TIMEUTILS
573 int main(int argc
, char *argv
[])
575 struct timeval tv
= { 0 };
576 char buf
[ISO_BUFSIZ
];
579 fprintf(stderr
, "usage: %s [<time> [<usec>]] | [--timestamp <str>]\n", argv
[0]);
583 if (strcmp(argv
[1], "--timestamp") == 0) {
586 parse_timestamp(argv
[2], &usec
);
587 tv
.tv_sec
= (time_t) (usec
/ 1000000);
588 tv
.tv_usec
= usec
% 1000000;
590 tv
.tv_sec
= strtos64_or_err(argv
[1], "failed to parse <time>");
592 tv
.tv_usec
= strtos64_or_err(argv
[2], "failed to parse <usec>");
595 strtimeval_iso(&tv
, ISO_DATE
, buf
, sizeof(buf
));
596 printf("Date: '%s'\n", buf
);
598 strtimeval_iso(&tv
, ISO_TIME
, buf
, sizeof(buf
));
599 printf("Time: '%s'\n", buf
);
601 strtimeval_iso(&tv
, ISO_DATE
| ISO_TIME
| ISO_COMMAUSEC
| ISO_T
,
603 printf("Full: '%s'\n", buf
);
605 strtimeval_iso(&tv
, ISO_TIMESTAMP_DOT
, buf
, sizeof(buf
));
606 printf("Zone: '%s'\n", buf
);
611 #endif /* TEST_PROGRAM_TIMEUTILS */