datetime.cpp

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

CPP
2,201
字号
    tm1.tm_min = minute;
    tm1.tm_sec = second;

    // and the DST in case it changes on this date
    struct tm tm2(tm1);
    mktime(&tm2);
    if ( tm2.tm_isdst != tm1.tm_isdst )
        tm1.tm_isdst = tm2.tm_isdst;

    (void)Set(tm1);

    // and finally adjust milliseconds
    return SetMillisecond(millisec);
}

wxDateTime& wxDateTime::Set(wxDateTime_t day,
                            Month        month,
                            int          year,
                            wxDateTime_t hour,
                            wxDateTime_t minute,
                            wxDateTime_t second,
                            wxDateTime_t millisec)
{
    wxDATETIME_CHECK( hour < 24 &&
                      second < 62 &&
                      minute < 60 &&
                      millisec < 1000,
                      _T("Invalid time in wxDateTime::Set()") );

    ReplaceDefaultYearMonthWithCurrent(&year, &month);

    wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
                      _T("Invalid date in wxDateTime::Set()") );

    // the range of time_t type (inclusive)
    static const int yearMinInRange = 1970;
    static const int yearMaxInRange = 2037;

    // test only the year instead of testing for the exact end of the Unix
    // time_t range - it doesn't bring anything to do more precise checks
    if ( year >= yearMinInRange && year <= yearMaxInRange )
    {
        // use the standard library version if the date is in range - this is
        // probably more efficient than our code
        struct tm tm;
        tm.tm_year = year - 1900;
        tm.tm_mon = month;
        tm.tm_mday = day;
        tm.tm_hour = hour;
        tm.tm_min = minute;
        tm.tm_sec = second;
        tm.tm_isdst = -1;       // mktime() will guess it

        (void)Set(tm);

        // and finally adjust milliseconds
        if (IsValid())
            SetMillisecond(millisec);

        return *this;
    }
    else
    {
        // do time calculations ourselves: we want to calculate the number of
        // milliseconds between the given date and the epoch

        // get the JDN for the midnight of this day
        m_time = GetTruncatedJDN(day, month, year);
        m_time -= EPOCH_JDN;
        m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;

        // JDN corresponds to GMT, we take localtime
        Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
    }

    return *this;
}

wxDateTime& wxDateTime::Set(double jdn)
{
    // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
    // EPOCH_JDN + 0.5
    jdn -= EPOCH_JDN + 0.5;

    m_time.Assign(jdn*MILLISECONDS_PER_DAY);

    // JDNs always are in UTC, so we don't need any adjustments for time zone

    return *this;
}

wxDateTime& wxDateTime::ResetTime()
{
    Tm tm = GetTm();

    if ( tm.hour || tm.min || tm.sec || tm.msec )
    {
        tm.msec =
        tm.sec =
        tm.min =
        tm.hour = 0;

        Set(tm);
    }

    return *this;
}

// ----------------------------------------------------------------------------
// DOS Date and Time Format functions
// ----------------------------------------------------------------------------
// the dos date and time value is an unsigned 32 bit value in the format:
// YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
//
// Y = year offset from 1980 (0-127)
// M = month (1-12)
// D = day of month (1-31)
// h = hour (0-23)
// m = minute (0-59)
// s = bisecond (0-29) each bisecond indicates two seconds
// ----------------------------------------------------------------------------

wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
{
    struct tm tm;
    InitTm(tm);

    long year = ddt & 0xFE000000;
    year >>= 25;
    year += 80;
    tm.tm_year = year;

    long month = ddt & 0x1E00000;
    month >>= 21;
    month -= 1;
    tm.tm_mon = month;

    long day = ddt & 0x1F0000;
    day >>= 16;
    tm.tm_mday = day;

    long hour = ddt & 0xF800;
    hour >>= 11;
    tm.tm_hour = hour;

    long minute = ddt & 0x7E0;
    minute >>= 5;
    tm.tm_min = minute;

    long second = ddt & 0x1F;
    tm.tm_sec = second * 2;

    return Set(mktime(&tm));
}

unsigned long wxDateTime::GetAsDOS() const
{
    unsigned long ddt;
    time_t ticks = GetTicks();
    struct tm *tm = localtime(&ticks);

    long year = tm->tm_year;
    year -= 80;
    year <<= 25;

    long month = tm->tm_mon;
    month += 1;
    month <<= 21;

    long day = tm->tm_mday;
    day <<= 16;

    long hour = tm->tm_hour;
    hour <<= 11;

    long minute = tm->tm_min;
    minute <<= 5;

    long second = tm->tm_sec;
    second /= 2;

    ddt = year | month | day | hour | minute | second;
    return ddt;
}

// ----------------------------------------------------------------------------
// time_t <-> broken down time conversions
// ----------------------------------------------------------------------------

wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    time_t time = GetTicks();
    if ( time != (time_t)-1 )
    {
        // use C RTL functions
        tm *tm;
        if ( tz.GetOffset() == -GetTimeZone() )
        {
            // we are working with local time
            tm = localtime(&time);

            // should never happen
            wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
        }
        else
        {
            time += (time_t)tz.GetOffset();
#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
            int time2 = (int) time;
            if ( time2 >= 0 )
#else
            if ( time >= 0 )
#endif
            {
                tm = gmtime(&time);

                // should never happen
                wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
            }
            else
            {
                tm = (struct tm *)NULL;
            }
        }

        if ( tm )
        {
            // adjust the milliseconds
            Tm tm2(*tm, tz);
            long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
            tm2.msec = (wxDateTime_t)(timeOnly % 1000);
            return tm2;
        }
        //else: use generic code below
    }

    // remember the time and do the calculations with the date only - this
    // eliminates rounding errors of the floating point arithmetics

    wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;

    long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();

    // we want to always have positive time and timeMidnight to be really
    // the midnight before it
    if ( timeOnly < 0 )
    {
        timeOnly = MILLISECONDS_PER_DAY + timeOnly;
    }

    timeMidnight -= timeOnly;

    // calculate the Gregorian date from JDN for the midnight of our date:
    // this will yield day, month (in 1..12 range) and year

    // actually, this is the JDN for the noon of the previous day
    long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;

    // CREDIT: code below is by Scott E. Lee (but bugs are mine)

    wxASSERT_MSG( jdn > -2, _T("JDN out of range") );

    // calculate the century
    long temp = (jdn + JDN_OFFSET) * 4 - 1;
    long century = temp / DAYS_PER_400_YEARS;

    // then the year and day of year (1 <= dayOfYear <= 366)
    temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
    long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
    long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;

    // and finally the month and day of the month
    temp = dayOfYear * 5 - 3;
    long month = temp / DAYS_PER_5_MONTHS;
    long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;

    // month is counted from March - convert to normal
    if ( month < 10 )
    {
        month += 3;
    }
    else
    {
        year += 1;
        month -= 9;
    }

    // year is offset by 4800
    year -= 4800;

    // check that the algorithm gave us something reasonable
    wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
    wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );

    // construct Tm from these values
    Tm tm;
    tm.year = (int)year;
    tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
    tm.mday = (wxDateTime_t)day;
    tm.msec = (wxDateTime_t)(timeOnly % 1000);
    timeOnly -= tm.msec;
    timeOnly /= 1000;               // now we have time in seconds

    tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
    timeOnly -= tm.sec;
    timeOnly /= SEC_PER_MIN;        // now we have time in minutes

    tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
    timeOnly -= tm.min;

    tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);

    return tm;
}

wxDateTime& wxDateTime::SetYear(int year)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    Tm tm(GetTm());
    tm.year = year;
    Set(tm);

    return *this;
}

wxDateTime& wxDateTime::SetMonth(Month month)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    Tm tm(GetTm());
    tm.mon = month;
    Set(tm);

    return *this;
}

wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    Tm tm(GetTm());
    tm.mday = mday;
    Set(tm);

    return *this;
}

wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    Tm tm(GetTm());
    tm.hour = hour;
    Set(tm);

    return *this;
}

wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    Tm tm(GetTm());
    tm.min = min;
    Set(tm);

    return *this;
}

wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    Tm tm(GetTm());
    tm.sec = sec;
    Set(tm);

    return *this;
}

wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
{
    wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );

    // we don't need to use GetTm() for this one
    m_time -= m_time % 1000l;
    m_time += millisecond;

    return *this;
}

// ----------------------------------------------------------------------------
// wxDateTime arithmetics
// ----------------------------------------------------------------------------

wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
{
    Tm tm(GetTm());

    tm.year += diff.GetYears();
    tm.AddMonths(diff.GetMonths());

    // check that the resulting date is valid
    if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
    {
        // We suppose that when adding one month to Jan 31 we want to get Feb
        // 28 (or 29), i.e. adding a month to the last day of the month should
        // give the last day of the next month which is quite logical.
        //
        // Unfortunately, there is no logic way to understand what should
        // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
        // We make it Feb 28 (last day too), but it is highly questionable.
        tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
    }

    tm.AddDays(diff.GetTotalDays());

    Set(tm);

    wxASSERT_MSG( IsSameTime(tm),
                  _T("Add(wxDateSpan) shouldn't modify time") );

    return *this;
}

// ----------------------------------------------------------------------------
// Weekday and monthday stuff
// ----------------------------------------------------------------------------

// convert Sun, Mon, ..., Sat into 6, 0, ..., 5
static inline int ConvertWeekDayToMondayBase(int wd)
{
    return wd == wxDateTime::Sun ? 6 : wd - 1;
}

/* static */
wxDateTime
wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)

⌨️ 快捷键说明

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