diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c3332c9b2c..cb635cd5e0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1984,6 +1984,7 @@ virTimeFieldsNow; virTimeFieldsNowRaw; virTimeFieldsThen; virTimeFieldsThenRaw; +virTimeLocalOffsetFromUTC; virTimeMillisNow; virTimeMillisNowRaw; virTimeStringNow; diff --git a/src/util/virtime.c b/src/util/virtime.c index caa4e241f6..3a56400fe2 100644 --- a/src/util/virtime.c +++ b/src/util/virtime.c @@ -1,7 +1,7 @@ /* * virtime.c: Time handling functions * - * Copyright (C) 2006-2012 Red Hat, Inc. + * Copyright (C) 2006-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -344,3 +344,46 @@ char *virTimeStringThen(unsigned long long when) return ret; } + +/** + * virTimeLocalOffsetFromUTC: + * + * This function is threadsafe, but is *not* async signal safe (due to + * localtime_r()). + * + * @offset: pointer to time_t that will be set to the difference + * between localtime and UTC in seconds (east of UTC is a + * positive number, and west of UTC is a negative number. + * + * Returns 0 on success, -1 on error with error reported + */ +int +virTimeLocalOffsetFromUTC(long *offset) +{ + struct tm gmtimeinfo; + time_t current, utc; + + /* time() gives seconds since Epoch in current timezone */ + if ((current = time(NULL)) == (time_t)-1) { + virReportSystemError(errno, "%s", + _("failed to get current system time")); + return -1; + } + + /* treat current as if it were in UTC */ + if (!gmtime_r(¤t, &gmtimeinfo)) { + virReportSystemError(errno, "%s", + _("gmtime_r failed")); + return -1; + } + + /* mktime() also obeys current timezone rules */ + if ((utc = mktime(&gmtimeinfo)) == (time_t)-1) { + virReportSystemError(errno, "%s", + _("mktime failed")); + return -1; + } + + *offset = current - utc; + return 0; +} diff --git a/src/util/virtime.h b/src/util/virtime.h index a5bd652602..25332dba93 100644 --- a/src/util/virtime.h +++ b/src/util/virtime.h @@ -1,7 +1,7 @@ /* * virtime.h: Time handling functions * - * Copyright (C) 2006-2011 Red Hat, Inc. + * Copyright (C) 2006-2011, 2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,7 +50,6 @@ int virTimeStringNowRaw(char *buf) int virTimeStringThenRaw(unsigned long long when, char *buf) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; - /* These APIs are *not* async signal safe and return -1, * raising a libvirt error on failure */ @@ -63,5 +62,7 @@ int virTimeFieldsThen(unsigned long long when, struct tm *fields) char *virTimeStringNow(void); char *virTimeStringThen(unsigned long long when); +int virTimeLocalOffsetFromUTC(long *offset) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; #endif diff --git a/tests/virtimetest.c b/tests/virtimetest.c index 73a3c3ed5c..bf27682533 100644 --- a/tests/virtimetest.c +++ b/tests/virtimetest.c @@ -72,6 +72,35 @@ static int testTimeFields(const void *args) } +typedef struct { + const char *zone; + long offset; +} testTimeLocalOffsetData; + +static int +testTimeLocalOffset(const void *args) +{ + const testTimeLocalOffsetData *data = args; + long actual; + + if (setenv("TZ", data->zone, 1) < 0) { + perror("setenv"); + return -1; + } + tzset(); + + if (virTimeLocalOffsetFromUTC(&actual) < 0) { + return -1; + } + + if (data->offset != actual) { + VIR_DEBUG("Expect Offset %ld got %ld\n", + data->offset, actual); + return -1; + } + return 0; +} + static int mymain(void) { @@ -119,6 +148,35 @@ mymain(void) TEST_FIELDS(2147483648000ull, 2038, 1, 19, 3, 14, 8); +#define TEST_LOCALOFFSET(tz, off) \ + do { \ + testTimeLocalOffsetData data = { \ + .zone = tz, \ + .offset = off, \ + }; \ + if (virtTestRun("Test localtime offset for " #tz, \ + testTimeLocalOffset, &data) < 0) \ + ret = -1; \ + } while (0) + + TEST_LOCALOFFSET("VIR00:30", -30 * 60); + TEST_LOCALOFFSET("VIR01:30", -90 * 60); + TEST_LOCALOFFSET("UTC", 0); + TEST_LOCALOFFSET("VIR-00:30", 30 * 60); + TEST_LOCALOFFSET("VIR-01:30", 90 * 60); +#if __TEST_DST + /* test DST processing with timezones that always + * have DST in effect; what's more, cover a zone with + * with an unusual DST different than a usual one hour + */ + /* NB: These tests fail at certain times of the day, so + * must be disabled until we figure out why + */ + TEST_LOCALOFFSET("VIR-00:30VID,0,365", 90 * 60); + TEST_LOCALOFFSET("VIR-02:30VID,0,365", 210 * 60); + TEST_LOCALOFFSET("VIR-02:30VID-04:30,0,365", 270 * 60); +#endif + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }