Rewritten timegm replacement function

The algorithm is based on Python 2.7 calendar.timegm.
pull/24/head
Tatsuhiro Tsujikawa 2012-07-06 00:34:37 +09:00
parent 4046f27ea9
commit f04090199f
5 changed files with 135 additions and 62 deletions

View File

@ -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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (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 * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* 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"
/* #include <stdint.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.
Some BSDs don't handle the putenv("foo") case properly, so we use /* Counter the number of leap year in the range [0, y). The |y| is the
unsetenv if the platform has it to remove environment variables. year, including century (e.g., 2012) */
*/ static int count_leap_year(int y)
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif // HAVE_CONFIG_H
#include <time.h>
#include <stdlib.h>
#include <string.h>
time_t
timegm(struct tm *tm)
{ {
time_t answer; y -= 1;
char *zone; return y/4-y/100+y/400;
}
zone=getenv("TZ");
putenv("TZ=UTC"); /* Returns nonzero if the |y| is the leap year. The |y| is the year,
tzset(); including century (e.g., 2012) */
answer=mktime(tm); static int is_leap_year(int y)
if(zone) {
{ return y%4 == 0 && (y%100 != 0 || y%400 == 0);
char *old_zone; }
old_zone=malloc(3+strlen(zone)+1); /* The number of days before ith month begins */
if(old_zone) static int daysum[] = {
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
strcpy(old_zone,"TZ="); };
strcat(old_zone,zone);
putenv(old_zone); // Based on the algorithm of Python 2.7 calendar.timegm.
} time_t timegm(struct tm *tm)
} {
else int days;
#ifdef HAVE_UNSETENV int num_leap_year;
unsetenv("TZ"); int64_t t;
#else if(tm->tm_mon > 11) {
putenv("TZ="); return -1;
#endif }
num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970);
tzset(); days = (tm->tm_year - 70) * 365 +
return answer; 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;
} }

View File

@ -1,7 +1,7 @@
/* /*
* aria2 - The high speed download utility * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -34,10 +34,6 @@
#ifndef D_TIMEGM_H #ifndef D_TIMEGM_H
#define D_TIMEGM_H #define D_TIMEGM_H
#ifdef __MINGW32__
# undef SIZE_MAX
#endif // __MINGW32__
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif // HAVE_CONFIG_H #endif // HAVE_CONFIG_H

View File

@ -181,13 +181,18 @@ void FtpConnectionTest::testReceiveMdtmResponse()
CPPUNIT_ASSERT(t.bad()); CPPUNIT_ASSERT(t.bad());
} }
{ {
// invalid month: 19, we don't care about invalid month.. // invalid month: 19
Time t; Time t;
serverSocket_->writeData("213 20081908124312\r\n"); serverSocket_->writeData("213 20081908124312\r\n");
waitRead(clientSocket_); waitRead(clientSocket_);
CPPUNIT_ASSERT_EQUAL(213, ftp_->receiveMdtmResponse(t)); 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()); 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; Time t;

View File

@ -222,6 +222,10 @@ if ENABLE_ASYNC_DNS
aria2c_SOURCES += AsyncNameResolverTest.cc aria2c_SOURCES += AsyncNameResolverTest.cc
endif # ENABLE_ASYNC_DNS endif # ENABLE_ASYNC_DNS
if !HAVE_TIMEGM
aria2c_SOURCES += TimegmTest.cc
endif # !HAVE_TIMEGM
aria2c_LDADD = ../src/libaria2c.a @LIBINTL@ @CPPUNIT_LIBS@ aria2c_LDADD = ../src/libaria2c.a @LIBINTL@ @CPPUNIT_LIBS@
AM_CPPFLAGS = -Wall\ AM_CPPFLAGS = -Wall\
-I$(top_srcdir)/src\ -I$(top_srcdir)/src\

59
test/TimegmTest.cc Normal file
View File

@ -0,0 +1,59 @@
#include "timegm.h"
#include <cstring>
#include <iostream>
#include <cppunit/extensions/HelperMacros.h>
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