/********************************************************************* * @fn osal_ConvertUTCTime * * @brief Converts UTCTime to UTCTimeStruct * * @param tm - pointer to breakdown struct * * @param secTime - number of seconds since 0 hrs, 0 minutes, * 0 seconds, on the 1st of January 2000 UTC * * @return none */ void osal_ConvertUTCTime( UTCTimeStruct *tm, UTCTime secTime ) { // calculate the time less than a day - hours, minutes, seconds { uint32 day = secTime % DAY; tm->seconds = day % 60UL; tm->minutes = (day % 3600UL) / 60UL; tm->hour = day / 3600UL; } // Fill in the calendar - day, month, year { uint16 numDays = secTime / DAY; tm->year = BEGYEAR; while ( numDays >= YearLength( tm->year ) ) { numDays -= YearLength( tm->year ); tm->year++; } tm->month = 0; while ( numDays >= monthLength( IsLeapYear( tm->year ), tm->month ) ) { numDays -= monthLength( IsLeapYear( tm->year ), tm->month ); tm->month++; } tm->day = numDays; } }
/********************************************************************* * @fn osal_ConvertUTCSecs * * @brief Converts a UTCTimeStruct to UTCTime * * @param tm - pointer to provided struct * * @return number of seconds since 00:00:00 on 01/01/2000 (UTC) */ UTCTime osal_ConvertUTCSecs(UTCTimeStruct *tm) { uint32 seconds; /* Seconds for the partial day */ seconds = (((tm->hour * 60UL) + tm->minutes) * 60UL) + tm->seconds; /* Account for previous complete days */ { /* Start with complete days in current month */ uint16 days = tm->day; /* Next, complete months in current year */ { int8 month = tm->month; // while (--month >= 0) { // modify by xyz - 2014.10.31 while (--month > 0) { days += monthLength(IsLeapYear(tm->year), month); } } /* Next, complete years before current year */ { uint16 year = tm->year; while (--year >= BEGYEAR) { days += YearLength(year); } } /* Add total seconds before partial day */ seconds += (days * DAY); } return (seconds); }
/** * After adjustments such as add(MONTH), add(YEAR), we don't want the * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar * 3, we want it to go to Feb 28. Adjustments which might run into this * problem call this method to retain the proper month. */ void GregorianCalendar::pinDayOfMonth() { int32_t monthLen = monthLength(internalGet(UCAL_MONTH)); int32_t dom = internalGet(UCAL_DATE); if(dom > monthLen) set(UCAL_DATE, monthLen); }
int32_t Grego::dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom) { int32_t weekInMonth = (dom + 6)/7; if (weekInMonth == 4) { if (dom + 7 > monthLength(year, month)) { weekInMonth = -1; } } else if (weekInMonth == 5) { weekInMonth = -1; } return weekInMonth; }
Date::Date(Day d, Month m, Year y) { QL_REQUIRE(y > 1900 && y < 2200, "year " << y << " out of bound. It must be in [1901,2199]"); QL_REQUIRE(Integer(m) > 0 && Integer(m) < 13, "month " << Integer(m) << " outside January-December range [1,12]"); bool leap = isLeap(y); Day len = monthLength(m,leap), offset = monthOffset(m,leap); QL_REQUIRE(d <= len && d > 0, "day outside month (" << Integer(m) << ") day-range " << "[1," << len << "]"); serialNumber_ = d + offset + yearOffset(y); }
Date Date::advance(const Date& date, Integer n, TimeUnit units) { switch (units) { case Days: return date + n; case Weeks: return date + 7*n; case Months: { Day d = date.dayOfMonth(); Integer m = Integer(date.month())+n; Year y = date.year(); while (m > 12) { m -= 12; y += 1; } while (m < 1) { m += 12; y -= 1; } QL_ENSURE(y >= 1900 && y <= 2199, "year " << y << " out of bounds. " << "It must be in [1901,2199]"); Integer length = monthLength(Month(m), isLeap(y)); if (d > length) d = length; return Date(d, Month(m), y); } case Years: { Day d = date.dayOfMonth(); Month m = date.month(); Year y = date.year()+n; QL_ENSURE(y >= 1900 && y <= 2199, "year " << y << " out of bounds. " << "It must be in [1901,2199]"); if (d == 29 && m == February && !isLeap(y)) d = 28; return Date(d,m,y); } default: QL_FAIL("undefined time units"); } }
TimeFrame::TimeFrame() { int i = 0,j = 0; for(int I = 1; I <= 12;I++) { int dim = monthLength(I); for(int J = 1;J<=dim;J++) { j++; for(int K=1;K<=24;K++) { assert(i < TIMESLICES); Hour[i] = K; Day[i] = J; Month[i] = I; YTD[i++] = j; } } } }
UBool GregorianCalendar::validateFields() const { for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) { // Ignore DATE and DAY_OF_YEAR which are handled below if (field != UCAL_DATE && field != UCAL_DAY_OF_YEAR && isSet((UCalendarDateFields)field) && ! boundsCheck(internalGet((UCalendarDateFields)field), (UCalendarDateFields)field)) return FALSE; } // Values differ in Least-Maximum and Maximum should be handled // specially. if (isSet(UCAL_DATE)) { int32_t date = internalGet(UCAL_DATE); if (date < getMinimum(UCAL_DATE) || date > monthLength(internalGet(UCAL_MONTH))) { return FALSE; } } if (isSet(UCAL_DAY_OF_YEAR)) { int32_t days = internalGet(UCAL_DAY_OF_YEAR); if (days < 1 || days > yearLength()) { return FALSE; } } // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero. // We've checked against minimum and maximum above already. if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) && 0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) { return FALSE; } return TRUE; }
void GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) { if((amount == 0) || U_FAILURE(status)) { return; } // J81 processing. (gregorian cutover) UBool inCutoverMonth = FALSE; int32_t cMonthLen=0; // 'c' for cutover; in days int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen) double cMonthStart=0.0; // in ms // Common code - see if we're in the cutover month of the cutover year if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) { switch (field) { case UCAL_DAY_OF_MONTH: case UCAL_WEEK_OF_MONTH: { int32_t max = monthLength(internalGet(UCAL_MONTH)); UDate t = internalGetTime(); // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an // additional 10 if we are after the cutover. Thus the monthStart // value will be correct iff we actually are in the cutover month. cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0); cMonthStart = t - ((cDayOfMonth - 1) * kOneDay); // A month containing the cutover is 10 days shorter. if ((cMonthStart < fGregorianCutover) && (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) { inCutoverMonth = TRUE; } } default: ; } } switch (field) { case UCAL_WEEK_OF_YEAR: { // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the // week. Also, rolling the week of the year can have seemingly // strange effects simply because the year of the week of year // may be different from the calendar year. For example, the // date Dec 28, 1997 is the first day of week 1 of 1998 (if // weeks start on Sunday and the minimal days in first week is // <= 3). int32_t woy = get(UCAL_WEEK_OF_YEAR, status); // Get the ISO year, which matches the week of year. This // may be one year before or after the calendar year. int32_t isoYear = get(UCAL_YEAR_WOY, status); int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR); if (internalGet(UCAL_MONTH) == UCAL_JANUARY) { if (woy >= 52) { isoDoy += handleGetYearLength(isoYear); } } else { if (woy == 1) { isoDoy -= handleGetYearLength(isoYear - 1); } } woy += amount; // Do fast checks to avoid unnecessary computation: if (woy < 1 || woy > 52) { // Determine the last week of the ISO year. // We do this using the standard formula we use // everywhere in this file. If we can see that the // days at the end of the year are going to fall into // week 1 of the next year, we drop the last week by // subtracting 7 from the last day of the year. int32_t lastDoy = handleGetYearLength(isoYear); int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek()) % 7; if (lastRelDow < 0) lastRelDow += 7; if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7; int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1); woy = ((woy + lastWoy - 1) % lastWoy) + 1; } set(UCAL_WEEK_OF_YEAR, woy); set(UCAL_YEAR_WOY,isoYear); return; } case UCAL_DAY_OF_MONTH: if( !inCutoverMonth ) { Calendar::roll(field, amount, status); return; } else { // [j81] 1582 special case for DOM // The default computation works except when the current month // contains the Gregorian cutover. We handle this special case // here. [j81 - aliu] double monthLen = cMonthLen * kOneDay; double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart + amount * kOneDay, monthLen); if (msIntoMonth < 0) { msIntoMonth += monthLen; } #if defined (U_DEBUG_CAL) fprintf(stderr, "%s:%d: roll DOM %d -> %.0lf ms \n", __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth); #endif setTimeInMillis(cMonthStart + msIntoMonth, status); return; } case UCAL_WEEK_OF_MONTH: if( !inCutoverMonth ) { Calendar::roll(field, amount, status); return; } else { #if defined (U_DEBUG_CAL) fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n", __FILE__, __LINE__,amount); #endif // NOTE: following copied from the old // GregorianCalendar::roll( WEEK_OF_MONTH ) code // This is tricky, because during the roll we may have to shift // to a different day of the week. For example: // s m t w r f s // 1 2 3 4 5 // 6 7 8 9 10 11 12 // When rolling from the 6th or 7th back one week, we go to the // 1st (assuming that the first partial week counts). The same // thing happens at the end of the month. // The other tricky thing is that we have to figure out whether // the first partial week actually counts or not, based on the // minimal first days in the week. And we have to use the // correct first day of the week to delineate the week // boundaries. // Here's our algorithm. First, we find the real boundaries of // the month. Then we discard the first partial week if it // doesn't count in this locale. Then we fill in the ends with // phantom days, so that the first partial week and the last // partial week are full weeks. We then have a nice square // block of weeks. We do the usual rolling within this block, // as is done elsewhere in this method. If we wind up on one of // the phantom days that we added, we recognize this and pin to // the first or the last day of the month. Easy, eh? // Another wrinkle: To fix jitterbug 81, we have to make all this // work in the oddball month containing the Gregorian cutover. // This month is 10 days shorter than usual, and also contains // a discontinuity in the days; e.g., the default cutover month // is Oct 1582, and goes from day of month 4 to day of month 15. // Normalize the DAY_OF_WEEK so that 0 is the first day of the week // in this locale. We have dow in 0..6. int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); if (dow < 0) dow += 7; // Find the day of month, compensating for cutover discontinuity. int32_t dom = cDayOfMonth; // Find the day of the week (normalized for locale) for the first // of the month. int32_t fdm = (dow - dom + 1) % 7; if (fdm < 0) fdm += 7; // Get the first day of the first full week of the month, // including phantom days, if any. Figure out if the first week // counts or not; if it counts, then fill in phantom days. If // not, advance to the first real full week (skip the partial week). int32_t start; if ((7 - fdm) < getMinimalDaysInFirstWeek()) start = 8 - fdm; // Skip the first partial week else start = 1 - fdm; // This may be zero or negative // Get the day of the week (normalized for locale) for the last // day of the month. int32_t monthLen = cMonthLen; int32_t ldm = (monthLen - dom + dow) % 7; // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. // Get the limit day for the blocked-off rectangular month; that // is, the day which is one past the last day of the month, // after the month has already been filled in with phantom days // to fill out the last week. This day has a normalized DOW of 0. int32_t limit = monthLen + 7 - ldm; // Now roll between start and (limit - 1). int32_t gap = limit - start; int32_t newDom = (dom + amount*7 - start) % gap; if (newDom < 0) newDom += gap; newDom += start; // Finally, pin to the real start and end of the month. if (newDom < 1) newDom = 1; if (newDom > monthLen) newDom = monthLen; // Set the DAY_OF_MONTH. We rely on the fact that this field // takes precedence over everything else (since all other fields // are also set at this point). If this fact changes (if the // disambiguation algorithm changes) then we will have to unset // the appropriate fields here so that DAY_OF_MONTH is attended // to. // If we are in the cutover month, manipulate ms directly. Don't do // this in general because it doesn't work across DST boundaries // (details, details). This takes care of the discontinuity. setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status); return; } default: Calendar::roll(field, amount, status); return; } }
inline bool Date::isEndOfMonth(const Date& d) { return (d.dayOfMonth() == monthLength(d.month(), isLeap(d.year()))); }
inline Date Date::endOfMonth(const Date& d) { Month m = d.month(); Year y = d.year(); return Date(monthLength(m, isLeap(y)), m, y); }