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-22 16:34 (seconds will be set to 0)
183 * 2012-09-22 (time will be set to 00:00:00)
184 * 16:34:22 (date will be set to today)
185 * 16:34 (date will be set to today, seconds to 0)
187 * yesterday (time is set to 00:00:00)
188 * today (time is set to 00:00:00)
189 * tomorrow (time is set to 00:00:00)
199 localtime_r(&x
, &tm
);
205 else if (streq(t
, "today")) {
206 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
209 } else if (streq(t
, "yesterday")) {
211 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
214 } else if (streq(t
, "tomorrow")) {
216 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
219 } else if (t
[0] == '+') {
221 r
= parse_sec(t
+ 1, &plus
);
226 } else if (t
[0] == '-') {
228 r
= parse_sec(t
+ 1, &minus
);
234 } else if (endswith(t
, " ago")) {
237 z
= strndup(t
, strlen(t
) - 4);
241 r
= parse_sec(z
, &minus
);
249 for (i
= 0; i
< ARRAY_SIZE(day_nr
); i
++) {
252 if (!startswith_no_case(t
, day_nr
[i
].name
))
255 skip
= strlen(day_nr
[i
].name
);
259 weekday
= day_nr
[i
].nr
;
265 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
270 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
275 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
282 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
289 k
= strptime(t
, "%y-%m-%d", &tm
);
291 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
296 k
= strptime(t
, "%Y-%m-%d", &tm
);
298 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
303 k
= strptime(t
, "%H:%M:%S", &tm
);
308 k
= strptime(t
, "%H:%M", &tm
);
315 k
= strptime(t
, "%Y%m%d%H%M%S", &tm
);
328 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
331 ret
= (usec_t
) x
*USEC_PER_SEC
;
344 static int format_iso_time(struct tm
*tm
, suseconds_t usec
, int flags
, char *buf
, size_t bufsz
)
349 if (flags
& ISO_8601_DATE
) {
350 len
= snprintf(p
, bufsz
, "%4d-%.2d-%.2d", tm
->tm_year
+ 1900,
351 tm
->tm_mon
+ 1, tm
->tm_mday
);
352 if (len
< 0 || (size_t) len
> bufsz
)
358 if ((flags
& ISO_8601_DATE
) && (flags
& ISO_8601_TIME
)) {
361 *p
++ = (flags
& ISO_8601_SPACE
) ? ' ' : 'T';
365 if (flags
& ISO_8601_TIME
) {
366 len
= snprintf(p
, bufsz
, "%02d:%02d:%02d", tm
->tm_hour
,
367 tm
->tm_min
, tm
->tm_sec
);
368 if (len
< 0 || (size_t) len
> bufsz
)
374 if (flags
& ISO_8601_DOTUSEC
) {
375 len
= snprintf(p
, bufsz
, ".%06ld", (long) usec
);
376 if (len
< 0 || (size_t) len
> bufsz
)
381 } else if (flags
& ISO_8601_COMMAUSEC
) {
382 len
= snprintf(p
, bufsz
, ",%06ld", (long) usec
);
383 if (len
< 0 || (size_t) len
> bufsz
)
389 if (flags
& ISO_8601_TIMEZONE
&& strftime(p
, bufsz
, "%z", tm
) <= 0)
395 /* timeval to ISO 8601 */
396 int strtimeval_iso(struct timeval
*tv
, int flags
, char *buf
, size_t bufsz
)
400 if (flags
& ISO_8601_GMTIME
)
401 tm
= *gmtime(&tv
->tv_sec
);
403 tm
= *localtime(&tv
->tv_sec
);
404 return format_iso_time(&tm
, tv
->tv_usec
, flags
, buf
, bufsz
);
407 /* struct tm to ISO 8601 */
408 int strtm_iso(struct tm
*tm
, int flags
, char *buf
, size_t bufsz
)
410 return format_iso_time(tm
, 0, flags
, buf
, bufsz
);
413 /* time_t to ISO 8601 */
414 int strtime_iso(const time_t *t
, int flags
, char *buf
, size_t bufsz
)
418 if (flags
& ISO_8601_GMTIME
)
422 return format_iso_time(&tm
, 0, flags
, buf
, bufsz
);
425 /* relative time functions */
426 int time_is_today(const time_t *t
, struct timeval
*now
)
428 if (now
->tv_sec
== 0)
429 gettimeofday(now
, NULL
);
430 return *t
/ (3600 * 24) == now
->tv_sec
/ (3600 * 24);
433 int time_is_thisyear(const time_t *t
, struct timeval
*now
)
435 if (now
->tv_sec
== 0)
436 gettimeofday(now
, NULL
);
437 return *t
/ (3600 * 24 * 365) == now
->tv_sec
/ (3600 * 24 * 365);
440 int strtime_short(const time_t *t
, struct timeval
*now
, int flags
, char *buf
, size_t bufsz
)
447 if (time_is_today(t
, now
)) {
448 rc
= snprintf(buf
, bufsz
, "%02d:%02d", tm
.tm_hour
, tm
.tm_min
);
449 if (rc
< 0 || (size_t) rc
> bufsz
)
453 } else if (time_is_thisyear(t
, now
)) {
454 if (flags
& UL_SHORTTIME_THISYEAR_HHMM
)
455 rc
= strftime(buf
, bufsz
, "%b%d/%H:%M", &tm
);
457 rc
= strftime(buf
, bufsz
, "%b%d", &tm
);
459 rc
= strftime(buf
, bufsz
, "%Y-%b%d", &tm
);
461 return rc
<= 0 ? -1 : 0;
465 time_t timegm(struct tm
*tm
)
467 const char *zone
= getenv("TZ");
474 setenv("TZ", zone
, 1);
480 #endif /* HAVE_TIMEGM */
482 #ifdef TEST_PROGRAM_TIMEUTILS
484 int main(int argc
, char *argv
[])
486 struct timeval tv
= { 0 };
487 char buf
[ISO_8601_BUFSIZ
];
490 fprintf(stderr
, "usage: %s <time> [<usec>]\n", argv
[0]);
494 tv
.tv_sec
= strtos64_or_err(argv
[1], "failed to parse <time>");
496 tv
.tv_usec
= strtos64_or_err(argv
[2], "failed to parse <usec>");
498 strtimeval_iso(&tv
, ISO_8601_DATE
, buf
, sizeof(buf
));
499 printf("Date: '%s'\n", buf
);
501 strtimeval_iso(&tv
, ISO_8601_TIME
, buf
, sizeof(buf
));
502 printf("Time: '%s'\n", buf
);
504 strtimeval_iso(&tv
, ISO_8601_DATE
| ISO_8601_TIME
| ISO_8601_COMMAUSEC
,
506 printf("Full: '%s'\n", buf
);
508 strtimeval_iso(&tv
, ISO_8601_DATE
| ISO_8601_TIME
| ISO_8601_DOTUSEC
|
509 ISO_8601_TIMEZONE
| ISO_8601_SPACE
,
511 printf("Zone: '%s'\n", buf
);
516 #endif /* TEST_PROGRAM_TIMEUTILS */