From 6b133c55894129c596457688a606117895f271b7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Fri, 9 Apr 2010 10:37:54 +0000 Subject: [PATCH] Timezones handling changes: - optimization, tzset() and TZ called only on month changes - fixed returning TZ on platforms where setenv() has side effects - test program (handy for the platform sensitive code) --- org.glite.lb/configure | 2 +- org.glite.lbjp-common.db/Makefile | 16 ++- org.glite.lbjp-common.db/configure | 2 +- org.glite.lbjp-common.db/src/db.c | 61 ++++++----- org.glite.lbjp-common.db/test/timezone.cpp | 162 +++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+), 31 deletions(-) create mode 100644 org.glite.lbjp-common.db/test/timezone.cpp diff --git a/org.glite.lb/configure b/org.glite.lb/configure index 71ff6f0..1c41dd1 100755 --- a/org.glite.lb/configure +++ b/org.glite.lb/configure @@ -330,7 +330,7 @@ BEGIN{ 'lb.harvester' => [ qw// ], 'lb.yaim' => [ qw/yaim_core:R/ ], 'lb.glite-LB' => [ qw/fetchcrl:R gpt:R gip_release:R gip_service:R bdii:R glite_version:R glite_info_templates:R glue_schema:R/ ], - 'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B/ ], + 'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B cppunit:B/ ], 'lbjp-common.log' => [ qw/log4c/ ], 'lbjp-common.maildir' => [ qw// ], 'lbjp-common.server-bones' => [ qw// ], diff --git a/org.glite.lbjp-common.db/Makefile b/org.glite.lbjp-common.db/Makefile index aa39082..0639deb 100644 --- a/org.glite.lbjp-common.db/Makefile +++ b/org.glite.lbjp-common.db/Makefile @@ -21,7 +21,7 @@ version=${module.version} CC=gcc -VPATH=${top_srcdir}/interface:${top_srcdir}/src:${top_srcdir}/examples +VPATH=${top_srcdir}/interface:${top_srcdir}/src:${top_srcdir}/examples:${top_srcdir}/test DEBUG:=-g -O0 -W -Wall @@ -65,7 +65,9 @@ TEST_INC:=-I${cppunit_prefix}/include LDFLAGS:=-L${stagedir}/${libdir} ${COVERAGE_FLAGS} COMPILE:=libtool --mode=compile ${CC} ${CFLAGS} +COMPILEXX:=libtool --mode=compile ${CXX} ${CFLAGS} LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/${libdir} ${LDFLAGS} +LINKXX:=libtool --mode=link ${CXX} -rpath ${stagedir}/${libdir} ${LDFLAGS} INSTALL:=libtool --mode=install install EXT_LIBS:=-lglite_lbu_trio -lglite_lbu_log -lpthread ${DL_LIBS} @@ -124,10 +126,13 @@ db_test_psql: db_test_psql.lo libglite_lbu_dbtest.la db_expire: db_expire.lo libglite_lbu_dbtest.la ${LINK} -o $@ $+ ${EXT_LIBS} +timezone: timezone.lo libglite_lbu_db.la + ${LINKXX} -o $@ $+ ${TEST_LIBS} ${EXT_LIBS} + compile: libglite_lbu_db.la -check: - -echo No checks here yet. +check: timezone + ./timezone test_coverage: -mkdir coverage @@ -171,7 +176,7 @@ install: all clean: rm -rvf *.o *.lo *.loT .libs lib* *.c *.h *.dox C/ CPP/ rm -rvf log.xml project/ rpmbuild/ RPMS/ tgz/ - rm -rvf db_expire db_test db_test_mysql db_test_psql + rm -rvf db_expire db_test db_test_mysql db_test_psql timezone db-mysql.o db-mysql.lo: db-mysql.c ${COMPILE} ${MYSQL_CPPFLAGS} -c $< @@ -185,6 +190,9 @@ db_test_mysql.o db_test_mysql.lo: db_test.c db_test_psql.o db_test_psql.lo: db_test.c ${COMPILE} -DPSQL_BACKEND=1 -c $< -o $@ +timezone.o timezone.lo: timezone.cpp + ${COMPILEXX} -c $< -o $@ ${TEST_INC} + %.o %.lo: %.c ${COMPILE} -c $< diff --git a/org.glite.lbjp-common.db/configure b/org.glite.lbjp-common.db/configure index 71ff6f0..1c41dd1 100755 --- a/org.glite.lbjp-common.db/configure +++ b/org.glite.lbjp-common.db/configure @@ -330,7 +330,7 @@ BEGIN{ 'lb.harvester' => [ qw// ], 'lb.yaim' => [ qw/yaim_core:R/ ], 'lb.glite-LB' => [ qw/fetchcrl:R gpt:R gip_release:R gip_service:R bdii:R glite_version:R glite_info_templates:R glue_schema:R/ ], - 'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B/ ], + 'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B cppunit:B/ ], 'lbjp-common.log' => [ qw/log4c/ ], 'lbjp-common.maildir' => [ qw// ], 'lbjp-common.server-bones' => [ qw// ], diff --git a/org.glite.lbjp-common.db/src/db.c b/org.glite.lbjp-common.db/src/db.c index 69705dd..095a28b 100644 --- a/org.glite.lbjp-common.db/src/db.c +++ b/org.glite.lbjp-common.db/src/db.c @@ -132,41 +132,58 @@ void glite_lbu_TimestampToStr(double t, char **str) { } +static time_t tm2time(struct tm *tm) { + static struct tm tm_last = { tm_year:0, tm_mon:0 }; + static time_t t = (time_t)-1; + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + char *tz; + + pthread_mutex_lock(&lock); + if (tm->tm_year == tm_last.tm_year && tm->tm_mon == tm_last.tm_mon) { + t = t + (tm->tm_sec - tm_last.tm_sec) + + (tm->tm_min - tm_last.tm_min) * 60 + + (tm->tm_hour - tm_last.tm_hour) * 3600 + + (tm->tm_mday - tm_last.tm_mday) * 86400; + memcpy(&tm_last, tm, sizeof tm_last); + } else { + tz = getenv("TZ"); + if (tz) tz = strdup(tz); + setenv("TZ", "UTC", 1); + tzset(); + + t = mktime(tm); + memcpy(&tm_last, tm, sizeof tm_last); + + if (tz) setenv("TZ", tz, 1); + else unsetenv("TZ"); + free(tz); + tzset(); + } + pthread_mutex_unlock(&lock); + + return t; +} + + time_t glite_lbu_StrToTime(const char *str) { struct tm tm; - char *tz; - time_t t; memset(&tm,0,sizeof(tm)); - tz = getenv("TZ"); - setenv("TZ", "UTC", 1); - tzset(); - sscanf(str,"%4d-%02d-%02d %02d:%02d:%02d", &tm.tm_year,&tm.tm_mon,&tm.tm_mday, &tm.tm_hour,&tm.tm_min,&tm.tm_sec); tm.tm_year -= 1900; tm.tm_mon--; - t = mktime(&tm); - - if (tz) setenv("TZ", tz, 1); - else unsetenv("TZ"); - tzset(); - return t; + return tm2time(&tm); } double glite_lbu_StrToTimestamp(const char *str) { struct tm tm; - double sec, t; - char *tz; + double sec; memset(&tm,0,sizeof(tm)); - tz = getenv("TZ"); - setenv("TZ", "UTC", 1); - tzset(); - sscanf(str,"%4d-%02d-%02d %02d:%02d:%lf", &tm.tm_year,&tm.tm_mon,&tm.tm_mday, &tm.tm_hour,&tm.tm_min,&sec); @@ -174,13 +191,7 @@ double glite_lbu_StrToTimestamp(const char *str) { tm.tm_mon--; tm.tm_sec = sec; - t = (sec - tm.tm_sec) + mktime(&tm); - - if (tz) setenv("TZ", tz, 1); - else unsetenv("TZ"); - tzset(); - - return t; + return (sec - tm.tm_sec) + tm2time(&tm); } diff --git a/org.glite.lbjp-common.db/test/timezone.cpp b/org.glite.lbjp-common.db/test/timezone.cpp new file mode 100644 index 0000000..746bec5 --- /dev/null +++ b/org.glite.lbjp-common.db/test/timezone.cpp @@ -0,0 +1,162 @@ +/* +Copyright (c) Members of the EGEE Collaboration. 2004-2010. +See http://www.eu-egee.org/partners for details on the copyright holders. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "db.h" + +#define zoneCheck(tmp) {\ + (tmp) = getenv("TZ"); \ + if (tz == NULL) CPPUNIT_ASSERT((tmp) == NULL);\ + else CPPUNIT_ASSERT((tmp) != NULL && strcmp(tz, (tmp)) == 0);\ +} + +static struct { + time_t t; + const char *db; +} data[] = { + // year of tiger (and day +, day -) + {t:1266142830, db:"2010-02-14 10:20:30"}, + {t:1266142830-24*3600, db:"2010-02-13 10:20:30"}, + {t:1266142830+24*3600, db:"2010-02-15 10:20:30"}, + + // two months later (and day +, day -) + {t:1271240430, db:"2010-04-14 10:20:30"}, + {t:1271240430-24*3600, db:"2010-04-13 10:20:30"}, + {t:1271240430+24*3600, db:"2010-04-15 10:20:30"}, +}; + +class ZoneTest: public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(ZoneTest); + CPPUNIT_TEST(testInitialZoneCheck); + CPPUNIT_TEST(testTimeZone3); + CPPUNIT_TEST(testTimeZoneNULL); + CPPUNIT_TEST(testTimeZoneUTC); + CPPUNIT_TEST_SUITE_END(); + +public: + + void setUp() { + tz0 = getenv("TZ"); + } + + void tearDown() { + if (tz0) setenv("TZ", tz0, 1); + else unsetenv("TZ"); + } + + void testInitialZoneCheck() { + char *tz2; + + zoneCheck(tz2); + } + + void testTimeZone3() { + tz = "Tst 3:00"; + setenv("TZ", tz, 1); + tzset(); + testInitialZoneCheck(); + convertCheck(); + } + + void testTimeZoneNULL() { + tz = NULL; + unsetenv("TZ"); + tzset(); + testInitialZoneCheck(); + convertCheck(); + } + + void testTimeZoneUTC() { + tz = "UTC"; + setenv("TZ", tz, 1); + tzset(); + testInitialZoneCheck(); + convertCheck(); + } + +private: + +const char *tz0, *tz; + +void convertCheck() { + char *tz2; + + time_t tt; + double st_d; + char *st_str, *st_dbstr, *t_dbstr; + + char *s; + time_t t; + double d; + size_t i; + + for (i = 0; i < sizeof(data)/sizeof(data[0]); i++) { + tt = data[i].t; + st_d = tt + 0.013; + asprintf(&t_dbstr, "'%s'", data[i].db); + asprintf(&st_str, "%s.013", data[i].db); + asprintf(&st_dbstr, "'%s.013'", data[i].db); + + glite_lbu_TimeToStr(tt, &s); + CPPUNIT_ASSERT(s != NULL); + CPPUNIT_ASSERT(strcmp(s, t_dbstr) == 0); + free(s); + zoneCheck(tz2); + + glite_lbu_TimestampToStr(st_d, &s); + CPPUNIT_ASSERT(s != NULL); + CPPUNIT_ASSERT(strncmp(s, st_dbstr, 24) == 0); + free(s); + zoneCheck(tz2); + + t = glite_lbu_StrToTime(data[i].db); + CPPUNIT_ASSERT(t == tt); + zoneCheck(tz2); + + d = glite_lbu_StrToTimestamp(st_str); + CPPUNIT_ASSERT(round(1000 * d) == round(1000 * st_d)); + zoneCheck(tz2); + + free(st_str); + free(st_dbstr); + free(t_dbstr); + } +} + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( ZoneTest ); + +int +main () +{ + CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); + CppUnit::TextUi::TestRunner runner; + + runner.addTest(suite); + return runner.run() ? 0 : 1; +} -- 1.8.2.3