assert(spec);
assert(next);
+ if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
+
usec++;
t = (time_t) (usec / USEC_PER_SEC);
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
if (t <= 0 || t == USEC_INFINITY)
return NULL; /* Timestamp is unset */
+ /* Let's not format times with years > 9999 */
+ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return NULL;
+
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
- if ((usec_t) sec != (t / USEC_PER_SEC))
- return NULL; /* overflow? */
if (!localtime_or_gmtime_r(&sec, &tm, utc))
return NULL;
return -EINVAL;
ret = (usec_t) x * USEC_PER_SEC + x_usec;
+ if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
finish:
ret += plus;
+ if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
+
if (ret > minus)
ret -= minus;
else
return timestamp - delta;
}
+
+#if SIZEOF_TIME_T == 8
+/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
+ * territory. However, since we want to stay away from this in all timezones we take one day off. */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
+#elif SIZEOF_TIME_T == 4
+/* With a 32bit time_t we can't go beyond 2038... */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
+#else
+#error "Yuck, time_t is neither 4 not 8 bytes wide?"
+#endif
if (r < 0)
return log_error_errno(r, "Failed to get realtime timestamp: %m");
+ if (x > USEC_TIMESTAMP_FORMATTABLE_MAX) {
+ log_error("Timestamp cannot be printed");
+ return -EINVAL;
+ }
+
if (mode == OUTPUT_SHORT_FULL) {
const char *k;
test_one_noutc("@1395716396");
test_should_parse("today UTC");
test_should_fail("today UTC UTC");
+ test_should_parse("1970-1-1 UTC");
+ test_should_fail("1969-1-1 UTC");
+#if SIZEOF_TIME_T == 8
+ test_should_parse("9999-12-30 23:59:59 UTC");
+ test_should_fail("9999-12-31 00:00:00 UTC");
+ test_should_fail("10000-01-01 00:00:00 UTC");
+#elif SIZEOF_TIME_T == 4
+ test_should_parse("2038-01-19 03:14:07 UTC");
+ test_should_fail( "2038-01-19 03:14:08 UTC");
+#endif
return 0;
}
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "random-util.h"
+#include "string-util.h"
#include "strv.h"
#include "time-util.h"
-#include "random-util.h"
static void test_parse_sec(void) {
usec_t u;
}
}
+static void test_format_timestamp_utc_one(usec_t t, const char *result) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+
+ assert_se(!format_timestamp_utc(buf, sizeof(buf), t) == !result);
+
+ if (result)
+ assert_se(streq(result, buf));
+}
+
+static void test_format_timestamp_utc(void) {
+ test_format_timestamp_utc_one(0, NULL);
+ test_format_timestamp_utc_one(1, "Thu 1970-01-01 00:00:00 UTC");
+ test_format_timestamp_utc_one(USEC_PER_SEC, "Thu 1970-01-01 00:00:01 UTC");
+
+#if SIZEOF_TIME_T == 8
+ test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Thu 9999-12-30 23:59:59 UTC");
+#elif SIZEOF_TIME_T == 4
+ test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Tue 2038-01-19 03:14:07 UTC");
+#endif
+
+ test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX+1, NULL);
+ test_format_timestamp_utc_one(USEC_INFINITY, NULL);
+}
+
int main(int argc, char *argv[]) {
uintmax_t x;
test_usec_add();
test_usec_sub();
test_format_timestamp();
+ test_format_timestamp_utc();
/* Ensure time_t is signed */
assert_cc((time_t) -1 < (time_t) 1);