summaryrefslogtreecommitdiff
path: root/src/basic/time-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/time-util.c')
-rw-r--r--src/basic/time-util.c161
1 files changed, 103 insertions, 58 deletions
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 531931f6e1..d4e0914b27 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <time.h>
#include <string.h>
#include <sys/timex.h>
#include <sys/timerfd.h>
@@ -205,11 +204,8 @@ static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc)
return NULL;
sec = (time_t) (t / USEC_PER_SEC);
+ localtime_or_gmtime_r(&sec, &tm, utc);
- if (utc)
- gmtime_r(&sec, &tm);
- else
- localtime_r(&sec, &tm);
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
return NULL;
@@ -235,10 +231,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut
return NULL;
sec = (time_t) (t / USEC_PER_SEC);
- if (utc)
- gmtime_r(&sec, &tm);
- else
- localtime_r(&sec, &tm);
+ localtime_or_gmtime_r(&sec, &tm, utc);
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
return NULL;
@@ -484,9 +477,10 @@ int parse_timestamp(const char *t, usec_t *usec) {
};
const char *k;
+ bool utc;
struct tm tm, copy;
time_t x;
- usec_t plus = 0, minus = 0, ret;
+ usec_t x_usec, plus = 0, minus = 0, ret;
int r, weekday = -1;
unsigned i;
@@ -511,28 +505,15 @@ int parse_timestamp(const char *t, usec_t *usec) {
assert(t);
assert(usec);
- x = time(NULL);
- assert_se(localtime_r(&x, &tm));
- tm.tm_isdst = -1;
-
- if (streq(t, "now"))
- goto finish;
-
- else if (streq(t, "today")) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ if (t[0] == '@')
+ return parse_sec(t + 1, usec);
- } else if (streq(t, "yesterday")) {
- tm.tm_mday --;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ ret = now(CLOCK_REALTIME);
- } else if (streq(t, "tomorrow")) {
- tm.tm_mday ++;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ if (streq(t, "now"))
goto finish;
- } else if (t[0] == '+') {
+ else if (t[0] == '+') {
r = parse_sec(t+1, &plus);
if (r < 0)
return r;
@@ -546,35 +527,51 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish;
- } else if (t[0] == '@')
- return parse_sec(t + 1, usec);
-
- else if (endswith(t, " ago")) {
- _cleanup_free_ char *z;
-
- z = strndup(t, strlen(t) - 4);
- if (!z)
- return -ENOMEM;
+ } else if (endswith(t, " ago")) {
+ t = strndupa(t, strlen(t) - strlen(" ago"));
- r = parse_sec(z, &minus);
+ r = parse_sec(t, &minus);
if (r < 0)
return r;
goto finish;
- } else if (endswith(t, " left")) {
- _cleanup_free_ char *z;
- z = strndup(t, strlen(t) - 4);
- if (!z)
- return -ENOMEM;
+ } else if (endswith(t, " left")) {
+ t = strndupa(t, strlen(t) - strlen(" left"));
- r = parse_sec(z, &plus);
+ r = parse_sec(t, &plus);
if (r < 0)
return r;
goto finish;
}
+ utc = endswith_no_case(t, " UTC");
+ if (utc)
+ t = strndupa(t, strlen(t) - strlen(" UTC"));
+
+ x = ret / USEC_PER_SEC;
+ x_usec = 0;
+
+ assert_se(localtime_or_gmtime_r(&x, &tm, utc));
+ tm.tm_isdst = -1;
+
+ if (streq(t, "today")) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto from_tm;
+
+ } else if (streq(t, "yesterday")) {
+ tm.tm_mday --;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto from_tm;
+
+ } else if (streq(t, "tomorrow")) {
+ tm.tm_mday ++;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto from_tm;
+ }
+
+
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
@@ -592,66 +589,106 @@ int parse_timestamp(const char *t, usec_t *usec) {
copy = tm;
k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
tm = copy;
k = strptime(t, "%y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%y-%m-%d", &tm);
if (k && *k == 0) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%Y-%m-%d", &tm);
if (k && *k == 0) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
tm = copy;
k = strptime(t, "%H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
- goto finish;
+ goto from_tm;
}
return -EINVAL;
-finish:
- x = mktime(&tm);
+parse_usec:
+ {
+ char *end;
+ unsigned long long val;
+ size_t l;
+
+ k++;
+ if (*k < '0' || *k > '9')
+ return -EINVAL;
+
+ /* base 10 instead of base 0, .09 is not base 8 */
+ errno = 0;
+ val = strtoull(k, &end, 10);
+ if (*end || errno)
+ return -EINVAL;
+
+ l = end-k;
+
+ /* val has l digits, make them 6 */
+ for (; l < 6; l++)
+ val *= 10;
+ for (; l > 6; l--)
+ val /= 10;
+
+ x_usec = val;
+ }
+
+from_tm:
+ x = mktime_or_timegm(&tm, utc);
if (x == (time_t) -1)
return -EINVAL;
if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL;
- ret = (usec_t) x * USEC_PER_SEC;
+ ret = (usec_t) x * USEC_PER_SEC + x_usec;
+finish:
ret += plus;
if (ret > minus)
ret -= minus;
@@ -1072,3 +1109,11 @@ int get_timezone(char **tz) {
*tz = z;
return 0;
}
+
+time_t mktime_or_timegm(struct tm *tm, bool utc) {
+ return utc ? timegm(tm) : mktime(tm);
+}
+
+struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
+ return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
+}