diff options
Diffstat (limited to 'mimelib/datetime.cpp')
-rw-r--r-- | mimelib/datetime.cpp | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/mimelib/datetime.cpp b/mimelib/datetime.cpp new file mode 100644 index 000000000..a704e127b --- /dev/null +++ b/mimelib/datetime.cpp @@ -0,0 +1,472 @@ +//============================================================================= +// File: datetime.cpp +// Contents: Definitions for DwDateTime +// Maintainer: Doug Sauder <dwsauder@fwb.gulf.net> +// WWW: http://www.fwb.gulf.net/~dwsauder/mimepp.html +// +// Copyright (c) 1996, 1997 Douglas W. Sauder +// All rights reserved. +// +// IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT, +// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF +// THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER +// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT +// NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" +// BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// +//============================================================================= + +#define DW_IMPLEMENTATION + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include <mimelib/config.h> +#include <mimelib/debug.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <mimelib/string.h> +#include <mimelib/datetime.h> +#include <mimelib/token.h> +#include <time.h> + +static char lWeekDay[7][4] + = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +static char lMonth[12][4] + = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +extern "C" int ParseRfc822Date(const char *str, struct tm *tms, int *z); +extern "C" int ParseDate(const char *str, struct tm *tms, int *z); +static DwInt32 ymd_to_jdnl(int year, int mon, int day, int julian); +static void jdnl_to_ymd(DwInt32 jdn, int *year, int *mon, int *day, int julian); +static DwUint32 my_inv_gmtime(struct tm* ptms); + +const char* const DwDateTime::sClassName = "DwDateTime"; + + +int DwDateTime::sDefaultZone = 0; +int DwDateTime::sIsDefaultZoneSet = 0; +DwDateTime* (*DwDateTime::sNewDateTime)(const DwString&, + DwMessageComponent*) = 0; + + +DwDateTime* DwDateTime::NewDateTime(const DwString& aStr, + DwMessageComponent* aParent) +{ + if (sNewDateTime) { + return sNewDateTime(aStr, aParent); + } + else { + return new DwDateTime(aStr, aParent); + } +} + + +void DwDateTime::SetDefaultZone(int aZone) +{ + sDefaultZone = aZone; + sIsDefaultZoneSet = 1; +} + + +DwDateTime::DwDateTime() +{ + Init(); + mIsModified = 1; +} + + +DwDateTime::DwDateTime(const DwDateTime& aDateTime) + : DwFieldBody(aDateTime) +{ + mYear = aDateTime.mYear; + mMonth = aDateTime.mMonth; + mDay = aDateTime.mDay; + mHour = aDateTime.mHour; + mMinute = aDateTime.mMinute; + mSecond = aDateTime.mSecond; + mZone = aDateTime.mZone; +} + + +DwDateTime::DwDateTime(const DwString& aStr, DwMessageComponent* aParent) + : DwFieldBody(aStr, aParent) +{ + Init(); + mIsModified = 0; +} + + +void DwDateTime::Init() +{ + mClassId = kCidDateTime; + mClassName = DwDateTime::sClassName; + // Check if default time zone is set + if (sIsDefaultZoneSet == 0) { + // Use calls to gmtime() and localtime() to get the time difference + // between local time and UTC (GMT) time. + time_t t_now = time((time_t*) 0); +#if defined(HAVE_GMTIME_R) + struct tm utc; + gmtime_r(&t_now, &utc); + struct tm local; + localtime_r(&t_now, &local); +#else + struct tm utc = *gmtime(&t_now); + struct tm local = *localtime(&t_now); +#endif + DwUint32 t_local = my_inv_gmtime(&local); + DwUint32 t_utc = my_inv_gmtime(&utc); + sDefaultZone = (int) (t_local - t_utc)/60; + sIsDefaultZoneSet = 1; + } + // Set the time zone from the default time zone + mZone = sDefaultZone; + // Get the current calendar time + time_t t_now = time((time_t*) 0); + // Set year, month, day, hour, minute, and second from calendar time + _FromCalendarTime(t_now); +} + + +DwDateTime::~DwDateTime() +{ +} + + +const DwDateTime& DwDateTime::operator = (const DwDateTime& aDateTime) +{ + if (this == &aDateTime) return *this; + DwFieldBody::operator = (aDateTime); + mYear = aDateTime.mYear; + mMonth = aDateTime.mMonth; + mDay = aDateTime.mDay; + mHour = aDateTime.mHour; + mMinute = aDateTime.mMinute; + mSecond = aDateTime.mSecond; + mZone = aDateTime.mZone; + return *this; +} + + +DwUint32 DwDateTime::AsUnixTime() const +{ + struct tm tt; + tt.tm_year = mYear - 1900; + tt.tm_mon = mMonth - 1; + tt.tm_mday = mDay; + tt.tm_hour = mHour; + tt.tm_min = mMinute; + tt.tm_sec = mSecond; + DwUint32 t = my_inv_gmtime(&tt); + t = (t == (DwUint32) -1) ? 0 : t; + t -= mZone*60; + return t; +} + + +void DwDateTime::FromUnixTime(DwUint32 aTime) +{ + _FromUnixTime(aTime); + SetModified(); +} + + +void DwDateTime::_FromUnixTime(DwUint32 aTime) +{ + time_t t = aTime + mZone*60; +#if defined(HAVE_GMTIME_R) + struct tm tt; + gmtime_r(&t, &tt); +#else + struct tm tt = *gmtime(&t); +#endif + mYear = tt.tm_year + 1900; + mMonth = tt.tm_mon + 1; + mDay = tt.tm_mday; + mHour = tt.tm_hour; + mMinute = tt.tm_min; + mSecond = tt.tm_sec; +} + +void DwDateTime::FromCalendarTime(time_t aTime) +{ + _FromCalendarTime(aTime); + SetModified(); +} + + +void DwDateTime::_FromCalendarTime(time_t aTime) +{ + // Note: the broken-down time is the only portable representation. + // ANSI does not even require that time_t be an integer type; it could + // be a double. And, it doesn't even have to be in seconds. + + // Get the broken-down time. +#if defined(HAVE_GMTIME_R) + struct tm tms_utc; + gmtime_r(&aTime, &tms_utc); +#else + struct tm tms_utc = *gmtime(&aTime); +#endif + // Convert to UNIX time, using portable routine + DwUint32 t_unix = my_inv_gmtime(&tms_utc); + // Set from the UNIX time + _FromUnixTime(t_unix); +} + + +DwInt32 DwDateTime::DateAsJulianDayNum() const +{ + DwInt32 jdn = ymd_to_jdnl(mYear, mMonth, mDay, -1); + return jdn; +} + + +void DwDateTime::DateFromJulianDayNum(DwInt32 aJdn) +{ + jdnl_to_ymd(aJdn, &mYear, &mMonth, &mDay, -1); + SetModified(); +} + + +DwInt32 DwDateTime::TimeAsSecsPastMidnight() const +{ + DwInt32 n = mHour; + n *= 60; + n += mMinute; + n *= 60; + n += mSecond; + return n; +} + + +void DwDateTime::TimeFromSecsPastMidnight(DwInt32 aSecs) +{ + mSecond = (int) (aSecs % 60); + aSecs /= 60; + mMinute = (int) (aSecs % 60); + aSecs /= 60; + mHour = (int) (aSecs % 24); + SetModified(); +} + + +void DwDateTime::Parse() +{ + mIsModified = 0; + char buffer[80]; + char *str; + int mustDelete; + // Allocate memory from heap only in rare instances where the buffer + // is too small. + if (mString.length() >= 80) { + mustDelete = 1; + str = new char [mString.length()+1]; + } + else { + mustDelete = 0; + str = buffer; + } + strncpy(str, mString.data(), mString.length()); + str[mString.length()] = 0; + str[79] = 0; + struct tm tms; + int zone; + int err = ParseRfc822Date(str, &tms, &zone); + if ( err == -1 ) // try another format + err = ParseDate(str, &tms, &zone); + if (!err) { + mYear = tms.tm_year + 1900; + mMonth = tms.tm_mon+1; + mDay = tms.tm_mday; + mHour = tms.tm_hour; + mMinute = tms.tm_min; + mSecond = tms.tm_sec; + mZone = zone; + } + else /* if (err) */ { + mYear = 1970; + mMonth = 1; + mDay = 1; + mHour = 0; + mMinute = 0; + mSecond = 0; + mZone = 0; + } + if (mustDelete) { + delete[] str; + } +} + + +void DwDateTime::Assemble() +{ + if (!mIsModified) return; + // Find the day of the week + DwInt32 jdn = DateAsJulianDayNum(); + int dow = (int) ((jdn+1)%7); + char sgn = (mZone < 0) ? '-' : '+'; + int z = (mZone < 0) ? -mZone : mZone; + char buffer[80]; + snprintf(buffer, sizeof(buffer), "%s, %d %s %4d %02d:%02d:%02d %c%02d%02d", + lWeekDay[dow], mDay, lMonth[(mMonth-1)%12], mYear, + mHour, mMinute, mSecond, sgn, z/60%24, z%60); + mString = buffer; + mIsModified = 0; +} + + +DwMessageComponent* DwDateTime::Clone() const +{ + return new DwDateTime(*this); +} + + +#if defined (DW_DEBUG_VERSION) +void DwDateTime::PrintDebugInfo(std::ostream& aStrm, int /*aDepth*/) const +{ + aStrm << + "---------------- Debug info for DwDateTime class ---------------\n"; + _PrintDebugInfo(aStrm); +} +#else +void DwDateTime::PrintDebugInfo(std::ostream& , int) const {} +#endif // defined (DW_DEBUG_VERSION) + + +#if defined (DW_DEBUG_VERSION) +void DwDateTime::_PrintDebugInfo(std::ostream& aStrm) const +{ + DwFieldBody::_PrintDebugInfo(aStrm); + aStrm << "Date: " + << mYear << '-' << mMonth << '-' << mDay << ' ' + << mHour << ':' << mMinute << ':' << mSecond << ' ' + << mZone << '\n'; +} +#else +void DwDateTime::_PrintDebugInfo(std::ostream& ) const {} +#endif // defined (DW_DEBUG_VERSION) + + +void DwDateTime::CheckInvariants() const +{ +#if defined (DW_DEBUG_VERSION) + DwFieldBody::CheckInvariants(); + assert(mYear >= 0); + assert(1 <= mMonth && mMonth <= 12); + assert(1 <= mDay && mDay <= 31); + assert(0 <= mHour && mHour < 24); + assert(0 <= mMinute && mMinute < 60); + assert(0 <= mSecond && mSecond < 60); + assert(-12*60 <= mZone && mZone <= 12*60); +#endif // defined (DW_DEBUG_VERSION) +} + + +#ifdef PAPAL /* Pope Gregory XIII's decree */ +#define LASTJULDATE 15821004L /* last day to use Julian calendar */ +#define LASTJULJDN 2299160L /* jdn of same */ +#else /* British-American usage */ +#define LASTJULDATE 17520902L /* last day to use Julian calendar */ +#define LASTJULJDN 2361221L /* jdn of same */ +#endif + + +static DwInt32 ymd_to_jdnl(int year, int mon, int day, int julian) +{ + DwInt32 jdn; + + if (julian < 0) /* set Julian flag if auto set */ + julian = (((year * 100L) + mon) * 100 + day <= LASTJULDATE); + + if (year < 0) /* adjust BC year */ + year++; + + if (julian) + jdn = 367L * year - 7 * (year + 5001L + (mon - 9) / 7) / 4 + + 275 * mon / 9 + day + 1729777L; + else + jdn = (DwInt32)(day - 32075) + + 1461L * (year + 4800L + (mon - 14) / 12) / 4 + + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 + - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4; + + return jdn; +} + + +static void jdnl_to_ymd(DwInt32 jdn, int *year, int *mon, int *day, int julian) +{ + DwInt32 x, z, m, d, y; + DwInt32 daysPer400Years = 146097L; + DwInt32 fudgedDaysPer4000Years = 1460970L + 31; + + if (julian < 0) /* set Julian flag if auto set */ + julian = (jdn <= LASTJULJDN); + + x = jdn + 68569L; + if (julian) { + x += 38; + daysPer400Years = 146100L; + fudgedDaysPer4000Years = 1461000L + 1; + } + z = 4 * x / daysPer400Years; + x = x - (daysPer400Years * z + 3) / 4; + y = 4000 * (x + 1) / fudgedDaysPer4000Years; + x = x - 1461 * y / 4 + 31; + m = 80 * x / 2447; + d = x - 2447 * m / 80; + x = m / 11; + m = m + 2 - 12 * x; + y = 100 * (z - 49) + y + x; + + *year = (int)y; + *mon = (int)m; + *day = (int)d; + + if (*year <= 0) /* adjust BC years */ + (*year)--; +} + +#define JDN_JAN_1_1970 2440588L + +/* + * Converts broken-down time to time in seconds since 1 Jan 1970 00:00. + * Pays no attention to time zone or daylight savings time. Another way + * to think about this function is that it is the inverse of gmtime(). + * One word of caution: the values in the broken down time must be + * correct. + * + * This function is different from mktime() in three ways: + * 1. mktime() accepts a broken-down local time and converts it to a scalar + * UTC time. Thus, mktime() takes time zone and daylight savings time + * information into account when computing the scalar time. (This makes + * mktime() highly non-portable). + * 2. mktime() will adjust for non-standard values, such as a tm_mday member + * that is out of range. This function does no such conversion. + * 3. mktime() sets the struct fields tm_yday, tm_wday, and tm_isdst to + * their correct values on output. This function does not. + */ +static DwUint32 my_inv_gmtime(struct tm* ptms) +{ + DwInt32 jdn; + DwUint32 t; + + jdn = ymd_to_jdnl(ptms->tm_year+1900, ptms->tm_mon+1, + ptms->tm_mday, -1); + t = jdn - JDN_JAN_1_1970; /* days */ + t = 24*t + ptms->tm_hour; /* hours */ + t = 60*t + ptms->tm_min; /* minutes */ + t = 60*t + ptms->tm_sec; /* seconds */ + return t; +} + + |