1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/date.h
2016-06-08 16:10:01 +02:00

560 lines
13 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// date.h
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef ULIB_DATE_H
#define ULIB_DATE_H
#include <ulib/string.h>
/**
* The UTimeDate class is based on the Gregorian (modern western) calendar.
* England adopted the Gregorian calendar on September 14th 1752, which is the
* earliest date that is supported by UTimeDate. Using earlier dates will give
* undefined results. Some countries adopted the Gregorian calendar later than
* England, thus the week day of early dates might be incorrect for these countries
* (but correct for England). The end of time is reached around 8000AD, by which
* time we expect UTimeDate to be obsolete...
*/
#define FIRST_DAY 2361222 // Julian day for 1752/09/14
#define FIRST_YEAR 1752 // wrong for many countries
class U_EXPORT UTimeDate {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
// COSTRUTTORI
UTimeDate()
{
U_TRACE_REGISTER_OBJECT(0, UTimeDate, "", 0)
julian = _day = _month = _year = 0;
}
void fromTime(time_t tm);
UTimeDate(time_t tm)
{
U_TRACE_REGISTER_OBJECT(0, UTimeDate, "%#19D", tm)
fromTime(tm);
}
void fromUTC(const char* str, const char* frm = "%02u%02u%02u");
UTimeDate(const char* str, bool UTC = false) // UTC is flag for date and time in Coordinated Universal Time: format YYMMDDMMSSZ
{
U_TRACE_REGISTER_OBJECT(0, UTimeDate, "%S,%b", str, UTC)
julian = _day = _month = _year = 0;
fromUTC(str, UTC ? "%02u%02u%02u" : "%d/%d/%d");
}
// set the current object by year
void setYear(int year)
{
U_TRACE(0, "UTimeDate::setYear(%d)", year)
// [ 0, 50) -> [2000,2050)
// [ 50, 150] -> [1950,2050)
// [1752,8000] -> [1752,8000]
_year = year;
if (year < 150) _year += 1900;
if (year < 50) _year += 100;
U_INTERNAL_ASSERT_RANGE(1752, _year, 8000)
julian = toJulian(_day, _month, _year);
}
void set(int day, int month, int year)
{
U_TRACE(0, "UTimeDate::set(%d,%d,%d)", day, month, year)
U_INTERNAL_ASSERT_RANGE(1, day, 31)
U_INTERNAL_ASSERT_RANGE(1, month, 12)
_day = day;
_month = month;
setYear(year);
}
// Construct a UTimeDate with a given day of the year and a given year. The base date for this computation
// is 31 Dec. of the previous year. i.e., UTimeDate(-1,1901) = 31 Dec. 1900 and UTimeDate(1,1901) = 2 Jan. 1901
UTimeDate(int day, int year)
{
U_TRACE_REGISTER_OBJECT(0, UTimeDate, "%d,%d", day, year)
fromJulian(julian = toJulian(1,1,year) - 1 + day);
}
UTimeDate(int day, int month, int year)
{
U_TRACE_REGISTER_OBJECT(0, UTimeDate, "%d,%d,%d", day, month, year)
set(day, month, year);
}
~UTimeDate()
{
U_TRACE_UNREGISTER_OBJECT(0, UTimeDate)
}
// ASSEGNAZIONI
void set(const UTimeDate& d)
{
_day = d._day;
_month = d._month;
_year = d._year;
julian = d.julian;
}
UTimeDate(const UTimeDate& d)
{
U_TRACE_REGISTER_OBJECT(0, UTimeDate, "%p", &d)
U_MEMORY_TEST_COPY(d)
set(d);
}
UTimeDate& operator=(const UTimeDate& d)
{
U_TRACE(0, "UTimeDate::operator=(%p)", &d)
U_MEMORY_TEST_COPY(d)
set(d);
return *this;
}
// SERVICES
int getJulian()
{
U_TRACE_NO_PARAM(0, "UTimeDate::getJulian()")
if (julian == 0) julian = toJulian(_day, _month, _year);
U_RETURN(julian);
}
bool isValid() const __pure;
int getDay() const { return _day; }
int getYear() const { return _year; }
int getMonth() const { return _month; }
const char* getDayName()
{
U_TRACE_NO_PARAM(0, "UTimeDate::getDayName()")
U_INTERNAL_ASSERT_RANGE(1, _day, 31)
return u_day_name[getDayOfWeek()];
}
UString getDayNameShort()
{
U_TRACE_NO_PARAM(0, "UTimeDate::getDayNameShort()")
UString result = UString(getDayName(), 3);
U_RETURN_STRING(result);
}
const char* getMonthName() const
{
U_TRACE_NO_PARAM(0, "UTimeDate::getMonthName()")
U_INTERNAL_ASSERT_RANGE(1, _month, 12)
return u_month_name[_month-1];
}
static void updateTime(char* ptr)
{
U_TRACE(0, "UTimeDate::updateTime(%.5S)", ptr)
U_INTERNAL_ASSERT(u_now->tv_sec % U_ONE_HOUR_IN_SECOND)
U_NUM2STR16(ptr, (u_now->tv_sec / 60) % 60);
U_NUM2STR16(ptr+3,u_now->tv_sec % 60);
}
// The daysTo() function returns the number of days between two date
int daysTo(UTimeDate& date)
{
U_TRACE(0, "UTimeDate::daysTo(%p)", &date)
int diff = (date.getJulian() - getJulian());
U_RETURN(diff);
}
void addDays(int day)
{
U_TRACE(0, "UTimeDate::addDays(%d)", day)
// NB: we need to consider the case (day < 0)...
fromJulian(julian = getJulian() + day);
}
void addYears(int year)
{
U_TRACE(0, "UTimeDate::addYears(%d)", year)
julian = toJulian(_day, _month, _year += year);
}
void addMonths(int month);
// Returns the number of days in the month (28..31) for this date
int getDaysInMonth() const __pure
{
U_TRACE_NO_PARAM(0, "UTimeDate::getDaysInMonth()")
if (_month == 2 &&
leapYear(_year))
{
U_RETURN(29);
}
U_RETURN(monthDays[_month]);
}
// Returns the day of the year [1,366] for this date
int getDayOfYear() __pure
{
U_TRACE_NO_PARAM(0, "UTimeDate::getDayOfYear()")
int y = _year - 1901,
result = getJulian() - (y * 365) - (y / 4) - 2415385; // 2415385 -> 31/12/1900
U_RETURN(result);
}
// Returns true if the specified year is a leap year; otherwise returns false
static bool leapYear(int year)
{
U_TRACE(0, "UTimeDate::leapYear(%d)", year)
// 1. Every year that is divisible by four is a leap year
// 2. of those years, if it can be divided by 100, it is NOT a leap year, unless
// 3. the year is divisible by 400. Then it is a leap year
if ( ((year % 4) == 0) &&
(((year % 100) != 0) ||
((year % 400) == 0)))
{
U_RETURN(true);
}
U_RETURN(false);
}
// Week management
int getWeekOfYear()
{
U_TRACE_NO_PARAM(0, "UTimeDate::getWeekOfYear()")
int week_number = ((getJulian() - toJulian(1, 1, _year)) / 7) + 1;
U_RETURN(week_number);
}
int getDayOfWeek()
{
U_TRACE_NO_PARAM(0, "UTimeDate::getDayOfWeek()")
int day_of_week = (getJulian() + 1) % 7; // gives the value 0 for Sunday ... 6 for Saturday
U_RETURN(day_of_week);
}
void setMondayPrevWeek()
{
U_TRACE_NO_PARAM(0, "UTimeDate::setMondayPrevWeek()")
int day_of_week = getDayOfWeek();
// 1 => -7
// 2 => -1
// 3 => -2
// 4 => -3
// 5 => -4
// 6 => -5
// 0 => -6
addDays(day_of_week >= 2 ? -day_of_week+1 : -6-day_of_week);
}
void setMondayNextWeek()
{
U_TRACE_NO_PARAM(0, "UTimeDate::setMondayNextWeek()")
int day_of_week = getDayOfWeek();
// 1 => 7
// 2 => 6
// 3 => 5
// 4 => 4
// 5 => 3
// 6 => 2
// 0 => 1
addDays(day_of_week ? 8-day_of_week : 1);
}
void setDayOfWeek(int day_of_week)
{
U_TRACE_NO_PARAM(0, "UTimeDate::setDayOfWeek()")
int diff = day_of_week - getDayOfWeek();
U_INTERNAL_DUMP("diff = %d", diff)
if (diff) addDays(diff);
U_ASSERT_EQUALS(day_of_week, getDayOfWeek())
}
void setYearAndWeek(int year, int week)
{
U_TRACE(0, "UTimeDate::setYearAndWeek(%d,%d)", year, week)
U_INTERNAL_ASSERT_RANGE(1, week, 53)
set(1, 1, year);
if (week > 1) addDays((week-1) * 7);
}
void setMondayPrevMonth(int month) // set monday of previous month
{
U_TRACE(0, "UTimeDate::setMondayPrevMonth(%d)", month)
fromJulian(julian = toJulian(1, month, _year));
setDayOfWeek(1);
}
// Print date with format
UString strftime(const char* fmt);
static UString strftime(const char* fmt, time_t t, bool blocale = false);
time_t getSecond()
{
U_TRACE_NO_PARAM(0, "UTimeDate::getSecond()")
time_t t = getSecondFromJulian(getJulian());
U_RETURN(t);
}
static time_t getSecondFromJulian(int _julian)
{
U_TRACE(0, "UTimeDate::getSecondFromJulian(%d)", _julian)
if (_julian >= 2440588) // 2440588 -> 01/01/1970
{
time_t t = (_julian - 2440588) * U_ONE_DAY_IN_SECOND;
U_RETURN(t);
}
U_RETURN(0);
}
static time_t getSecondFromTime(const char* str) // seconds from string in HH:MM:SS format
{
U_TRACE(0, "UTimeDate::getSecondFromTime(%.*S)", 8, str)
U_INTERNAL_ASSERT_EQUALS(str[2], ':')
U_INTERNAL_ASSERT_EQUALS(str[5], ':')
// NB: sscanf() is VERY HEAVY...!!!
time_t t = (60 * 60 * atoi(str)) + (60 * atoi(str+3)) + atoi(str+6);
U_RETURN(t);
}
static time_t getSecondFromTime(const char* str, bool gmt, const char* fmt = "%a, %d %b %Y %T GMT", struct tm* tm = 0);
void setCurrentDate() // UNIX system time - SecsSince1Jan1970UTC
{
U_TRACE_NO_PARAM(0, "UTimeDate::setCurrentDate()")
U_gettimeofday // NB: optimization if it is enough a time resolution of one second...
fromTime(u_now->tv_sec);
}
static void setCurrentDate(UTimeDate& date)
{
U_TRACE(0, "UTimeDate::setCurrentDate(%p)", &date)
date.setCurrentDate();
}
// This can be used for comments and other from of communication to tell the time ago instead of the exact time which might not be correct to some one in another time zone
UString ago(uint32_t granularity = 0)
{
U_TRACE(0, "UTimeDate::ago(%u)", granularity)
return ago(getSecond(), granularity);
}
static UString ago(time_t tm, uint32_t granularity = 0)
{
U_TRACE(0, "UTimeDate::ago(%ld,%u)", tm, granularity)
U_gettimeofday // NB: optimization if it is enough a time resolution of one second...
return _ago(tm, granularity);
}
// OPERATOR
bool operator==(UTimeDate& date)
{
U_TRACE(0, "UTimeDate::operator==(%p,%p)", this, &date)
if (getJulian() == date.getJulian()) U_RETURN(true);
U_RETURN(false);
}
bool operator!=(UTimeDate& date);
bool operator< (UTimeDate& date);
bool operator<=(UTimeDate& date);
bool operator> (UTimeDate& date);
bool operator>=(UTimeDate& date);
UTimeDate& operator++() { addDays( 1); return *this; }
UTimeDate& operator--() { addDays(-1); return *this; }
UTimeDate& operator+=(UTimeDate& d)
{
U_TRACE(0, "UTimeDate::operator+=(%p)", &d)
fromJulian(getJulian() + d.getJulian());
return *this;
}
UTimeDate& operator-=(UTimeDate& d)
{
U_TRACE(0, "UTimeDate::operator-=(%p)", &d)
fromJulian(getJulian() - d.getJulian());
return *this;
}
friend UTimeDate operator+(const UTimeDate& d1, UTimeDate& d2)
{
U_TRACE(0, "UTimeDate::operator+(%p,%p)", &d1, &d2)
return UTimeDate(d1) += d2;
}
friend UTimeDate operator-(const UTimeDate& d1, UTimeDate& d2)
{
U_TRACE(0, "UTimeDate::operator+(%p,%p)", &d1, &d2)
return UTimeDate(d1) -= d2;
}
UTimeDate& operator+=(int days)
{
U_TRACE(0, "UTimeDate::operator+=(%d)", days)
addDays(days);
return *this;
}
UTimeDate& operator-=(int days)
{
U_TRACE(0, "UTimeDate::operator-=(%d)", days)
addDays(-days);
return *this;
}
friend UTimeDate operator+(UTimeDate& d, int days)
{
U_TRACE(0, "UTimeDate::operator+(%p,%d)", &d, days)
return UTimeDate(d) += days;
}
friend UTimeDate operator-(UTimeDate& d, int days)
{
U_TRACE(0, "UTimeDate::operator-(%p,%d)", &d, days)
return UTimeDate(d) -= days;
}
// STREAM
#ifdef U_STDCPP_ENABLE
friend U_EXPORT istream& operator>>(istream& is, UTimeDate& d); // dd/mm/yyyy
friend U_EXPORT ostream& operator<<(ostream& os, const UTimeDate& d); // dd/mm/yyyy
# ifdef DEBUG
bool invariant() { return (julian >= FIRST_DAY); } // 14/09/1752
const char* dump(bool reset) const;
# endif
#endif
protected:
int julian, _day, _month, _year;
static const char* periods[8];
static const uint32_t lengths[8];
static const short monthDays[13];
void fromJulian(int julian);
static int toJulian(int day, int month, int year);
static UString _ago(time_t tm, uint32_t granularity);
};
#endif