unsigned firstDayOfWeek() { static unsigned firstDay = getFirstDayOfWeek(); return firstDay; }
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; } }
int main(int argc, const char* argv[]) { if(argc < 2) { printError(); return 0; } int iMonth = 0; int iYear = 0; sscanf(argv[1], "%d.%d", &iMonth, &iYear); if(iYear <= 1900 || iMonth > 12 || iMonth <= 0) { printError(); return 0; } // Set environment's default locale setlocale(LC_ALL, ""); // Init console colors HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo( hstdout, &csbi ); int iFirstWeekDay = getFirstDayOfWeek(); int iMonthStartWeekDay = getMonthStartDay(iYear, iMonth); int iDaysInMonth = getDaysInMonth(iYear, iMonth); wchar_t Buf[255]; int iRow = 0; bool bFillCalendar = false; int iCurrentWeekDay = iFirstWeekDay; int iCurrentMonthDay = 1; while(iCurrentMonthDay <= iDaysInMonth) { iCurrentWeekDay = iFirstWeekDay; for(int i = 0; i < 7; i++) { if(iRow == 0) { // Setting white color on black background SetConsoleTextAttribute(hstdout, 0xF0); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + iCurrentWeekDay, Buf, 255); wprintf(L"%4.3ls ", Buf); } else if(iRow == 1) { // Setting white color on black background SetConsoleTextAttribute(hstdout, 0x7F); if(iMonthStartWeekDay == iCurrentWeekDay) { bFillCalendar = true; } } // Filling blank space before and after selected month if(!bFillCalendar && iRow != 0) { wprintf(L"%5s", " "); } if(bFillCalendar) { // Setting days color if(iCurrentWeekDay == 5 || iCurrentWeekDay == 6) { // Sundays and saturdays are light red! SetConsoleTextAttribute(hstdout, 0x7C); } else { // Other days are white SetConsoleTextAttribute(hstdout, 0x71); } if(isToday(iYear, iMonth, iCurrentMonthDay)) { // Printing "today" in the brackets swprintf(Buf, L"[%2d]", iCurrentMonthDay); wprintf(L"%5.4s", Buf); } else { wprintf(L"%4d ", iCurrentMonthDay); } iCurrentMonthDay++; if(iCurrentMonthDay > iDaysInMonth) { bFillCalendar = false; } } if(iCurrentWeekDay == 6) { iCurrentWeekDay = 0; } else { iCurrentWeekDay++; } } SetConsoleTextAttribute(hstdout, csbi.wAttributes); wprintf(L"\r\n"); iRow++; } printf("Press any key to continue..."); getch(); return 0; }