From 3280568594d45de3a8460f1a99171f8933526cb3 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Sat, 5 May 2012 15:08:04 +0000 Subject: [PATCH] added: Date structure -- represents year, month, day, hour, min, sec with O(1) algorithm for converting from time_t (seconds from Unix epoch) git-svn-id: svn://ttmath.org/publicrep/pikotools/trunk@405 e52654a7-88a9-db11-a3e9-0013d4bc506e --- Makefile | 13 +- date/Makefile | 27 +++ date/Makefile.dep | 3 + date/Makefile.o.dep | 1 + date/date.cpp | 385 ++++++++++++++++++++++++++++++++++++++ date/date.h | 293 +++++++++++++++++++++++++++++ mainparser/Makefile.dep | 6 +- mainparser/Makefile.o.dep | 2 +- space/Makefile.dep | 8 +- space/Makefile.o.dep | 2 +- utf8/Makefile.dep | 6 +- utf8/Makefile.o.dep | 2 +- 12 files changed, 734 insertions(+), 14 deletions(-) create mode 100644 date/Makefile create mode 100644 date/Makefile.dep create mode 100644 date/Makefile.o.dep create mode 100644 date/date.cpp create mode 100644 date/date.h diff --git a/Makefile b/Makefile index 0f5d76a..d3d882e 100755 --- a/Makefile +++ b/Makefile @@ -13,7 +13,10 @@ export CXX export CXXFLAGS -all: space mainparser utf8 +all: space mainparser utf8 date + + + space: FORCE @cd space ; $(MAKE) -e @@ -24,6 +27,12 @@ mainparser: FORCE utf8: FORCE @cd utf8 ; $(MAKE) -e +date: FORCE + @cd date ; $(MAKE) -e + + + + FORCE: @@ -32,8 +41,10 @@ clean: @cd space ; $(MAKE) -e clean @cd mainparser ; $(MAKE) -e clean @cd utf8 ; $(MAKE) -e clean + @cd date ; $(MAKE) -e clean depend: @cd space ; $(MAKE) -e depend @cd mainparser ; $(MAKE) -e depend @cd utf8 ; $(MAKE) -e depend + @cd date ; $(MAKE) -e depend diff --git a/date/Makefile b/date/Makefile new file mode 100644 index 0000000..fc7c747 --- /dev/null +++ b/date/Makefile @@ -0,0 +1,27 @@ +include Makefile.o.dep + +libname=date.a + +all: $(libname) + +$(libname): $(o) + ar rcs $(libname) $(o) + + +%.o: %.cpp + $(CXX) -c $(CXXFLAGS) $< + + + +depend: + makedepend -Y. -I.. -f- *.cpp > Makefile.dep + echo -n "o = " > Makefile.o.dep + ls -1 *.cpp | xargs -I foo echo -n foo " " | sed -E "s/([^\.]*)\.cpp[ ]/\1\.o/g" >> Makefile.o.dep + + +clean: + rm -f *.o + rm -f $(libname) + + +include Makefile.dep diff --git a/date/Makefile.dep b/date/Makefile.dep new file mode 100644 index 0000000..2f861c5 --- /dev/null +++ b/date/Makefile.dep @@ -0,0 +1,3 @@ +# DO NOT DELETE + +date.o: date.h diff --git a/date/Makefile.o.dep b/date/Makefile.o.dep new file mode 100644 index 0000000..1e1c891 --- /dev/null +++ b/date/Makefile.o.dep @@ -0,0 +1 @@ +o = date.o \ No newline at end of file diff --git a/date/date.cpp b/date/date.cpp new file mode 100644 index 0000000..236fda6 --- /dev/null +++ b/date/date.cpp @@ -0,0 +1,385 @@ +/* + * This file is a part of PikoTools + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "date.h" + +// for memset +#include + + +namespace PT +{ + + +Date::Date() +{ + Clear(); +} + + +Date::Date(const Date & d) +{ + operator=(d); +} + + +Date::Date(time_t t) +{ + FromTime(t); +} + +Date::Date(const tm & t) +{ + FromTm(t); +} + + +Date & Date::operator=(const Date & d) +{ + year = d.year; + month = d.month; + day = d.day; + hour = d.hour; + min = d.min; + sec = d.sec; + +return *this; +} + + +Date & Date::operator=(time_t t) +{ + FromTime(t); + +return *this; +} + + +Date & Date::operator=(const tm & t) +{ + FromTm(t); + +return *this; +} + + +Date Date::operator+(time_t t) const +{ + time_t t0 = ToTime(); + Date d(t0 + t); + +return d; +} + + +Date Date::operator-(time_t t) const +{ + time_t t0 = ToTime(); + Date d(t0 - t); + +return d; +} + + +Date & Date::operator+=(time_t t) +{ + time_t t0 = ToTime(); + FromTime(t0 + t); + +return *this; +} + + +Date & Date::operator-=(time_t t) +{ + time_t t0 = ToTime(); + FromTime(t0 - t); + +return *this; +} + + +time_t Date::operator-(const Date & d) const +{ + time_t t0 = ToTime(); + time_t t1 = d.ToTime(); + time_t res; + + if( t1 >= t0 ) + res = t1 - t0; + else + res = t0 - t1; + +return res; +} + + +bool Date::IsTheSameDay(const Date & d) const +{ + return year == d.year && month == d.month && day == d.day; +} + + +bool Date::IsTheSameHour(const Date & d) const +{ + return hour == d.hour && min == d.min && sec == d.sec; +} + + +bool Date::operator==(const Date & d) const +{ + return IsTheSameDay(d) && IsTheSameHour(d); +} + + +bool Date::operator!=(const Date & d) const +{ + return !operator==(d); +} + + +int Date::Compare(const Date & d) const +{ + if( year != d.year ) + return year - d.year; + + if( month != d.month ) + return month - d.month; + + if( day != d.day ) + return day - d.day; + + if( hour != d.hour ) + return hour - d.hour; + + if( min != d.min ) + return min - d.min; + + if( sec != d.sec ) + return sec - d.sec; + + // dates are equal + return 0; +} + + +bool Date::operator>(const Date & d) const +{ + return Compare(d) > 0; +} + + +bool Date::operator>=(const Date & d) const +{ + return Compare(d) >= 0; +} + + +bool Date::operator<(const Date & d) const +{ + return Compare(d) < 0; +} + + +bool Date::operator<=(const Date & d) const +{ + return Compare(d) <= 0; +} + + +void Date::Clear() +{ + year = 1970; + month = 1; + day = 1; + hour = 0; + min = 0; + sec = 0; +} + + +void Date::AssertRange(int & val, int val_min, int val_max) +{ + if( val < val_min ) + val = val_min; + + if( val > val_max ) + val = val_max; +} + + +void Date::AssertCorrectDate() +{ + // 10000 is only a 'cosmetic' limit + // we can make calculations with greater values + AssertRange(year, 1970, 10000); + AssertRange(month, 1, 12); + AssertRange(day, 1, MonthLen(year, month)); + AssertRange(hour, 0, 23); + AssertRange(min, 0, 59); + AssertRange(sec, 0, 59); +} + + + +int Date::MonthLen(int y, int m) +{ + if( m == 2 && IsYearLeap(y) ) + return 29; + + const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if( m>=1 && m<=12 ) + return days[m-1]; + +return 0; +} + + + +bool Date::IsYearLeap(int y) +{ + return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); +} + + +bool Date::IsYearLeap() const +{ + return IsYearLeap(year); +} + + +/* + return 'days' starts from 0000:03:01 (year 0, month 3 - March, day 1) +*/ +long long Date::ToDays() const +{ + long long m = (month + 9) % 12; + long long y = year - m/10; + +return 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + (day - 1); +} + + +time_t Date::ToTime() const +{ + time_t res = time_t((ToDays() - 719468) * 60*60*24); + res += hour * 60 * 60; + res += min * 60; + res += sec; + +return res; +} + + +tm Date::ToTm() const +{ +tm t; + + memset(&t, 0, sizeof(tm)); + t.tm_year = year - 1900; + t.tm_mon = month - 1; + t.tm_mday = day; + t.tm_hour = hour; + t.tm_min = min; + t.tm_sec = sec; + +return t; +} + + +/* + this method calculates year, month and a day from given 'days' + 'days' starts from 0000:03:01 (year 0, month 3 - March, day 1) +*/ +void Date::FromDays(long long days) +{ + //static_assert( sizeof(long long) >= 8 , "operation here should be used at least with 64 bits precision"); + + year = int(((long long)(10000)*days + 14780)/3652425); + int delta = int(days - (365*year + year/4 - year/100 + year/400)); + + if( delta < 0 ) + { + year -= 1; + delta = int(days - (365*year + year/4 - year/100 + year/400)); + } + + int mi = (100*delta + 52)/3060; + month = (mi + 2)%12 + 1; + year = year + (mi + 2)/12; + day = delta - (mi*306 + 5)/10 + 1; +} + + +void Date::FromTime(time_t t) +{ + time_t days = t / 60 / 60 / 24; + time_t diff = t - days * 60 * 60 * 24; + + hour = int(diff / 60 / 60); + min = int((diff - hour * 60 * 60) / 60); + sec = int((diff - hour * 60 * 60 - min * 60)); + + FromDays((long long)(days) + 719468); +} + + +void Date::FromTm(const tm & t) +{ + year = t.tm_year + 1900; + month = t.tm_mon + 1; + day = t.tm_mday; + hour = t.tm_hour; + min = t.tm_min; + sec = t.tm_sec; +} + + +int Date::WeekDay() const +{ + long long d = ToDays(); + +return (int)((d+3) % 7); +} + + + + + +} // namespace + diff --git a/date/date.h b/date/date.h new file mode 100644 index 0000000..157a6a9 --- /dev/null +++ b/date/date.h @@ -0,0 +1,293 @@ +/* + * This file is a part of PikoTools + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfile_picotools_mainparser_mainparser +#define headerfile_picotools_mainparser_mainparser + +#include + + + +namespace PT +{ + + +/* + this class represents a Date (year, month, day, hour, min, sec) + it has O(1) algorithm when converting from/to time_t (seconds from unix epoch) + + algorithm description: + http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html + http://alcor.concordia.ca/~gpkatch/gdate-method.html +*/ +class Date +{ +public: + + + /* + default c-ctor sets the unix epoch (Clear method): 1970.01.01 00:00:00 + */ + Date(); + + + /* + converting from Date, time_t (seconds from unix epoch), tm structure + */ + Date(const Date & d); + Date(time_t t); + Date(const tm & t); + Date & operator=(const Date & d); + Date & operator=(time_t t); + Date & operator=(const tm & t); + + + /* + adding/subtracting time_t (seconds from unix epoch) + */ + Date operator+(time_t t) const; + Date operator-(time_t t) const; + Date & operator+=(time_t t); + Date & operator-=(time_t t); + + + /* + returns a difference in second between two dates + (always a value greater than zero) + */ + time_t operator-(const Date & d) const; + + + /* + 'Compare' returns zero if this and d are equal + return value less than zero if this is lower than d + and a value greater than zero if this is greater than d + */ + int Compare(const Date & d) const; + + + /* + returns true if year, month and day are the same + */ + bool IsTheSameDay(const Date & d) const; + + + /* + returns true if hour, min and sec are the same + */ + bool IsTheSameHour(const Date & d) const; + + + /* + operators for comparing + */ + bool operator==(const Date & d) const; + bool operator!=(const Date & d) const; + bool operator>(const Date & d) const; + bool operator>=(const Date & d) const; + bool operator<(const Date & d) const; + bool operator<=(const Date & d) const; + + + /* + set unix epoch: 1970.01.01 00:00:00 + */ + void Clear(); + + + /* + assert a correct date (values will be from the correct range) + year: 1970 - 10000 + month: 1 - 12 + day: 1 - MonthLen + hour: 0 - 23 + min: 0 - 59 + sec: 0 - 59 + */ + void AssertCorrectDate(); + + + /* + converts time_t in seconds (from unix epoch) to this object + */ + void FromTime(time_t t); + + + /* + converts tm structure to this object + */ + void FromTm(const tm & t); + + + /* + returns time_t (in seconds from unix epoch) + */ + time_t ToTime() const; + + + /* + return tm structure + only tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec fields are set + the rest is equal to zero + */ + tm ToTm() const; + + + /* + getting/setting the number of days from 0000:03:01 (year 0, month 3 - March, day 1) + (ToDays() and FromDays() can work even with a year less than 1970) + */ + long long ToDays() const; + void FromDays(long long g); + + + /* + returns how many days there is in a month + y - year 1970 - ... + m - month 1-12 + */ + static int MonthLen(int y, int m); + + + /* + returns true if 'y' is a leap year + leap year has one additional day (in february) - so the year lasts 366 days + */ + static bool IsYearLeap(int y); + + + /* + returns true if the currecn year is a leap year + */ + bool IsYearLeap() const; + + + /* + returns a day index from a week + sunday - 0 + monday - 1 + ... + saturday - 6 + */ + int WeekDay() const; + + + /* + this method produces: year.month.day, eg. 1990.02.12 + */ + template + void SerializeDay(Stream & out) const; + + + /* + this method produces: hour:min:sec, eg: 13:05:39 + */ + template + void SerializeHour(Stream & out) const; + + + /* + this method produces: year.month.day our:min:sec, eg: 1990.02.12 13:05:39 + */ + template + void Serialize(Stream & out) const; + + int year; // 1970 - ... + int month; // 1 - 12 + int day; // 1 - 31 + int hour; // 0 - 23 + int min; // 0 - 59 + int sec; // 0 - 59 + + +private: + + void AssertRange(int & val, int val_min, int val_max); + + template + void SerializeInt(Stream & out, int val) const; + +}; + + + +template +void Date::SerializeInt(Stream & out, int val) const +{ + if( val < 10 ) + out << '0'; + + out << val; +} + + + +template +void Date::SerializeDay(Stream & out) const +{ + out << year << '.'; + SerializeInt(out, month); + out << '.'; + SerializeInt(out, day); +} + + +template +void Date::SerializeHour(Stream & out) const +{ + SerializeInt(out, hour); + out << ':'; + SerializeInt(out, min); + out << ':'; + SerializeInt(out, sec); +} + +template +void Date::Serialize(Stream & out) const +{ + SerializeDay(out); + out << ' '; + SerializeHour(out); +} + + + + + +} // namespace + +#endif diff --git a/mainparser/Makefile.dep b/mainparser/Makefile.dep index ad09e57..25b02b8 100644 --- a/mainparser/Makefile.dep +++ b/mainparser/Makefile.dep @@ -1,3 +1,3 @@ -# DO NOT DELETE - -mainparser.o: mainparser.h +# DO NOT DELETE + +mainparser.o: mainparser.h diff --git a/mainparser/Makefile.o.dep b/mainparser/Makefile.o.dep index bf313b1..0a6c3a7 100644 --- a/mainparser/Makefile.o.dep +++ b/mainparser/Makefile.o.dep @@ -1 +1 @@ -o = mainparser.o +o = mainparser.o \ No newline at end of file diff --git a/space/Makefile.dep b/space/Makefile.dep index c43c31e..cb2d118 100755 --- a/space/Makefile.dep +++ b/space/Makefile.dep @@ -1,4 +1,4 @@ -# DO NOT DELETE - -space.o: space.h ../utf8/utf8.h -spaceparser.o: spaceparser.h space.h ../utf8/utf8.h +# DO NOT DELETE + +space.o: space.h ../utf8/utf8.h +spaceparser.o: spaceparser.h space.h ../utf8/utf8.h diff --git a/space/Makefile.o.dep b/space/Makefile.o.dep index 2f519c5..20925f2 100755 --- a/space/Makefile.o.dep +++ b/space/Makefile.o.dep @@ -1 +1 @@ -o = space.o spaceparser.o +o = space.o spaceparser.o \ No newline at end of file diff --git a/utf8/Makefile.dep b/utf8/Makefile.dep index f2f3eba..f08d758 100755 --- a/utf8/Makefile.dep +++ b/utf8/Makefile.dep @@ -1,3 +1,3 @@ -# DO NOT DELETE - -utf8.o: utf8.h +# DO NOT DELETE + +utf8.o: utf8.h diff --git a/utf8/Makefile.o.dep b/utf8/Makefile.o.dep index 33f18af..25d2b0d 100755 --- a/utf8/Makefile.o.dep +++ b/utf8/Makefile.o.dep @@ -1 +1 @@ -o = utf8.o +o = utf8.o \ No newline at end of file