touch: implement -d --date (our own testsuite needs that)
[oweals/busybox.git] / libbb / time.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) 2007 Denys Vlasenko
6  *
7  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
8  */
9 #include "libbb.h"
10
11 void FAST_FUNC parse_datestr(const char *date_str, struct tm *tm_time)
12 {
13         char end = '\0';
14         const char *last_colon = strrchr(date_str, ':');
15
16         if (last_colon != NULL) {
17                 /* Parse input and assign appropriately to tm_time */
18
19                 if (sscanf(date_str, "%u:%u%c",
20                                         &tm_time->tm_hour,
21                                         &tm_time->tm_min,
22                                         &end) >= 2) {
23                         /* no adjustments needed */
24                 } else if (sscanf(date_str, "%u.%u-%u:%u%c",
25                                         &tm_time->tm_mon, &tm_time->tm_mday,
26                                         &tm_time->tm_hour, &tm_time->tm_min,
27                                         &end) >= 4) {
28                         /* Adjust dates from 1-12 to 0-11 */
29                         tm_time->tm_mon -= 1;
30                 } else if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &tm_time->tm_year,
31                                         &tm_time->tm_mon, &tm_time->tm_mday,
32                                         &tm_time->tm_hour, &tm_time->tm_min,
33                                         &end) >= 5) {
34                         tm_time->tm_year -= 1900; /* Adjust years */
35                         tm_time->tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
36                 } else if (sscanf(date_str, "%u-%u-%u %u:%u%c", &tm_time->tm_year,
37                                         &tm_time->tm_mon, &tm_time->tm_mday,
38                                         &tm_time->tm_hour, &tm_time->tm_min,
39                                         &end) >= 5) {
40                         tm_time->tm_year -= 1900; /* Adjust years */
41                         tm_time->tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
42 //TODO: coreutils 6.9 also accepts "YYYY-MM-DD HH" (no minutes)
43                 } else {
44                         bb_error_msg_and_die(bb_msg_invalid_date, date_str);
45                 }
46                 if (end == ':') {
47                         if (sscanf(last_colon + 1, "%u%c", &tm_time->tm_sec, &end) == 1)
48                                 end = '\0';
49                         /* else end != NUL and we error out */
50                 }
51         } else {
52                 if (sscanf(date_str, "%2u%2u%2u%2u%u%c", &tm_time->tm_mon,
53                                 &tm_time->tm_mday, &tm_time->tm_hour, &tm_time->tm_min,
54                                 &tm_time->tm_year, &end) < 4)
55                         bb_error_msg_and_die(bb_msg_invalid_date, date_str);
56                 /* correct for century  - minor Y2K problem here? */
57                 if (tm_time->tm_year >= 1900) {
58                         tm_time->tm_year -= 1900;
59                 }
60                 /* adjust date */
61                 tm_time->tm_mon -= 1;
62                 if (end == '.') {
63                         if (sscanf(strchr(date_str, '.') + 1, "%u%c",
64                                         &tm_time->tm_sec, &end) == 1)
65                                 end = '\0';
66                         /* else end != NUL and we error out */
67                 }
68         }
69         if (end != '\0') {
70                 bb_error_msg_and_die(bb_msg_invalid_date, date_str);
71         }
72 }
73
74 time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *tm_time)
75 {
76         time_t t = mktime(tm_time);
77         if (t == (time_t) -1L) {
78                 bb_error_msg_and_die(bb_msg_invalid_date, date_str);
79         }
80         return t;
81 }
82
83 #if ENABLE_MONOTONIC_SYSCALL
84
85 #include <sys/syscall.h>
86 /* Old glibc (< 2.3.4) does not provide this constant. We use syscall
87  * directly so this definition is safe. */
88 #ifndef CLOCK_MONOTONIC
89 #define CLOCK_MONOTONIC 1
90 #endif
91
92 /* libc has incredibly messy way of doing this,
93  * typically requiring -lrt. We just skip all this mess */
94 static void get_mono(struct timespec *ts)
95 {
96         if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts))
97                 bb_error_msg_and_die("clock_gettime(MONOTONIC) failed");
98 }
99 unsigned long long FAST_FUNC monotonic_ns(void)
100 {
101         struct timespec ts;
102         get_mono(&ts);
103         return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
104 }
105 unsigned long long FAST_FUNC monotonic_us(void)
106 {
107         struct timespec ts;
108         get_mono(&ts);
109         return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
110 }
111 unsigned FAST_FUNC monotonic_sec(void)
112 {
113         struct timespec ts;
114         get_mono(&ts);
115         return ts.tv_sec;
116 }
117
118 #else
119
120 unsigned long long FAST_FUNC monotonic_ns(void)
121 {
122         struct timeval tv;
123         gettimeofday(&tv, NULL);
124         return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
125 }
126 unsigned long long FAST_FUNC monotonic_us(void)
127 {
128         struct timeval tv;
129         gettimeofday(&tv, NULL);
130         return tv.tv_sec * 1000000ULL + tv.tv_usec;
131 }
132 unsigned FAST_FUNC monotonic_sec(void)
133 {
134         return time(NULL);
135 }
136
137 #endif