datetime.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,201 行 · 第 1/5 页

CPP
2,201
字号
///////////////////////////////////////////////////////////////////////////////
// Name:        wx/datetime.h
// Purpose:     implementation of time/date related classes
// Author:      Vadim Zeitlin
// Modified by:
// Created:     11.05.99
// RCS-ID:      $Id: datetime.cpp,v 1.136.2.1 2006/02/05 15:19:09 rgammans Exp $
// Copyright:   (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
//              parts of code taken from sndcal library by Scott E. Lee:
//
//               Copyright 1993-1995, Scott E. Lee, all rights reserved.
//               Permission granted to use, copy, modify, distribute and sell
//               so long as the above copyright and this permission statement
//               are retained in all copies.
//
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

// TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
//       the proper way to do it is to implement (subset of) wxStrftime() for
//       CE instead of this horror!!

/*
 * Implementation notes:
 *
 * 1. the time is stored as a 64bit integer containing the signed number of
 *    milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
 *    expressed in GMT.
 *
 * 2. the range is thus something about 580 million years, but due to current
 *    algorithms limitations, only dates from Nov 24, 4714BC are handled
 *
 * 3. standard ANSI C functions are used to do time calculations whenever
 *    possible, i.e. when the date is in the range Jan 1, 1970 to 2038
 *
 * 4. otherwise, the calculations are done by converting the date to/from JDN
 *    first (the range limitation mentioned above comes from here: the
 *    algorithm used by Scott E. Lee's code only works for positive JDNs, more
 *    or less)
 *
 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
 *    this moment in local time and may be converted to the object
 *    corresponding to the same date/time in another time zone by using
 *    ToTimezone()
 *
 * 6. the conversions to the current (or any other) timezone are done when the
 *    internal time representation is converted to the broken-down one in
 *    wxDateTime::Tm.
 */

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #pragma implementation "datetime.h"
#endif

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if !defined(wxUSE_DATETIME) || wxUSE_DATETIME

#ifndef WX_PRECOMP
    #include "wx/string.h"
    #include "wx/log.h"
#endif // WX_PRECOMP

#include "wx/intl.h"
#include "wx/thread.h"
#include "wx/tokenzr.h"
#include "wx/module.h"

#include <ctype.h>

#ifdef __WINDOWS__
    #include "wx/msw/wrapwin.h"
    #include <winnls.h>
    #ifndef __WXWINCE__
        #include <locale.h>
    #endif
#endif

#include "wx/datetime.h"
#include "wx/stopwatch.h"           // for wxGetLocalTimeMillis()

const long wxDateTime::TIME_T_FACTOR = 1000l;

#if wxUSE_EXTENDED_RTTI

template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
{
    data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
}

template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
{
    s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
}

wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)

#endif

//
// ----------------------------------------------------------------------------
// conditional compilation
// ----------------------------------------------------------------------------

#if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
        ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
    // glibc 2.0.7 strptime() is broken - the following snippet causes it to
    // crash (instead of just failing):
    //
    //      strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
    //      strptime(buf, "%x", &tm);
    //
    // so don't use it
    #undef HAVE_STRPTIME
#endif // broken strptime()

#if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
    // configure detects strptime as linkable because it's in the OS X
    // System library but MSL headers don't declare it.

//    char *strptime(const char *, const char *, struct tm *);
    // However, we DON'T want to just provide it here because we would
    // crash and/or overwrite data when strptime from OS X tries
    // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
    // So for now let's just say we don't have strptime
    #undef HAVE_STRPTIME
#endif

#if defined(__MWERKS__) && wxUSE_UNICODE
    #include <wtime.h>
#endif

#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
    #if defined(__WXPALMOS__)
        #define WX_GMTOFF_IN_TM
    #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
        #define WX_TIMEZONE _timezone
    #elif defined(__MWERKS__)
        long wxmw_timezone = 28800;
        #define WX_TIMEZONE wxmw_timezone
    #elif defined(__DJGPP__) || defined(__WINE__)
        #include <sys/timeb.h>
        #include <values.h>
        static long wxGetTimeZone()
        {
            static long timezone = MAXLONG; // invalid timezone
            if (timezone == MAXLONG)
            {
                struct timeb tb;
                ftime(&tb);
                timezone = tb.timezone;
            }
            return timezone;
        }
        #define WX_TIMEZONE wxGetTimeZone()
    #elif defined(__DARWIN__)
        #define WX_GMTOFF_IN_TM
    #else // unknown platform - try timezone
        #define WX_TIMEZONE timezone
    #endif
#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM

// ----------------------------------------------------------------------------
// macros
// ----------------------------------------------------------------------------

// debugging helper: just a convenient replacement of wxCHECK()
#define wxDATETIME_CHECK(expr, msg)     \
        if ( !(expr) )                  \
        {                               \
            wxFAIL_MSG(msg);            \
            *this = wxInvalidDateTime;  \
            return *this;               \
        }

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

class wxDateTimeHolidaysModule : public wxModule
{
public:
    virtual bool OnInit()
    {
        wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);

        return true;
    }

    virtual void OnExit()
    {
        wxDateTimeHolidayAuthority::ClearAllAuthorities();
        wxDateTimeHolidayAuthority::ms_authorities.clear();
    }

private:
    DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
};

IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// some trivial ones
static const int MONTHS_IN_YEAR = 12;

static const int SEC_PER_MIN = 60;

static const int MIN_PER_HOUR = 60;

static const int HOURS_PER_DAY = 24;

static const long SECONDS_PER_DAY = 86400l;

static const int DAYS_PER_WEEK = 7;

static const long MILLISECONDS_PER_DAY = 86400000l;

// this is the integral part of JDN of the midnight of Jan 1, 1970
// (i.e. JDN(Jan 1, 1970) = 2440587.5)
static const long EPOCH_JDN = 2440587l;

// the date of JDN -0.5 (as we don't work with fractional parts, this is the
// reference date for us) is Nov 24, 4714BC
static const int JDN_0_YEAR = -4713;
static const int JDN_0_MONTH = wxDateTime::Nov;
static const int JDN_0_DAY = 24;

// the constants used for JDN calculations
static const long JDN_OFFSET         = 32046l;
static const long DAYS_PER_5_MONTHS  = 153l;
static const long DAYS_PER_4_YEARS   = 1461l;
static const long DAYS_PER_400_YEARS = 146097l;

// this array contains the cumulated number of days in all previous months for
// normal and leap years
static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
{
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
};

// ----------------------------------------------------------------------------
// global data
// ----------------------------------------------------------------------------

const wxChar * wxDefaultDateTimeFormat = wxT("%c");
const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");

// in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
// indicate an invalid wxDateTime object
const wxDateTime wxDefaultDateTime;

wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;

// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------

// debugger helper: shows what the date really is
#ifdef __WXDEBUG__
extern const wxChar *wxDumpDate(const wxDateTime* dt)
{
    static wxChar buf[128];

    wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));

    return buf;
}
#endif // Debug

// get the number of days in the given month of the given year
static inline
wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
{
    // the number of days in month in Julian/Gregorian calendar: the first line
    // is for normal years, the second one is for the leap ones
    static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
    {
        { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
        { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    };

    return daysInMonth[wxDateTime::IsLeapYear(year)][month];
}

// returns the time zone in the C sense, i.e. the difference UTC - local
// (in seconds)
static int GetTimeZone()
{
#ifdef WX_GMTOFF_IN_TM
    // set to true when the timezone is set
    static bool s_timezoneSet = false;
    static long gmtoffset = LONG_MAX; // invalid timezone

    // ensure that the timezone variable is set by calling localtime
    if ( !s_timezoneSet )
    {
        // just call localtime() instead of figuring out whether this system
        // supports tzset(), _tzset() or something else
        time_t t = 0;
        struct tm *tm;

        tm = localtime(&t);
        s_timezoneSet = true;

        // note that GMT offset is the opposite of time zone and so to return
        // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
        // cases we have to negate it
        gmtoffset = -tm->tm_gmtoff;
    }

    return (int)gmtoffset;
#else // !WX_GMTOFF_IN_TM
    return (int)WX_TIMEZONE;
#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
}

// return the integral part of the JDN for the midnight of the given date (to
// get the real JDN you need to add 0.5, this is, in fact, JDN of the
// noon of the previous day)
static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
                            wxDateTime::Month mon,
                            int year)
{
    // CREDIT: code below is by Scott E. Lee (but bugs are mine)

    // check the date validity
    wxASSERT_MSG(
      (year > JDN_0_YEAR) ||
      ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
      ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
      _T("date out of range - can't convert to JDN")
                );

    // make the year positive to avoid problems with negative numbers division
    year += 4800;

    // months are counted from March here
    int month;
    if ( mon >= wxDateTime::Mar )
    {
        month = mon - 2;
    }
    else
    {
        month = mon + 10;
        year--;
    }

    // now we can simply add all the contributions together
    return ((year / 100) * DAYS_PER_400_YEARS) / 4
            + ((year % 100) * DAYS_PER_4_YEARS) / 4
            + (month * DAYS_PER_5_MONTHS + 2) / 5
            + day
            - JDN_OFFSET;
}

#ifndef __WXWINCE__
// this function is a wrapper around strftime(3) adding error checking
static wxString CallStrftime(const wxChar *format, const tm* tm)
{
    wxChar buf[4096];
    // Create temp wxString here to work around mingw/cygwin bug 1046059
    // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
    wxString s;

    if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
    {
        // buffer is too small?
        wxFAIL_MSG(_T("strftime() failed"));
    }

    s = buf;
    return s;
}
#endif

#ifdef HAVE_STRPTIME

// glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
// which, unfortunately, wreaks havoc elsewhere
#if defined(__GLIBC__) && (__GLIBC__ == 2)
    extern "C" char *strptime(const char *, const char *, struct tm *);
#endif

// Unicode-friendly strptime() wrapper
static const wxChar *
CallStrptime(const wxChar *input, const char *fmt, tm *tm)
{
    // the problem here is that strptime() returns pointer into the string we
    // passed to it while we're really interested in the pointer into the
    // original, Unicode, string so we try to transform the pointer back
#if wxUSE_UNICODE
    wxCharBuffer inputMB(wxConvertWX2MB(input));
#else // ASCII
    const char * const inputMB = input;
#endif // Unicode/Ascii

    const char *result = strptime(inputMB, fmt, tm);
    if ( !result )
        return NULL;

#if wxUSE_UNICODE
    // FIXME: this is wrong in presence of surrogates &c
    return input + (result - inputMB.data());
#else // ASCII
    return result;
#endif // Unicode/Ascii
}

#endif // HAVE_STRPTIME

// if year and/or month have invalid values, replace them with the current ones
static void ReplaceDefaultYearMonthWithCurrent(int *year,
                                               wxDateTime::Month *month)
{
    struct tm *tmNow = NULL;

    if ( *year == wxDateTime::Inv_Year )
    {
        tmNow = wxDateTime::GetTmNow();

        *year = 1900 + tmNow->tm_year;
    }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?