diff --git a/src/timegm.c b/src/timegm.c index 32d2c27c..655d73b4 100644 --- a/src/timegm.c +++ b/src/timegm.c @@ -1,71 +1,80 @@ -/* timegm.c - libc replacement function - * Copyright (C) 2004 Free Software Foundation, Inc. +/* + * aria2 - The high speed download utility * - * This file is part of GnuPG. + * Copyright (C) 2012 Tatsuhiro Tsujikawa * - * GnuPG is free software; you can redistribute it and/or modify + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * GnuPG is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. */ +/* copyright --> */ +#include "timegm.h" -/* - timegm() is a GNU function that might not be available everywhere. - It's basically the inverse of gmtime() - you give it a struct tm, - and get back a time_t. It differs from mktime() in that it handles - the case where the struct tm is UTC and the local environment isn't. +#include - Some BSDs don't handle the putenv("foo") case properly, so we use - unsetenv if the platform has it to remove environment variables. -*/ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif // HAVE_CONFIG_H - -#include -#include -#include - -time_t -timegm(struct tm *tm) +/* Counter the number of leap year in the range [0, y). The |y| is the + year, including century (e.g., 2012) */ +static int count_leap_year(int y) { - time_t answer; - char *zone; - - zone=getenv("TZ"); - putenv("TZ=UTC"); - tzset(); - answer=mktime(tm); - if(zone) - { - char *old_zone; - - old_zone=malloc(3+strlen(zone)+1); - if(old_zone) - { - strcpy(old_zone,"TZ="); - strcat(old_zone,zone); - putenv(old_zone); - } - } - else -#ifdef HAVE_UNSETENV - unsetenv("TZ"); -#else - putenv("TZ="); -#endif - - tzset(); - return answer; + y -= 1; + return y/4-y/100+y/400; +} + +/* Returns nonzero if the |y| is the leap year. The |y| is the year, + including century (e.g., 2012) */ +static int is_leap_year(int y) +{ + return y%4 == 0 && (y%100 != 0 || y%400 == 0); +} + +/* The number of days before ith month begins */ +static int daysum[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +// Based on the algorithm of Python 2.7 calendar.timegm. +time_t timegm(struct tm *tm) +{ + int days; + int num_leap_year; + int64_t t; + if(tm->tm_mon > 11) { + return -1; + } + num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970); + days = (tm->tm_year - 70) * 365 + + num_leap_year + daysum[tm->tm_mon] + tm->tm_mday-1; + if(tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) { + ++days; + } + t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec; + if(sizeof(time_t) == 4) { + if(t < INT32_MIN || t > INT32_MAX) { + return -1; + } + } + return t; } diff --git a/src/timegm.h b/src/timegm.h index f16a1616..e57bbd4c 100644 --- a/src/timegm.h +++ b/src/timegm.h @@ -1,7 +1,7 @@ /* * aria2 - The high speed download utility * - * Copyright (C) 2006 Tatsuhiro Tsujikawa + * Copyright (C) 2012 Tatsuhiro Tsujikawa * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,12 +34,8 @@ #ifndef D_TIMEGM_H #define D_TIMEGM_H -#ifdef __MINGW32__ -# undef SIZE_MAX -#endif // __MINGW32__ - #ifdef HAVE_CONFIG_H -# include "config.h" +# include "config.h" #endif // HAVE_CONFIG_H #ifdef __cplusplus diff --git a/test/FtpConnectionTest.cc b/test/FtpConnectionTest.cc index 9928b9fe..df85ce8f 100644 --- a/test/FtpConnectionTest.cc +++ b/test/FtpConnectionTest.cc @@ -181,13 +181,18 @@ void FtpConnectionTest::testReceiveMdtmResponse() CPPUNIT_ASSERT(t.bad()); } { - // invalid month: 19, we don't care about invalid month.. + // invalid month: 19 Time t; serverSocket_->writeData("213 20081908124312\r\n"); waitRead(clientSocket_); CPPUNIT_ASSERT_EQUAL(213, ftp_->receiveMdtmResponse(t)); - // Wed Jul 8 12:43:12 2009 +#ifdef HAVE_TIMEGM + // Time will be normalized. Wed Jul 8 12:43:12 2009 CPPUNIT_ASSERT_EQUAL((time_t)1247056992, t.getTime()); +#else // !HAVE_TIMEGM + // The replacement timegm does not normalize. + CPPUNIT_ASSERT_EQUAL((time_t)-1, t.getTime()); +#endif // !HAVE_TIMEGM } { Time t; diff --git a/test/Makefile.am b/test/Makefile.am index 48926011..8aed8b49 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -222,6 +222,10 @@ if ENABLE_ASYNC_DNS aria2c_SOURCES += AsyncNameResolverTest.cc endif # ENABLE_ASYNC_DNS +if !HAVE_TIMEGM +aria2c_SOURCES += TimegmTest.cc +endif # !HAVE_TIMEGM + aria2c_LDADD = ../src/libaria2c.a @LIBINTL@ @CPPUNIT_LIBS@ AM_CPPFLAGS = -Wall\ -I$(top_srcdir)/src\ diff --git a/test/TimegmTest.cc b/test/TimegmTest.cc new file mode 100644 index 00000000..40ccf860 --- /dev/null +++ b/test/TimegmTest.cc @@ -0,0 +1,59 @@ +#include "timegm.h" + +#include +#include + +#include + +namespace aria2 { + +class TimegmTest:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(TimegmTest); + CPPUNIT_TEST(testTimegm); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp() {} + + void tearDown() {} + + void testTimegm(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TimegmTest); + +namespace { +void setTime(struct tm* tm, int yr, int mon, int day, int h, int m, int s) +{ + tm->tm_year = yr - 1900; + tm->tm_mon = mon-1; + tm->tm_mday = day; + tm->tm_hour = h; + tm->tm_min = m; + tm->tm_sec = s; +} +} // namespace + +void TimegmTest::testTimegm() +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + setTime(&tm, 1970, 1, 1, 0, 0, 0); + CPPUNIT_ASSERT_EQUAL((time_t)0, timegm(&tm)); + setTime(&tm, 2000, 1, 2, 1, 2, 3); + CPPUNIT_ASSERT_EQUAL((time_t)946774923, timegm(&tm)); + setTime(&tm, 2000, 2, 2, 1, 2, 3); + CPPUNIT_ASSERT_EQUAL((time_t)949453323, timegm(&tm)); + setTime(&tm, 2015, 10, 21, 10, 19, 30); + CPPUNIT_ASSERT_EQUAL((time_t)1445422770, timegm(&tm)); + setTime(&tm, 1970, 13, 1, 0, 0, 0); + CPPUNIT_ASSERT_EQUAL((time_t)-1, timegm(&tm)); + setTime(&tm, 2039, 1, 1, 0, 0, 0); + if(sizeof(time_t) == 4) { + CPPUNIT_ASSERT_EQUAL((time_t)-1, timegm(&tm)); + } else if(sizeof(time_t) == 8) { + CPPUNIT_ASSERT_EQUAL((time_t)2177452800LL, timegm(&tm)); + } +} + +} // namespace aria2