From 519b054a24d2c6d96e1f3eb9d924d7e36994ed6e Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 15 May 2013 19:09:37 +0200 Subject: [PATCH] Add gmtime_r(3). --- libc/Makefile | 8 +- libc/localtime_r.cpp | 39 ---- libc/{ => time}/gmtime.cpp | 4 +- libc/time/gmtime_r.cpp | 242 ++++++++++++++++++++ libc/{ => time}/localtime.cpp | 2 +- libc/{gmtime_r.cpp => time/localtime_r.cpp} | 10 +- 6 files changed, 254 insertions(+), 51 deletions(-) delete mode 100644 libc/localtime_r.cpp rename libc/{ => time}/gmtime.cpp (91%) create mode 100644 libc/time/gmtime_r.cpp rename libc/{ => time}/localtime.cpp (98%) rename libc/{gmtime_r.cpp => time/localtime_r.cpp} (78%) diff --git a/libc/Makefile b/libc/Makefile index c2b4dfbd..f8cd2c26 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -70,14 +70,10 @@ fwrite.o \ fwriting.o \ getdelim.o \ getline.o \ -gmtime.o \ -gmtime_r.o \ heap.o \ integer.o \ ldiv.o \ lldiv.o \ -localtime.o \ -localtime_r.o \ mblen.o \ mbrlen.o \ mbrtowc.o \ @@ -136,6 +132,10 @@ time/asctime.o \ time/asctime_r.o \ time/ctime.o \ time/ctime_r.o \ +time/gmtime.o \ +time/gmtime_r.o \ +time/localtime.o \ +time/localtime_r.o \ timespec.o \ time/strftime.o \ ungetc.o \ diff --git a/libc/localtime_r.cpp b/libc/localtime_r.cpp deleted file mode 100644 index 6611c4fb..00000000 --- a/libc/localtime_r.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - This file is part of the Sortix C Library. - - The Sortix C Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - The Sortix C Library 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with the Sortix C Library. If not, see . - - localtime_r.cpp - Transform date and time. - -*******************************************************************************/ - -#include - -extern "C" struct tm* localtime_r(const time_t* timer, struct tm* ret) -{ - time_t time = *timer; - ret->tm_sec = time % 60; // No leap seconds. - ret->tm_min = (time / 60) % 60; - ret->tm_hour = (time / 60 / 60) % 24; - ret->tm_mday = 0; - ret->tm_mon = 0; - ret->tm_year = 0; - ret->tm_wday = 0; - ret->tm_isdst = 0; - return ret; -} diff --git a/libc/gmtime.cpp b/libc/time/gmtime.cpp similarity index 91% rename from libc/gmtime.cpp rename to libc/time/gmtime.cpp index f3d68a0b..479450ee 100644 --- a/libc/gmtime.cpp +++ b/libc/time/gmtime.cpp @@ -17,8 +17,8 @@ You should have received a copy of the GNU Lesser General Public License along with the Sortix C Library. If not, see . - gmtime.cpp - Transform date and time. + time/gmtime.cpp + Convert a timestamp into a date and time according to the local timezone. *******************************************************************************/ diff --git a/libc/time/gmtime_r.cpp b/libc/time/gmtime_r.cpp new file mode 100644 index 00000000..9948b675 --- /dev/null +++ b/libc/time/gmtime_r.cpp @@ -0,0 +1,242 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library 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 Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + time/gmtime_r.cpp + Convert a timestamp into a date and time according to UTC. + +*******************************************************************************/ + +#include + +#include +#include + +static const int DAYS_JANUARY = 31; +static const int DAYS_FEBRUARY = 28; +static const int DAYS_MARCH = 31; +static const int DAYS_APRIL = 30; +static const int DAYS_MAY = 31; +static const int DAYS_JUNE = 30; +static const int DAYS_JULY = 31; +static const int DAYS_AUGUST = 31; +static const int DAYS_SEPTEMBER = 30; +static const int DAYS_OCTOBER = 31; +static const int DAYS_NOVEMBER = 30; +static const int DAYS_DECEMBER = 31; + +#define DECL_LEAP_SECOND(year, jun, dec) \ + {0, 0, 0, 0, 0, jun, 0, 0, 0, 0, 0, dec} + +static int8_t leap_seconds[][12] = +{ + DECL_LEAP_SECOND(1970, 0, 0), + DECL_LEAP_SECOND(1971, 0, 0), + DECL_LEAP_SECOND(1972, 0, 0), + DECL_LEAP_SECOND(1972, 1, 1), + DECL_LEAP_SECOND(1973, 0, 1), + DECL_LEAP_SECOND(1974, 0, 1), + DECL_LEAP_SECOND(1975, 0, 1), + DECL_LEAP_SECOND(1976, 0, 1), + DECL_LEAP_SECOND(1977, 0, 1), + DECL_LEAP_SECOND(1978, 0, 1), + DECL_LEAP_SECOND(1979, 0, 1), + DECL_LEAP_SECOND(1980, 0, 0), + DECL_LEAP_SECOND(1981, 1, 0), + DECL_LEAP_SECOND(1982, 1, 0), + DECL_LEAP_SECOND(1983, 1, 0), + DECL_LEAP_SECOND(1984, 0, 0), + DECL_LEAP_SECOND(1985, 1, 0), + DECL_LEAP_SECOND(1986, 0, 0), + DECL_LEAP_SECOND(1987, 0, 1), + DECL_LEAP_SECOND(1988, 0, 0), + DECL_LEAP_SECOND(1989, 0, 1), + DECL_LEAP_SECOND(1990, 0, 1), + DECL_LEAP_SECOND(1991, 0, 0), + DECL_LEAP_SECOND(1992, 1, 0), + DECL_LEAP_SECOND(1993, 1, 0), + DECL_LEAP_SECOND(1994, 1, 0), + DECL_LEAP_SECOND(1995, 0, 1), + DECL_LEAP_SECOND(1996, 0, 0), + DECL_LEAP_SECOND(1997, 1, 0), + DECL_LEAP_SECOND(1998, 0, 1), + DECL_LEAP_SECOND(1999, 0, 0), + DECL_LEAP_SECOND(2000, 0, 0), + DECL_LEAP_SECOND(2001, 0, 0), + DECL_LEAP_SECOND(2002, 0, 0), + DECL_LEAP_SECOND(2003, 0, 0), + DECL_LEAP_SECOND(2004, 0, 0), + DECL_LEAP_SECOND(2005, 0, 1), + DECL_LEAP_SECOND(2006, 0, 0), + DECL_LEAP_SECOND(2007, 0, 0), + DECL_LEAP_SECOND(2008, 0, 1), + DECL_LEAP_SECOND(2009, 0, 0), + DECL_LEAP_SECOND(2010, 0, 0), + DECL_LEAP_SECOND(2011, 0, 0), + DECL_LEAP_SECOND(2012, 1, 0), + DECL_LEAP_SECOND(2013, 0, 0), +}; + +static time_t get_leap_second(int year, int month) +{ + const time_t num_years = sizeof(leap_seconds) / sizeof(leap_seconds[0]); + if ( year < 1970 ) + return 0; + if ( num_years <= year-1970 ) + return 0; + return leap_seconds[year-1970][month]; +} + +static time_t leap_seconds_in_year(int year) +{ + time_t ret = 0; + for ( int i = 0; i < 12; i++ ) + ret += get_leap_second(year, i); + return ret; +} + +static bool is_leap_year(int year) +{ + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +static time_t days_in_year(int year) +{ + return DAYS_JANUARY + + DAYS_FEBRUARY + (is_leap_year(year) ? 1 : 0) + + DAYS_MARCH + + DAYS_APRIL + + DAYS_MAY + + DAYS_JUNE + + DAYS_JULY + + DAYS_AUGUST + + DAYS_SEPTEMBER + + DAYS_OCTOBER + + DAYS_NOVEMBER + + DAYS_DECEMBER; +} + +extern "C" struct tm* gmtime_r(const time_t* time_ptr, struct tm* ret) +{ + time_t left = *time_ptr; + + ret->tm_year = 1970; + ret->tm_wday = 4 /* Supposedly, the world began on a Thursday. */; + + // If the timestamp is after the epoch. + while ( 0 < left ) + { + time_t year_leaps = leap_seconds_in_year(ret->tm_year); + time_t year_days = days_in_year(ret->tm_year); + time_t year_seconds = year_days * 24 * 60 * 60 + year_leaps; + if ( year_seconds <= left ) + { + left -= year_seconds; + ret->tm_wday = (ret->tm_wday + year_days) % 7; + ret->tm_year++; + continue; + } + break; + } + + // If the timestamp was before the epoch. + while ( left < 0 ) + { + ret->tm_year--; + time_t year_leaps = leap_seconds_in_year(ret->tm_year); + time_t year_days = days_in_year(ret->tm_year); + time_t year_seconds = year_days * 24 * 60 * 60 + year_leaps; + left += year_seconds; + // We need to avoid taking the modulo of a negative value or the + // (broken) C modulo operator gives the wrong result. + ret->tm_wday = (ret->tm_wday - year_days + 7*7*7*7) % 7; + } + + int month_days_list[12] = + { + DAYS_JANUARY, + DAYS_FEBRUARY + (is_leap_year(ret->tm_year) ? 1 : 0), + DAYS_MARCH, + DAYS_APRIL, + DAYS_MAY, + DAYS_JUNE, + DAYS_JULY, + DAYS_AUGUST, + DAYS_SEPTEMBER, + DAYS_OCTOBER, + DAYS_NOVEMBER, + DAYS_DECEMBER, + }; + + // Figure out the correct month. + ret->tm_mon = 0; + ret->tm_yday = 0; + while ( true ) + { + int month_leaps = get_leap_second(ret->tm_year, ret->tm_mon); + int month_days = month_days_list[ret->tm_mon]; + int month_seconds = month_days * 24 * 60 * 60 + month_leaps; + if ( month_seconds <= left ) + { + left -= month_seconds; + ret->tm_mon++; + ret->tm_yday += month_days; + ret->tm_wday = (ret->tm_wday + month_days) % 7; + continue; + } + break; + } + + ret->tm_mday = left / (24 * 60 * 60); + left = left % (24 * 60 * 60); + + // If this is a regular timestamp. + if ( ret->tm_mday < month_days_list[ret->tm_mon] ) + { + ret->tm_yday += ret->tm_mday; + + ret->tm_hour = left / (60 * 60); + left = left % (60 * 60); + + ret->tm_min = left / 60; + left = left % 60; + + ret->tm_sec = left; + } + + // If we got the timestamp for an added leap second. + else + { + ret->tm_mday--; // Seemingly additional day. + ret->tm_yday += ret->tm_mday; + ret->tm_hour = 23; + ret->tm_min = 59; + ret->tm_sec = 60; + } + + ret->tm_wday = (ret->tm_wday + ret->tm_mday) % 7; + + // TODO: Support daylight savings and timezones. + ret->tm_isdst = -1; + + // Fix the ranges of some of the variables. + ret->tm_mday += 1; + ret->tm_year -= 1900; + + return ret; +} diff --git a/libc/localtime.cpp b/libc/time/localtime.cpp similarity index 98% rename from libc/localtime.cpp rename to libc/time/localtime.cpp index ff07d376..c11caedf 100644 --- a/libc/localtime.cpp +++ b/libc/time/localtime.cpp @@ -17,7 +17,7 @@ You should have received a copy of the GNU Lesser General Public License along with the Sortix C Library. If not, see . - localtime.cpp + time/localtime.cpp Convert a timestamp into a date and time according to the local timezone. *******************************************************************************/ diff --git a/libc/gmtime_r.cpp b/libc/time/localtime_r.cpp similarity index 78% rename from libc/gmtime_r.cpp rename to libc/time/localtime_r.cpp index be06d3c5..6d00cb8c 100644 --- a/libc/gmtime_r.cpp +++ b/libc/time/localtime_r.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of the Sortix C Library. @@ -17,14 +17,14 @@ You should have received a copy of the GNU Lesser General Public License along with the Sortix C Library. If not, see . - gmtime_r.cpp - Transform date and time. + time/localtime_r.cpp + Convert a timestamp into a date and time according to the local timezone. *******************************************************************************/ #include -extern "C" struct tm* gmtime_r(const time_t* timer, struct tm* ret) +extern "C" struct tm* localtime_r(const time_t* time_ptr, struct tm* ret) { - return localtime_r(timer, ret); + return gmtime_r(time_ptr, ret); }