date: factor out date parsing (in preparation for touch -d)
[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 #if ENABLE_MONOTONIC_SYSCALL
75
76 #include <sys/syscall.h>
77 /* Old glibc (< 2.3.4) does not provide this constant. We use syscall
78  * directly so this definition is safe. */
79 #ifndef CLOCK_MONOTONIC
80 #define CLOCK_MONOTONIC 1
81 #endif
82
83 /* libc has incredibly messy way of doing this,
84  * typically requiring -lrt. We just skip all this mess */
85 static void get_mono(struct timespec *ts)
86 {
87         if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts))
88                 bb_error_msg_and_die("clock_gettime(MONOTONIC) failed");
89 }
90 unsigned long long FAST_FUNC monotonic_ns(void)
91 {
92         struct timespec ts;
93         get_mono(&ts);
94         return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
95 }
96 unsigned long long FAST_FUNC monotonic_us(void)
97 {
98         struct timespec ts;
99         get_mono(&ts);
100         return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
101 }
102 unsigned FAST_FUNC monotonic_sec(void)
103 {
104         struct timespec ts;
105         get_mono(&ts);
106         return ts.tv_sec;
107 }
108
109 #else
110
111 unsigned long long FAST_FUNC monotonic_ns(void)
112 {
113         struct timeval tv;
114         gettimeofday(&tv, NULL);
115         return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
116 }
117 unsigned long long FAST_FUNC monotonic_us(void)
118 {
119         struct timeval tv;
120         gettimeofday(&tv, NULL);
121         return tv.tv_sec * 1000000ULL + tv.tv_usec;
122 }
123 unsigned FAST_FUNC monotonic_sec(void)
124 {
125         return time(NULL);
126 }
127
128 #endif