time-util.c revision 7568345034f2890af745747783c5abfbf6eccf0f
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering/***
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering This file is part of systemd.
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering Copyright 2010 Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering systemd is free software; you can redistribute it and/or modify it
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering under the terms of the GNU Lesser General Public License as published by
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering (at your option) any later version.
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering systemd is distributed in the hope that it will be useful, but
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering Lesser General Public License for more details.
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering You should have received a copy of the GNU Lesser General Public License
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering***/
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen#include <time.h>
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen#include <string.h>
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen#include <sys/timex.h>
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering#include "util.h"
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering#include "time-util.h"
93cc7779e0c121b75183920173f37cd1ee9d59cfThomas Hindoe Paaboel Andersen#include "strv.h"
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poetteringusec_t now(clockid_t clock_id) {
8b43440b7ef4b81c69c31de7ff820dc07a780254Lennart Poettering struct timespec ts;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering assert_se(clock_gettime(clock_id, &ts) == 0);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering return timespec_load(&ts);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering}
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poetteringdual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering assert(ts);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering ts->realtime = now(CLOCK_REALTIME);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering ts->monotonic = now(CLOCK_MONOTONIC);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering return ts;
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering}
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poetteringdual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering int64_t delta;
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering assert(ts);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering if (u == (usec_t) -1) {
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering ts->realtime = ts->monotonic = (usec_t) -1;
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering return ts;
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering }
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering ts->realtime = u;
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (u == 0)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->monotonic = 0;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->monotonic = now(CLOCK_MONOTONIC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if ((int64_t) ts->monotonic > delta)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->monotonic -= delta;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->monotonic = 0;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering }
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return ts;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringdual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering int64_t delta;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(ts);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (u == (usec_t) -1) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->realtime = ts->monotonic = (usec_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return ts;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering }
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->monotonic = u;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->realtime = now(CLOCK_REALTIME);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if ((int64_t) ts->realtime > delta)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->realtime -= delta;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->realtime = 0;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return ts;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringusec_t timespec_load(const struct timespec *ts) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(ts);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (ts->tv_sec == (time_t) -1 &&
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->tv_nsec == (long) -1)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return (usec_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return (usec_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (usec_t) ts->tv_sec * USEC_PER_SEC +
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (usec_t) ts->tv_nsec / NSEC_PER_USEC;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringstruct timespec *timespec_store(struct timespec *ts, usec_t u) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(ts);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (u == (usec_t) -1) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->tv_sec = (time_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->tv_nsec = (long) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return ts;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering }
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->tv_sec = (time_t) (u / USEC_PER_SEC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return ts;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringusec_t timeval_load(const struct timeval *tv) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(tv);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (tv->tv_sec == (time_t) -1 &&
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering tv->tv_usec == (suseconds_t) -1)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return (usec_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return (usec_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (usec_t) tv->tv_sec * USEC_PER_SEC +
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (usec_t) tv->tv_usec;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringstruct timeval *timeval_store(struct timeval *tv, usec_t u) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(tv);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (u == (usec_t) -1) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering tv->tv_sec = (time_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering tv->tv_usec = (suseconds_t) -1;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering } else {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering tv->tv_sec = (time_t) (u / USEC_PER_SEC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering }
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return tv;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringchar *format_timestamp(char *buf, size_t l, usec_t t) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering struct tm tm;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering time_t sec;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(buf);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(l > 0);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (t <= 0 || t == (usec_t) -1)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return NULL;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering sec = (time_t) (t / USEC_PER_SEC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return NULL;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return buf;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringchar *format_timestamp_us(char *buf, size_t l, usec_t t) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering struct tm tm;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering time_t sec;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(buf);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering assert(l > 0);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (t <= 0 || t == (usec_t) -1)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return NULL;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering sec = (time_t) (t / USEC_PER_SEC);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering localtime_r(&sec, &tm);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return NULL;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return NULL;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return buf;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering}
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poetteringchar *format_timestamp_relative(char *buf, size_t l, usec_t t) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering const char *s;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering usec_t n, d;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering n = now(CLOCK_REALTIME);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (t <= 0 || (t == (usec_t) -1))
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering return NULL;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (n > t) {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering d = n - t;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering s = "ago";
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering } else {
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering d = t - n;
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering s = "left";
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering }
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering if (d >= USEC_PER_YEAR)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering d / USEC_PER_YEAR,
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else if (d >= USEC_PER_MONTH)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering d / USEC_PER_MONTH,
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else if (d >= USEC_PER_WEEK)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering d / USEC_PER_WEEK,
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else if (d >= 2*USEC_PER_DAY)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else if (d >= 25*USEC_PER_HOUR)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf, l, "1 day " USEC_FMT "h %s",
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering else if (d >= 6*USEC_PER_HOUR)
d0a7c5f69207b6719bab94893035fc8f5f6f87cbLennart Poettering snprintf(buf, l, USEC_FMT "h %s",
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering d / USEC_PER_HOUR, s);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering else if (d >= USEC_PER_HOUR)
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering d / USEC_PER_HOUR,
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering else if (d >= 5*USEC_PER_MINUTE)
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering snprintf(buf, l, USEC_FMT "min %s",
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering d / USEC_PER_MINUTE, s);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering else if (d >= USEC_PER_MINUTE)
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering d / USEC_PER_MINUTE,
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering else if (d >= USEC_PER_SEC)
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering snprintf(buf, l, USEC_FMT "s %s",
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering d / USEC_PER_SEC, s);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering else if (d >= USEC_PER_MSEC)
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering snprintf(buf, l, USEC_FMT "ms %s",
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering d / USEC_PER_MSEC, s);
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering else if (d > 0)
78f22b973fa2c9b09bd974680836df17163d9ee0Lennart Poettering snprintf(buf, l, USEC_FMT"us %s",
d, s);
else
snprintf(buf, l, "now");
buf[l-1] = 0;
return buf;
}
char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
static const struct {
const char *suffix;
usec_t usec;
} table[] = {
{ "y", USEC_PER_YEAR },
{ "month", USEC_PER_MONTH },
{ "w", USEC_PER_WEEK },
{ "d", USEC_PER_DAY },
{ "h", USEC_PER_HOUR },
{ "min", USEC_PER_MINUTE },
{ "s", USEC_PER_SEC },
{ "ms", USEC_PER_MSEC },
{ "us", 1 },
};
unsigned i;
char *p = buf;
bool something = false;
assert(buf);
assert(l > 0);
if (t == (usec_t) -1)
return NULL;
if (t <= 0) {
snprintf(p, l, "0");
p[l-1] = 0;
return p;
}
/* The result of this function can be parsed with parse_sec */
for (i = 0; i < ELEMENTSOF(table); i++) {
int k = 0;
size_t n;
bool done = false;
usec_t a, b;
if (t <= 0)
break;
if (t < accuracy && something)
break;
if (t < table[i].usec)
continue;
if (l <= 1)
break;
a = t / table[i].usec;
b = t % table[i].usec;
/* Let's see if we should shows this in dot notation */
if (t < USEC_PER_MINUTE && b > 0) {
usec_t cc;
int j;
j = 0;
for (cc = table[i].usec; cc > 1; cc /= 10)
j++;
for (cc = accuracy; cc > 1; cc /= 10) {
b /= 10;
j--;
}
if (j > 0) {
k = snprintf(p, l,
"%s"USEC_FMT".%0*llu%s",
p > buf ? " " : "",
a,
j,
(unsigned long long) b,
table[i].suffix);
t = 0;
done = true;
}
}
/* No? Then let's show it normally */
if (!done) {
k = snprintf(p, l,
"%s"USEC_FMT"%s",
p > buf ? " " : "",
a,
table[i].suffix);
t = b;
}
n = MIN((size_t) k, l);
l -= n;
p += n;
something = true;
}
*p = 0;
return buf;
}
void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
assert(f);
assert(name);
assert(t);
if (!dual_timestamp_is_set(t))
return;
fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
name,
t->realtime,
t->monotonic);
}
void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
unsigned long long a, b;
assert(value);
assert(t);
if (sscanf(value, "%llu %llu", &a, &b) != 2)
log_debug("Failed to parse finish timestamp value %s", value);
else {
t->realtime = a;
t->monotonic = b;
}
}
int parse_timestamp(const char *t, usec_t *usec) {
static const struct {
const char *name;
const int nr;
} day_nr[] = {
{ "Sunday", 0 },
{ "Sun", 0 },
{ "Monday", 1 },
{ "Mon", 1 },
{ "Tuesday", 2 },
{ "Tue", 2 },
{ "Wednesday", 3 },
{ "Wed", 3 },
{ "Thursday", 4 },
{ "Thu", 4 },
{ "Friday", 5 },
{ "Fri", 5 },
{ "Saturday", 6 },
{ "Sat", 6 },
};
const char *k;
struct tm tm, copy;
time_t x;
usec_t plus = 0, minus = 0, ret;
int r, weekday = -1;
unsigned i;
/*
* Allowed syntaxes:
*
* 2012-09-22 16:34:22
* 2012-09-22 16:34 (seconds will be set to 0)
* 2012-09-22 (time will be set to 00:00:00)
* 16:34:22 (date will be set to today)
* 16:34 (date will be set to today, seconds to 0)
* now
* yesterday (time is set to 00:00:00)
* today (time is set to 00:00:00)
* tomorrow (time is set to 00:00:00)
* +5min
* -5days
* @2147483647 (seconds since epoch)
*
*/
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;
} else if (streq(t, "yesterday")) {
tm.tm_mday --;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish;
} else if (streq(t, "tomorrow")) {
tm.tm_mday ++;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish;
} else if (t[0] == '+') {
r = parse_sec(t+1, &plus);
if (r < 0)
return r;
goto finish;
} else if (t[0] == '-') {
r = parse_sec(t+1, &minus);
if (r < 0)
return r;
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;
r = parse_sec(z, &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;
r = parse_sec(z, &plus);
if (r < 0)
return r;
goto finish;
}
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
if (!startswith_no_case(t, day_nr[i].name))
continue;
skip = strlen(day_nr[i].name);
if (t[skip] != ' ')
continue;
weekday = day_nr[i].nr;
t += skip + 1;
break;
}
copy = tm;
k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
if (k && *k == 0)
goto finish;
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
if (k && *k == 0)
goto finish;
tm = copy;
k = strptime(t, "%y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
goto finish;
}
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
goto finish;
}
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;
}
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;
}
tm = copy;
k = strptime(t, "%H:%M:%S", &tm);
if (k && *k == 0)
goto finish;
tm = copy;
k = strptime(t, "%H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
goto finish;
}
return -EINVAL;
finish:
x = mktime(&tm);
if (x == (time_t) -1)
return -EINVAL;
if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL;
ret = (usec_t) x * USEC_PER_SEC;
ret += plus;
if (ret > minus)
ret -= minus;
else
ret = 0;
*usec = ret;
return 0;
}
int parse_sec(const char *t, usec_t *usec) {
static const struct {
const char *suffix;
usec_t usec;
} table[] = {
{ "seconds", USEC_PER_SEC },
{ "second", USEC_PER_SEC },
{ "sec", USEC_PER_SEC },
{ "s", USEC_PER_SEC },
{ "minutes", USEC_PER_MINUTE },
{ "minute", USEC_PER_MINUTE },
{ "min", USEC_PER_MINUTE },
{ "months", USEC_PER_MONTH },
{ "month", USEC_PER_MONTH },
{ "msec", USEC_PER_MSEC },
{ "ms", USEC_PER_MSEC },
{ "m", USEC_PER_MINUTE },
{ "hours", USEC_PER_HOUR },
{ "hour", USEC_PER_HOUR },
{ "hr", USEC_PER_HOUR },
{ "h", USEC_PER_HOUR },
{ "days", USEC_PER_DAY },
{ "day", USEC_PER_DAY },
{ "d", USEC_PER_DAY },
{ "weeks", USEC_PER_WEEK },
{ "week", USEC_PER_WEEK },
{ "w", USEC_PER_WEEK },
{ "years", USEC_PER_YEAR },
{ "year", USEC_PER_YEAR },
{ "y", USEC_PER_YEAR },
{ "usec", 1ULL },
{ "us", 1ULL },
{ "", USEC_PER_SEC }, /* default is sec */
};
const char *p;
usec_t r = 0;
bool something = false;
assert(t);
assert(usec);
p = t;
for (;;) {
long long l, z = 0;
char *e;
unsigned i, n = 0;
p += strspn(p, WHITESPACE);
if (*p == 0) {
if (!something)
return -EINVAL;
break;
}
errno = 0;
l = strtoll(p, &e, 10);
if (errno > 0)
return -errno;
if (l < 0)
return -ERANGE;
if (*e == '.') {
char *b = e + 1;
errno = 0;
z = strtoll(b, &e, 10);
if (errno > 0)
return -errno;
if (z < 0)
return -ERANGE;
if (e == b)
return -EINVAL;
n = e - b;
} else if (e == p)
return -EINVAL;
e += strspn(e, WHITESPACE);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
usec_t k = (usec_t) z * table[i].usec;
for (; n > 0; n--)
k /= 10;
r += (usec_t) l * table[i].usec + k;
p = e + strlen(table[i].suffix);
something = true;
break;
}
if (i >= ELEMENTSOF(table))
return -EINVAL;
}
*usec = r;
return 0;
}
int parse_nsec(const char *t, nsec_t *nsec) {
static const struct {
const char *suffix;
nsec_t nsec;
} table[] = {
{ "seconds", NSEC_PER_SEC },
{ "second", NSEC_PER_SEC },
{ "sec", NSEC_PER_SEC },
{ "s", NSEC_PER_SEC },
{ "minutes", NSEC_PER_MINUTE },
{ "minute", NSEC_PER_MINUTE },
{ "min", NSEC_PER_MINUTE },
{ "months", NSEC_PER_MONTH },
{ "month", NSEC_PER_MONTH },
{ "msec", NSEC_PER_MSEC },
{ "ms", NSEC_PER_MSEC },
{ "m", NSEC_PER_MINUTE },
{ "hours", NSEC_PER_HOUR },
{ "hour", NSEC_PER_HOUR },
{ "hr", NSEC_PER_HOUR },
{ "h", NSEC_PER_HOUR },
{ "days", NSEC_PER_DAY },
{ "day", NSEC_PER_DAY },
{ "d", NSEC_PER_DAY },
{ "weeks", NSEC_PER_WEEK },
{ "week", NSEC_PER_WEEK },
{ "w", NSEC_PER_WEEK },
{ "years", NSEC_PER_YEAR },
{ "year", NSEC_PER_YEAR },
{ "y", NSEC_PER_YEAR },
{ "usec", NSEC_PER_USEC },
{ "us", NSEC_PER_USEC },
{ "nsec", 1ULL },
{ "ns", 1ULL },
{ "", 1ULL }, /* default is nsec */
};
const char *p;
nsec_t r = 0;
bool something = false;
assert(t);
assert(nsec);
p = t;
for (;;) {
long long l, z = 0;
char *e;
unsigned i, n = 0;
p += strspn(p, WHITESPACE);
if (*p == 0) {
if (!something)
return -EINVAL;
break;
}
errno = 0;
l = strtoll(p, &e, 10);
if (errno > 0)
return -errno;
if (l < 0)
return -ERANGE;
if (*e == '.') {
char *b = e + 1;
errno = 0;
z = strtoll(b, &e, 10);
if (errno > 0)
return -errno;
if (z < 0)
return -ERANGE;
if (e == b)
return -EINVAL;
n = e - b;
} else if (e == p)
return -EINVAL;
e += strspn(e, WHITESPACE);
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
nsec_t k = (nsec_t) z * table[i].nsec;
for (; n > 0; n--)
k /= 10;
r += (nsec_t) l * table[i].nsec + k;
p = e + strlen(table[i].suffix);
something = true;
break;
}
if (i >= ELEMENTSOF(table))
return -EINVAL;
}
*nsec = r;
return 0;
}
bool ntp_synced(void) {
struct timex txc = {};
if (adjtimex(&txc) < 0)
return false;
if (txc.status & STA_UNSYNC)
return false;
return true;
}
int get_timezones(char ***ret) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **zones = NULL;
size_t n_zones = 0, n_allocated = 0;
assert(ret);
zones = strv_new("UTC", NULL);
if (!zones)
return -ENOMEM;
n_allocated = 2;
n_zones = 1;
f = fopen("/usr/share/zoneinfo/zone.tab", "re");
if (f) {
char l[LINE_MAX];
FOREACH_LINE(l, f, return -errno) {
char *p, *w;
size_t k;
p = strstrip(l);
if (isempty(p) || *p == '#')
continue;
/* Skip over country code */
p += strcspn(p, WHITESPACE);
p += strspn(p, WHITESPACE);
/* Skip over coordinates */
p += strcspn(p, WHITESPACE);
p += strspn(p, WHITESPACE);
/* Found timezone name */
k = strcspn(p, WHITESPACE);
if (k <= 0)
continue;
w = strndup(p, k);
if (!w)
return -ENOMEM;
if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
free(w);
return -ENOMEM;
}
zones[n_zones++] = w;
zones[n_zones] = NULL;
}
strv_sort(zones);
} else if (errno != ENOENT)
return -errno;
*ret = zones;
zones = NULL;
return 0;
}
bool timezone_is_valid(const char *name) {
bool slash = false;
const char *p, *t;
struct stat st;
if (!name || *name == 0 || *name == '/')
return false;
for (p = name; *p; p++) {
if (!(*p >= '0' && *p <= '9') &&
!(*p >= 'a' && *p <= 'z') &&
!(*p >= 'A' && *p <= 'Z') &&
!(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
return false;
if (*p == '/') {
if (slash)
return false;
slash = true;
} else
slash = false;
}
if (slash)
return false;
t = strappenda("/usr/share/zoneinfo/", name);
if (stat(t, &st) < 0)
return false;
if (!S_ISREG(st.st_mode))
return false;
return true;
}