datetime.cpp

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

CPP
2,201
字号
{
    wxASSERT_MSG( numWeek > 0,
                  _T("invalid week number: weeks are counted from 1") );

    // Jan 4 always lies in the 1st week of the year
    wxDateTime dt(4, Jan, year);
    dt.SetToWeekDayInSameWeek(wd);
    dt += wxDateSpan::Weeks(numWeek - 1);

    return dt;
}

// use a separate function to avoid warnings about using deprecated
// SetToTheWeek in GetWeek below
static wxDateTime
SetToTheWeek(int year,
             wxDateTime::wxDateTime_t numWeek,
             wxDateTime::WeekDay weekday,
             wxDateTime::WeekFlags flags)
{
    // Jan 4 always lies in the 1st week of the year
    wxDateTime dt(4, wxDateTime::Jan, year);
    dt.SetToWeekDayInSameWeek(weekday, flags);
    dt += wxDateSpan::Weeks(numWeek - 1);

    return dt;
}

bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
                              WeekDay weekday,
                              WeekFlags flags)
{
    int year = GetYear();
    *this = ::SetToTheWeek(year, numWeek, weekday, flags);
    if ( GetYear() != year )
    {
        // oops... numWeek was too big
        return false;
    }

    return true;
}

wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
                               WeekDay weekday,
                               WeekFlags flags) const
{
    return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
}

wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
                                          int year)
{
    // take the current month/year if none specified
    if ( year == Inv_Year )
        year = GetYear();
    if ( month == Inv_Month )
        month = GetMonth();

    return Set(GetNumOfDaysInMonth(year, month), month, year);
}

wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
{
    wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );

    int wdayDst = weekday,
        wdayThis = GetWeekDay();
    if ( wdayDst == wdayThis )
    {
        // nothing to do
        return *this;
    }

    if ( flags == Default_First )
    {
        flags = GetCountry() == USA ? Sunday_First : Monday_First;
    }

    // the logic below based on comparing weekday and wdayThis works if Sun (0)
    // is the first day in the week, but breaks down for Monday_First case so
    // we adjust the week days in this case
    if ( flags == Monday_First )
    {
        if ( wdayThis == Sun )
            wdayThis += 7;
        if ( wdayDst == Sun )
            wdayDst += 7;
    }
    //else: Sunday_First, nothing to do

    // go forward or back in time to the day we want
    if ( wdayDst < wdayThis )
    {
        return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
    }
    else // weekday > wdayThis
    {
        return Add(wxDateSpan::Days(wdayDst - wdayThis));
    }
}

wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
{
    wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );

    int diff;
    WeekDay wdayThis = GetWeekDay();
    if ( weekday == wdayThis )
    {
        // nothing to do
        return *this;
    }
    else if ( weekday < wdayThis )
    {
        // need to advance a week
        diff = 7 - (wdayThis - weekday);
    }
    else // weekday > wdayThis
    {
        diff = weekday - wdayThis;
    }

    return Add(wxDateSpan::Days(diff));
}

wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
{
    wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );

    int diff;
    WeekDay wdayThis = GetWeekDay();
    if ( weekday == wdayThis )
    {
        // nothing to do
        return *this;
    }
    else if ( weekday > wdayThis )
    {
        // need to go to previous week
        diff = 7 - (weekday - wdayThis);
    }
    else // weekday < wdayThis
    {
        diff = wdayThis - weekday;
    }

    return Subtract(wxDateSpan::Days(diff));
}

bool wxDateTime::SetToWeekDay(WeekDay weekday,
                              int n,
                              Month month,
                              int year)
{
    wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );

    // we don't check explicitly that -5 <= n <= 5 because we will return false
    // anyhow in such case - but may be should still give an assert for it?

    // take the current month/year if none specified
    ReplaceDefaultYearMonthWithCurrent(&year, &month);

    wxDateTime dt;

    // TODO this probably could be optimised somehow...

    if ( n > 0 )
    {
        // get the first day of the month
        dt.Set(1, month, year);

        // get its wday
        WeekDay wdayFirst = dt.GetWeekDay();

        // go to the first weekday of the month
        int diff = weekday - wdayFirst;
        if ( diff < 0 )
            diff += 7;

        // add advance n-1 weeks more
        diff += 7*(n - 1);

        dt += wxDateSpan::Days(diff);
    }
    else // count from the end of the month
    {
        // get the last day of the month
        dt.SetToLastMonthDay(month, year);

        // get its wday
        WeekDay wdayLast = dt.GetWeekDay();

        // go to the last weekday of the month
        int diff = wdayLast - weekday;
        if ( diff < 0 )
            diff += 7;

        // and rewind n-1 weeks from there
        diff += 7*(-n - 1);

        dt -= wxDateSpan::Days(diff);
    }

    // check that it is still in the same month
    if ( dt.GetMonth() == month )
    {
        *this = dt;

        return true;
    }
    else
    {
        // no such day in this month
        return false;
    }
}

static inline
wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
{
    return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
}

wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
{
    return GetDayOfYearFromTm(GetTm(tz));
}

wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
{
    if ( flags == Default_First )
    {
        flags = GetCountry() == USA ? Sunday_First : Monday_First;
    }

    Tm tm(GetTm(tz));
    wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);

    int wdTarget = GetWeekDay(tz);
    int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
    int week;
    if ( flags == Sunday_First )
    {
        // FIXME: First week is not calculated correctly.
        week = (nDayInYear - wdTarget + 7) / 7;
        if ( wdYearStart == Wed || wdYearStart == Thu )
            week++;
    }
    else // week starts with monday
    {
        // adjust the weekdays to non-US style.
        wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
        wdTarget = ConvertWeekDayToMondayBase(wdTarget);

        // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
        //
        //      Week 01 of a year is per definition the first week that has the
        //      Thursday in this year, which is equivalent to the week that
        //      contains the fourth day of January. In other words, the first
        //      week of a new year is the week that has the majority of its
        //      days in the new year. Week 01 might also contain days from the
        //      previous year and the week before week 01 of a year is the last
        //      week (52 or 53) of the previous year even if it contains days
        //      from the new year. A week starts with Monday (day 1) and ends
        //      with Sunday (day 7).
        //

        // if Jan 1 is Thursday or less, it is in the first week of this year
        if ( wdYearStart < 4 )
        {
            // count the number of entire weeks between Jan 1 and this date
            week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;

            // be careful to check for overflow in the next year
            if ( week == 53 && tm.mday - wdTarget > 28 )
                    week = 1;
        }
        else // Jan 1 is in the last week of the previous year
        {
            // check if we happen to be at the last week of previous year:
            if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
                week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
            else
                week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
        }
    }

    return (wxDateTime::wxDateTime_t)week;
}

wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
                                                    const TimeZone& tz) const
{
    Tm tm = GetTm(tz);
    wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
    int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
    if ( nWeek < 0 )
    {
        // this may happen for January when Jan, 1 is the last week of the
        // previous year
        nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
    }

    return (wxDateTime::wxDateTime_t)nWeek;
}

wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
{
    int year = GetYear();
    wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
                      _T("invalid year day") );

    bool isLeap = IsLeapYear(year);
    for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
    {
        // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
        // don't need it neither - because of the CHECK above we know that
        // yday lies in December then
        if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
        {
            Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);

            break;
        }
    }

    return *this;
}

// ----------------------------------------------------------------------------
// Julian day number conversion and related stuff
// ----------------------------------------------------------------------------

double wxDateTime::GetJulianDayNumber() const
{
    return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
}

double wxDateTime::GetRataDie() const
{
    // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
    return GetJulianDayNumber() - 1721119.5 - 306;
}

// ----------------------------------------------------------------------------
// timezone and DST stuff
// ----------------------------------------------------------------------------

int wxDateTime::IsDST(wxDateTime::Country country) const
{
    wxCHECK_MSG( country == Country_Default, -1,
                 _T("country support not implemented") );

    // use the C RTL for the dates in the standard range
    time_t timet = GetTicks();
    if ( timet != (time_t)-1 )
    {
        tm *tm = localtime(&timet);

        wxCHECK_MSG( tm, -1, _T("localtime() failed") );

        return tm->tm_isdst;
    }
    else
    {
        int year = GetYear();

        if ( !IsDSTApplicable(year, country) )
        {
            // no DST time in this year in this country
            return -1;
        }

        return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
    }
}

wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
{
    long secDiff = GetTimeZone() + tz.GetOffset();

    // we need to know whether DST is or not in effect for this date unless
    // the test disabled by the caller
    if ( !noDST && (IsDST() == 1) )
    {
        // FIXME we assume that the DST is always shifted by 1 hour
        secDiff -= 3600;
    }

    return Add(wxTimeSpan::Seconds(secDiff));
}

wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
{
    long secDiff = GetTimeZone() + tz.GetOffset();

    // we need to know whether DST is or not in effect for this date unless
    // the test disabled by the caller
    if ( !noDST && (IsDST() == 1) )
    {
        // FIXME we assume that the DST is always shifted by 1 hour
        secDiff -= 3600;
    }

    return Subtract(wxTimeSpan::Seconds(secDiff));
}

// ----------------------------------------------------------------------------
// wxDateTime to/from text representations
// ----------------------------------------------------------------------------

wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
{
    wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );

    // we have to use our own implementation if the date is out of range of
    // strftime() or if we use non standard specificators
    time_t time = GetTicks();
    if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
    {
        // use strftime()
        tm *tm;
        if ( tz.GetOffset() == -GetTimeZone() )
        {
            // we are working with local time
            tm = localtime(&time);

            // should never happen
            wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
        }
        else
        {
            time += (int)tz.GetOffset();

#if defined(__VMS__) || defined(__WATCOMC__) /

⌨️ 快捷键说明

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