日历程序的偏移 [英] Offset for a calendar program

查看:87
本文介绍了日历程序的偏移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

该程序采用自1753年以来的所有用户输入年份和月份,并为其创建日历.但是我在偏移量方面有问题,偏移量是本月开始的那一天.据我所知,只是偏移量关闭了,其他所有东西似乎都工作得很好. 这是我的代码.

This program takes any user input year since 1753 and month and creates a calendar for it. However I'm having issues with the offset which is the day the month starts out on. As far as I can tell it is just the offset that is off and everything else seems to work great. Here is my code.

#include <iostream>
#include <iomanip>
using namespace std;


int getMonth(int month);
int getYear(int year);
int computeOffset(int year, int month);
int numDaysYear(int year);
int numDaysMonth(int year, int month);
bool isLeapYear(int year);
void display(int year, int month, int offset);


/**********************************************************************
 * This function will call all the functions necessary to make a calendar
 * for any given month and year.
 ***********************************************************************/
int main()
{
   int numDays;
   int offset;
   int month;
   int year;

   month = getMonth(month);

   year = getYear(year);

   offset = computeOffset(year, month);

   display(year, month, offset);

   return 0;
}

/***********************************************************************
 * Gets the month number.
 **********************************************************************/
int getMonth(int month)
{
   cout << "Enter a month number: ";
   cin >> month;

   while ( month < 1 || month > 12)
   {
      cout << "Month must be between 1 and 12.\n"
           << "Enter a month number: ";
      cin >> month;
   }

   return month;
}

/***********************************************************************
 * Gets the year.
 **********************************************************************/
int getYear(int year)
{
   cout << "Enter year: ";
   cin >> year;

   while ( year < 1753)
   {
      cout << "Year must be 1753 or later.\n"
           << "Enter year: ";
      cin >> year;
   }
   return year;
}

/***********************************************************************
 * Computes the offset.
 **********************************************************************/

int computeOffset(int year, int month)
{
   int offset = 0;
   int count = year - 1753;
   for ( int iYear = 0; iYear < count; iYear++)
   {
      offset = ( offset + 365 + isLeapYear(year)) % 7;
   }

   for ( int iMonth = 1; iMonth < month; iMonth++)
   {
      offset = ( offset + numDaysMonth(year, iMonth)) % 7;
   }

   return offset;
}



/***********************************************************************
 * Computes the number of days in the given year.
 **********************************************************************/
int numDaysYear(int year)
{
   int daysYear = 365 + isLeapYear(year);
   return daysYear;
}

/***********************************************************************
 * Calculates the number of days in the given month.
 **********************************************************************/
int numDaysMonth(int year, int month)
{
   int daysMonth;

   if ( month == 1)
      daysMonth = 31;
   else if ( month == 2)
   {
      if (isLeapYear(year) == true)
         daysMonth = 29;
      else
         daysMonth = 28;
   }
   else if ( month == 3)
      daysMonth = 31;
   else if ( month == 4)
      daysMonth = 30;
   else if ( month == 5)
      daysMonth = 31;
   else if ( month == 6)
      daysMonth = 30;
   else if ( month == 7)
      daysMonth = 31;
   else if ( month == 8)
      daysMonth = 31;
   else if ( month == 9)
      daysMonth = 30;
   else if ( month == 10)
      daysMonth = 31;
   else if ( month == 11)
      daysMonth = 30;
   else if ( month == 12)
      daysMonth = 31;

   return daysMonth;
}

/***********************************************************************
 * Determines if given year is a leap year.
 **********************************************************************/
bool isLeapYear(int year)
{
   if ( year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
      return true;
   else
      return false;
}
 /**********************************************************************
 * Displays the calender table.
 **********************************************************************/
void display(int year, int month, int offset)
 {
    int dayOfWeek;
    int day;

    cout << endl;
    if ( month == 1)
       cout << "January";
    else if ( month == 2)
       cout << "February";
    else if ( month == 3)
       cout << "March";
    else if ( month == 4)
       cout << "April";
    else if ( month == 5)
       cout << "May";
    else if ( month == 6)
       cout << "June";
    else if ( month == 7)
       cout << "July";
    else if ( month == 8)
       cout << "August";
    else if ( month == 9)
       cout << "September";
    else if ( month == 10)
       cout << "October";
    else if ( month == 11)
       cout << "November";
    else if ( month == 12)
       cout << "December";


    cout << ", " << year << "\n";
    // Display month header
    cout << "  Su  Mo  Tu  We  Th  Fr  Sa\n";

    // Gets the correct offset width and end the line on the right
    //day of the week
    if (offset == 0)
    {
       day = 2;
       cout << setw(6);
    }
    else if (offset == 1)
    {
       day = 3;
       cout << setw(10);
    }
    else if (offset == 2)
    {
       day = 4;
       cout << setw(14);
    }
    else if (offset == 3)
    {
       day = 5;
       cout << setw(18);
    }
    else if (offset == 4)
    {
       day = 6;
       cout << setw(22);
    }
    else if (offset == 5)
    {
       day = 7;
       cout << setw(26);
    }
    else if (offset == 6)
    {
       day = 1;
       cout << setw(2);
    }
    else
       cout << "Error offset must be >= 0 and <=6\n";

    // The loop for displaying the days and ending the line in the right place
    for ( dayOfWeek = 1; dayOfWeek <= numDaysMonth(year, month); dayOfWeek++ )
    {
       cout << "  " <<  setw(2) << dayOfWeek;
       ++day;
       if (day == 8)
       {
          cout << "\n";
          day = 1;
       }
    }
    if ( day >= 2 && day <= 7)
       cout << "\n";

    return;
 }`

推荐答案

旧问题的新答案.新答案的理由:该领域中更好的工具和技术.

New answer for old question. Rationale for new answer: Better tools and technology in this area.

此答案大量使用了以下免费的,仅开放源代码的标头库 .我将从最高级别开始提供此答案,然后深入到较低级别的详细信息.但是,无论何时何地,我们都无需深入进行详细的日历计算. "date.h" 为我们处理.

This answer is heavily using this free, open-source header-only library. I'm going to present this answer starting at the highest level, and drilling down to the lower level details. But at all levels at no time do we have to get into detailed calendrical computations. "date.h" handles that for us.

这里是main:

#include "date.h"
#include <iomanip>
#include <ostream>
#include <string>
#include <iostream>

int
main()
{
    print_calendar_year(std::cout);
}

这只是对我的输出:

January 2016
  S  M  T  W  T  F  S
                 1  2
  3  4  5  6  7  8  9
 10 11 12 13 14 15 16
 17 18 19 20 21 22 23
 24 25 26 27 28 29 30
 31

February 2016
  S  M  T  W  T  F  S
     1  2  3  4  5  6
  7  8  9 10 11 12 13
 14 15 16 17 18 19 20
 21 22 23 24 25 26 27
 28 29

March 2016
  S  M  T  W  T  F  S
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30 31

April 2016
  S  M  T  W  T  F  S
                 1  2
  3  4  5  6  7  8  9
 10 11 12 13 14 15 16
 17 18 19 20 21 22 23
 24 25 26 27 28 29 30

May 2016
  S  M  T  W  T  F  S
  1  2  3  4  5  6  7
  8  9 10 11 12 13 14
 15 16 17 18 19 20 21
 22 23 24 25 26 27 28
 29 30 31

June 2016
  S  M  T  W  T  F  S
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30

July 2016
  S  M  T  W  T  F  S
                 1  2
  3  4  5  6  7  8  9
 10 11 12 13 14 15 16
 17 18 19 20 21 22 23
 24 25 26 27 28 29 30
 31

August 2016
  S  M  T  W  T  F  S
     1  2  3  4  5  6
  7  8  9 10 11 12 13
 14 15 16 17 18 19 20
 21 22 23 24 25 26 27
 28 29 30 31

September 2016
  S  M  T  W  T  F  S
              1  2  3
  4  5  6  7  8  9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26 27 28 29 30

October 2016
  S  M  T  W  T  F  S
                    1
  2  3  4  5  6  7  8
  9 10 11 12 13 14 15
 16 17 18 19 20 21 22
 23 24 25 26 27 28 29
 30 31

November 2016
  S  M  T  W  T  F  S
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30

December 2016
  S  M  T  W  T  F  S
              1  2  3
  4  5  6  7  8  9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26 27 28 29 30 31

可以使用以下命令打印出明年的日历:

One could print out next year's calendar with:

using namespace date::literals;
print_calendar_year(std::cout, 2017_y);

我将从以下语句开始:

这是一个类型安全系统.

文字2017_y是类型date::year的对象,而不是简单的整数.具有表示yearmonth的类型意味着将这些概念混淆的可能性要小得多.错误往往会在编译时被发现.

The literal 2017_y is an object of type date::year, not a simple integer. Having types that mean year and month means it is far less likely to mix up these concepts. Mistakes tend to be caught at compile time.

print_calendar_year非常简单:

void
print_calendar_year(std::ostream& os, date::year y = current_year())
{
    using namespace date;
    for (auto ym = y/jan; ym < y/jan + years{1}; ym += months{1})
    {
        print_calendar_month(os, ym);
        os << '\n';
    }
}

表达式year/month创建一个名为date::year_month的类型,它只不过是一个简单的结构{year, month}.因此,此函数只是简单地设置了一个循环,以从y年的1月到下一个1月(不包括下一个1月)进行迭代.这一切都非常容易理解.请注意,不允许使用裸int s".一切都有非整数类型.

The expression year/month creates a type called date::year_month which is nothing more than a simple struct {year, month}. So this function simply sets up a loop to iterate from Jan of the year y, to the next Jan, excluding the next Jan. It is all quite readable. And note that "bare ints" are not allowed. Everything has a non-integral type.

print_calendar_month是橡胶与道路的交汇处:

print_calendar_month is where the rubber meets the road:

void
print_calendar_month(std::ostream& os, date::year_month ym = current_year_month())
{
    using namespace std;
    using namespace date;
    os << format("%B %Y\n", sys_days{ym/1});
    os << "  S  M  T  W  T  F  S\n";
    auto wd = unsigned{weekday{ym/1}};
    os << string(wd*3, ' ');
    auto const e = (ym/last).day();
    for (day d = 1_d; d <= e; wd = 0)
    {
        for (; wd < 7 && d <= e; ++wd, ++d)
            os << setw(3) << unsigned{d};
        os << '\n';
    }
}

os << format("%B %Y\n", sys_days{ym/1});是每个月打印标题的内容(例如January 2016).这些是类似于strftime的格式设置标志,将遵循当前全局std::locale的本地化设置(与操作系统支持的一样多).

os << format("%B %Y\n", sys_days{ym/1}); is what prints out the title for each month (e.g. January 2016). These are strftime-like formatting flags that will respect the localization settings of the current global std::locale (as much as the OS supports).

子表达式ym/1创建类型date::year_month_day,该类型表示指定的月份和年份的第一天. date::year_month_day是持有{year, month, day}的简单类.

The subexpression ym/1 creates a type date::year_month_day which stands for the first day of the indicated month and year. date::year_month_day is a simply class holding {year, month, day}.

sys_days是基于system_clockchrono::time_point,精度为days. date::format可以采用任何精度system_clock time_point并使用类似于strftime的格式标记对其进行格式化.如图所示,可以将year_month_day转换为sys_days.这是从{year, month, day}字段类型到串行{count of days}类型的转换.

sys_days is a chrono::time_point based on system_clock with a precision of days. date::format can take any precision system_clock time_point and format it using strftime-like formatting flags. A year_month_day can be converted to a sys_days as shown. This is a conversion from a {year, month, day} field type to a serial {count of days} type.

os << " S M T W T F S\n";显然会打印出日历的星期几标题.

os << " S M T W T F S\n"; obviously prints out the day-of-the-week header for the calendar.

auto wd = unsigned{weekday{ym/1}};查找月份的第一天的星期几,并使用编码[Sun == 0, Sat == 6]将该weekday转换为unsigned. [注意: gcc需要语法unsigned(weekday{ym/1}).它不喜欢{}. — 尾注]

auto wd = unsigned{weekday{ym/1}}; finds the day of the week of the first day of the month and converts that weekday into an unsigned using the encoding [Sun == 0, Sat == 6]. [Note: gcc requires the syntax unsigned(weekday{ym/1}). It doesn't like the {} for unsigned. — end note]

os << string(wd*3, ' ');只是在每月的第一天之前每天打印出3个空格,以填充第一行.

os << string(wd*3, ' '); just prints out 3 spaces for each day before the first day of the month to pad out the first row.

auto const e = (ym/last).day();是类型为date::day的常量,它等于今年和月份组合的月份的最后一天.

auto const e = (ym/last).day(); is a constant of type date::day that is equal to the last day of the month for this year and month combination.

for (day d = 1_d; d <= e; wd = 0)

从第1天开始循环,直到一个月的最后一天(包括该天),然后将unsigned wd设置回每次迭代的星期日编码.

Starting with day 1 loop until the last day of the month (inclusive) and set the unsigned wd back to the encoding for Sunday on each iteration.

for (; wd < 7 && d <= e; ++wd, ++d):直到您到达星期末或月末,都增加星期几和月几.

for (; wd < 7 && d <= e; ++wd, ++d): Until you reach the end of the week or the end of the month, increment both day of the week and day of the month.

os << setw(3) << unsigned{d};:将月份中的日期转换为unsigned,并以3个空格的宽度a右对齐打印出来.

os << setw(3) << unsigned{d};: Convert the day of the month to an unsigned and print it out right-aligned in a width a of 3 spaces.

os << '\n';在打印完一周后返回.

os << '\n'; return after printing the week.

这就是程序的大部分!几乎所有棘手的日历逻辑都封装在以下两行代码中:

And that's the bulk of the program! Almost all of the tricky calendrical logic is encapsulated within these two lines of code:

auto wd = unsigned{weekday{ym/1}};
auto const e = (ym/last).day();

为完整起见,这里有获取当前date::year和当前date::year_month的函数:

For completeness here are the functions to get the current date::year and the current date::year_month:

date::year_month
current_year_month()
{
    using namespace std::chrono;
    using namespace date;
    year_month_day ymd = floor<days>(system_clock::now());
    return ymd.year()/ymd.month();
}

date::year
current_year()
{
    using namespace std::chrono;
    using namespace date;
    year_month_day ymd = floor<days>(system_clock::now());
    return ymd.year();
}

这两个方法都简单地使用floor将从system_clock::now()返回的system_clock::time_point截断为days的精度,然后将精度为time_point的日期转换为date::year_month_day类型.然后,此类型具有用于yearmonth的吸气剂,以选择所需的部分日历类型.

Both of these simply truncate a system_clock::time_point returned from system_clock::now() to a precision of days using floor, and then convert that days-precision time_point to a date::year_month_day type. This type then has getters for year and month to pick out the desired partial calendar types.

更新

好吧, TemplateRex 在下面问了一个我不想先回答的问题,然后我无法回答不能帮助自己,因为答案凸显了"date.h"的强大功能. ;-)

Well, TemplateRex asked a question below that I didn't want to answer at first, and then I couldn't help myself because the answer highlights how powerful "date.h" is to work with. ;-)

问题是:

您可以像这样以3x4格式打印日历吗?

Can you print out the calendars in a 3x4 format like this?

 January 2016            February 2016           March 2016          
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2        1  2  3  4  5  6           1  2  3  4  5
  3  4  5  6  7  8  9     7  8  9 10 11 12 13     6  7  8  9 10 11 12
 10 11 12 13 14 15 16    14 15 16 17 18 19 20    13 14 15 16 17 18 19
 17 18 19 20 21 22 23    21 22 23 24 25 26 27    20 21 22 23 24 25 26
 24 25 26 27 28 29 30    28 29                   27 28 29 30 31      
 31                                                                  

 April 2016              May 2016                June 2016           
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2     1  2  3  4  5  6  7              1  2  3  4
  3  4  5  6  7  8  9     8  9 10 11 12 13 14     5  6  7  8  9 10 11
 10 11 12 13 14 15 16    15 16 17 18 19 20 21    12 13 14 15 16 17 18
 17 18 19 20 21 22 23    22 23 24 25 26 27 28    19 20 21 22 23 24 25
 24 25 26 27 28 29 30    29 30 31                26 27 28 29 30      

 July 2016               August 2016             September 2016      
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2        1  2  3  4  5  6                 1  2  3
  3  4  5  6  7  8  9     7  8  9 10 11 12 13     4  5  6  7  8  9 10
 10 11 12 13 14 15 16    14 15 16 17 18 19 20    11 12 13 14 15 16 17
 17 18 19 20 21 22 23    21 22 23 24 25 26 27    18 19 20 21 22 23 24
 24 25 26 27 28 29 30    28 29 30 31             25 26 27 28 29 30   
 31                                                                  

 October 2016            November 2016           December 2016       
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                    1           1  2  3  4  5                 1  2  3
  2  3  4  5  6  7  8     6  7  8  9 10 11 12     4  5  6  7  8  9 10
  9 10 11 12 13 14 15    13 14 15 16 17 18 19    11 12 13 14 15 16 17
 16 17 18 19 20 21 22    20 21 22 23 24 25 26    18 19 20 21 22 23 24
 23 24 25 26 27 28 29    27 28 29 30             25 26 27 28 29 30 31
 30 31                                                               

很明显是这样,因为我不想手动输入以上所有内容! ;-)

Evidently so, because I wasn't about to type in all that above manually! ;-)

它需要重写print_calendar_year并引入几个新功能,最显着的是:

It requires a rewrite of print_calendar_year and the introduction of a a couple of new functions, most notably:

void
print_line_of_calendar_month(std::ostream& os, date::year_month ym, unsigned line,
                             date::weekday firstdow);

此功能仅打印与year_month ym关联的日历的一行,并且是此3x4格式的核心.

This function prints just one line of the calendar associated with the year_month ym and is the heart of this 3x4 format.

我还认为,使该程序可以本地化是很有趣的,这样可以打印出所需的第一周的第一天,以及月份和一周中某天的本地化名称(与std::locale在您的平台上允许).

I also thought it would be fun to make this program localizable so that the desired first-day-of-week could be printed out, as well as localized names for the month and day-of-week (as much as the std::locale on your platform allows).

行编号为[0,infinity].第0行会打印出月份年份,例如January 2016.第1行打印出星期几的标题:Su Mo Tu We Th Fr Sa.然后,第[2,infinity]行会打印出该月的某天.

The lines are numbered [0, infinity]. Line 0 prints out the month year such as January 2016. Line 1 prints out the day-of-week headers: Su Mo Tu We Th Fr Sa. And then lines [2, infinity] print out the days of the month.

为什么无穷大?

因为不同的月份使用不同的行数,所以我希望能够告诉year/month打印下一行,即使它不需要(因为该季度的另一个月份需要它).因此,当您要求日历打印出不需要的行时,它仅会输出适当数量的' '用于填充.

Because different months take different number of lines, so I wanted to be able to tell a year/month to print a next line even if it didn't need to (because another month in the quarter needed it). So when you ask for a calendar to print out a line that it doesn't need, it just outputs the proper number of ' ' for padding purposes.

足够的介绍,这里是功能:

Enough intro, here's the function:

void
print_line_of_calendar_month(std::ostream& os, date::year_month ym, unsigned line,
                             date::weekday firstdow)
{
    using namespace std;
    using namespace date;
    switch (line)
    {
    case 0:
        os << left << setw(21) << format(os.getloc(), " %B %Y", sys_days{ym/1}) << right;
        break;
    case 1:
        {
        auto sd = sys_days{ym/firstdow[1]};
        for (auto const esd = sd + weeks{1}; sd < esd; sd += days{1})
        {
            auto d = format(os.getloc(), "%a", sd);
            d.resize(2);
            os << ' ' << d;
        }
        break;
        }
    case 2:
        {
        auto wd = weekday{ym/1};         // This line and the next are the "offset"
        os << string((wd-firstdow).count()*3, ' '); // referred to in the question.
        auto d = 1_d;
        do
        {
            os << setw(3) << unsigned(d);
            ++d;
        } while (++wd != firstdow);
        break;
        }
    default:
        {
        unsigned index = line - 2;
        auto sd = sys_days{ym/1};
        if (weekday{sd} == firstdow)
            ++index;
        auto ymdw = ym/firstdow[index];
        if (ymdw.ok())
        {
            auto d = year_month_day{ymdw}.day();
            auto const e = (ym/last).day();
            auto wd = firstdow;
            do
            {
                os << setw(3) << unsigned(d);
            } while (++wd != firstdow && ++d <= e);
            os << string((firstdow-wd).count()*3, ' ');
        }
        else
            os << string(21, ' ');
        break;
        }
    }
}

因此打开行号[0,infinity],然后为每个行号做正确的事情:

So switch on line number [0, infinity], and for each line number, do the right thing:

0.打印月份年份标题.

这将传递到osformat locale以获取本地化的月份名称.

This passes to format the locale of the os to get the localized month name.

1.打印星期几的标题.

这将传递到osformat locale以获取本地化的工作日名称,并打印前2个字符. (不幸的是)只有当这些字符是多字节字符时,这才是大致正确的,但是本文主要是关于日历,而不是Unicode.

This passes to format the locale of the os to get the localized weekday names, and prints the first 2 characters. This is (unfortunately) only approximately correct when these are multi-byte characters, but this post is mostly about calendars, not Unicode.

2.打印出第一周,该周可能以空格为前缀.要加前缀的空格数为3 *(月初的天数超过一周的第一天).然后附加几天,直到到达一周的最后一天.请注意,工作日减法始终是7模,因此您不必担心星期几的基础编码.工作日形成一个循环范围.在一周中的所有天中循环播放时,确实需要遵循do-while的要求,而不是传统的for.

2. Print out the first week, which might be prefixed with spaces. The number of spaces to prefix with is 3*(number of days the first of the month is past the first day of the week). Then append days until you reach the last day of the week. Note that weekday subtraction is always modulo 7 so you don't have to worry about the underlying encoding of the days of the weeks. The weekdays form a circular range. This does require something along the lines of this do-while as opposed to a traditional for when looping over all the days in a week.

3 - infinity.啊,这是有趣的部分.

3 - infinity. Ah, here's the fun part.

"date.h"中有一种类型称为year_month_weekday,它是存储{year, month, weekday, unsigned}的类型.您可以通过以下方式指定母亲节:五月的第二个星期日:sun[2]/may/2016.该表达式创建一个结构{2016, 5, 0, 2}(大致而言).因此,如果switch降落在这里,那么我们正在寻找本月和年的[第一个,最后一个]周日,确切的索引取决于line,以及是否在网上打印了一个周日2.

There's a type in "date.h" called year_month_weekday which is a type storing {year, month, weekday, unsigned}. This is how you might specify Mother's day: The second Sunday of May: sun[2]/may/2016. This expression creates a struct {2016, 5, 0, 2} (roughly speaking). And so if the switch lands here, then we are looking for the [first, last] Sunday of this month and year, where the exact index is dependent upon line, and whether or not we printed a Sunday out on line 2.

也是键,该库允许使用任何索引:

Also key, this library allows any index to be used:

auto ymdw = ym/firstdow[index];

index可以为1,也可以为57.上面的行可以编译,而不是运行时错误.

index could be 1, or it could be 57. The above line compiles and is not a run-time error.

但是几个月不能有57个星期日(或星期一或其他日期)!

But months can't have 57 Sundays (or Mondays or whatever)!

没问题.您可以询问ym/firstdow[index]是否为有效日期.这是下一行的内容:

No problem. You can ask if ym/firstdow[index] is a valid date. This is what the next line does:

if (ymdw.ok())

如果日期有效,那么您需要做的工作.否则,您只需打印出空白行即可.

If the date is valid, then you've got work to do. Else you just print out a blank row.

如果有工作要做,请将year_month_weekday转换为year_month_day,以便从中获取日期(d).并找到该月的最后一天:

If we've got work to do, then convert the year_month_weekday to a year_month_day so that you can get the day of the month from it (d). And find the last day of the month:

auto const e = (ym/last).day();

然后从一周的第一天开始迭代,以月末或一周的最后一天为准.打印每个地点的当月日期.然后,如果您没有在一周的最后一天结束,请打印空格以填充到一周的最后一天.

Then iterate from the first day of the week to whichever comes first: the end of the month or the last day of the week. Print out the day of the month for each spot. And then if you didn't end on the last day of the week, print spaces to pad out to the last day of the week.

我们已经完成了print_line_of_calendar_month!请注意,从未在该级别上输出换行符.在此级别上甚至都不会输出月间填充.每个日历正好是21 char宽,并且可以打印到任意行.

And we're done with print_line_of_calendar_month! Note that newlines were never output on this level. Not even inter-month padding is output on this level. Each calendar is exactly 21 char wide, and can be printed out to an arbitrary number of rows.

现在我们需要另一个辅助工具:日历月开始填充空白行之前需要多少行?

Now we need another minor utility: What is the number of rows a calendar month needs before it starts padding with blank rows?

unsigned
number_of_lines_calendar(date::year_month ym, date::weekday firstdow)
{
    using namespace date;
    return ceil<weeks>((weekday{ym/1} - firstdow) +
                       ((ym/last).day() - day{0})).count() + 2;
}

这是一个月中的天数,再加上从一周的第一天到该月的第一天的天数,再加上两行表示星期几的标题和年份,每月标题.最后的小数部分会四舍五入!

This is the number of days in the month, plus the number of days from the first day of the week to the first of the month, plus 2 more rows for the day-of-the-week heading and the year-month heading. Fractional weeks at the end are rounded up!

注意:

  1. 从一周的第一天到一个月的第一天的天数就是:(weekday{ym/1} - firstdow).

一个月中的天数在此处编码为((ym/last).day() - day{0}).请注意,day{0}不是有效的day,但在减法中仍然有用:day - day给出chrono::duration days的结果.另一种说法是((ym/last).day() - day{1} + days{1}).

The number of days in the month is encoded here as ((ym/last).day() - day{0}). Note that day{0} is not a valid day, but can still be useful in the subtraction: day - day gives a result of the chrono::duration days. Another way to say this would have been ((ym/last).day() - day{1} + days{1}).

请注意,此处使用ceil<weeks>days的数量转换为weeks的数量,如果转换不精确,则四舍五入到下一个weeks. 1周== 1行.此汇总说明了在一周的最后一天之前结束的最后一周.

Note that ceil<weeks> is used here to convert the number of days to number of weeks, rounding up to the next weeks if the conversion is not exact. 1 week == 1 row. This roundup accounts for the last week that ends prior to the last day of the week.

现在print_calendar_year可以根据以下原语进行重写:

Now print_calendar_year can be rewritten in terms of these primitives:

void
print_calendar_year(std::ostream& os, unsigned const cols = 3,
                    date::year const y = current_year(),
                    date::weekday const firstdow = date::sun)
{
    using namespace date;
    if (cols == 0 || 12 % cols != 0)
        throw std::runtime_error("The number of columns " + std::to_string(cols)
                                 + " must be one of [1, 2, 3, 4, 6, 12]");
    // Compute number of lines needed for each calendar month
    unsigned ml[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    for (auto& m : ml)
        m = number_of_lines_calendar(y/month{m}, firstdow);
    for (auto r = 0u; r < 12/cols; ++r) // for each row
    {
        const auto lines = *std::max_element(std::begin(ml) + (r*cols),
                                             std::begin(ml) + ((r+1)*cols));
        for (auto l = 0u; l < lines; ++l) // for each line
        {
            for (auto c = 0u; c < cols; ++c) // for each column
            {
                if (c != 0)
                    os << "   ";
                print_line_of_calendar_month(os, y/month{r*cols + c+1}, l, firstdow);
            }
            os << '\n';
        }
        os << '\n';
    }
}

首先计算每个月需要多少行:

First compute for each month how many lines it needs:

// Compute number of lines needed for each calendar month
unsigned ml[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
for (auto& m : ml)
    m = number_of_lines_calendar(y/month{m}, firstdow);

然后对于每个日历行",通过搜索ml的适当子集来找到该行所需的行数.

Then for each "calendar row", find the number of lines needed for that row by searching the proper subset of ml.

然后,对于每一行,对于每个日历列",为该列打印出相应日历月的行.

And then for each line, and for each "calendar column", print out the line of the corresponding calendar month for that column.

在每行之后打印'\n'.

在每个日历行之后,打印'\n'.

After each calendar row, print a '\n'.

请注意,我们仍然不需要沉迷于日历计算中.在这个级别上,我们需要知道每周7天",每天3个空格"和每个日历行12/cols月".

Note that still at no time did we need to sink down into calendrical arithmetic. At this level we needed to know "7 days per week", "3 spaces per day" and "12/cols months per calendar row".

在macOS上,此驱动程序:

On macOS this driver:

using namespace date::literals;
std::cout.imbue(std::locale("de_DE"));
print_calendar_year(std::cout, 3, 2016_y, mon);

输出:

 Januar 2016             Februar 2016            März 2016          
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
              1  2  3     1  2  3  4  5  6  7        1  2  3  4  5  6
  4  5  6  7  8  9 10     8  9 10 11 12 13 14     7  8  9 10 11 12 13
 11 12 13 14 15 16 17    15 16 17 18 19 20 21    14 15 16 17 18 19 20
 18 19 20 21 22 23 24    22 23 24 25 26 27 28    21 22 23 24 25 26 27
 25 26 27 28 29 30 31    29                      28 29 30 31         

 April 2016              Mai 2016                Juni 2016           
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
              1  2  3                       1           1  2  3  4  5
  4  5  6  7  8  9 10     2  3  4  5  6  7  8     6  7  8  9 10 11 12
 11 12 13 14 15 16 17     9 10 11 12 13 14 15    13 14 15 16 17 18 19
 18 19 20 21 22 23 24    16 17 18 19 20 21 22    20 21 22 23 24 25 26
 25 26 27 28 29 30       23 24 25 26 27 28 29    27 28 29 30         
                         30 31                                       

 Juli 2016               August 2016             September 2016      
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
              1  2  3     1  2  3  4  5  6  7              1  2  3  4
  4  5  6  7  8  9 10     8  9 10 11 12 13 14     5  6  7  8  9 10 11
 11 12 13 14 15 16 17    15 16 17 18 19 20 21    12 13 14 15 16 17 18
 18 19 20 21 22 23 24    22 23 24 25 26 27 28    19 20 21 22 23 24 25
 25 26 27 28 29 30 31    29 30 31                26 27 28 29 30      

 Oktober 2016            November 2016           Dezember 2016       
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
                 1  2        1  2  3  4  5  6              1  2  3  4
  3  4  5  6  7  8  9     7  8  9 10 11 12 13     5  6  7  8  9 10 11
 10 11 12 13 14 15 16    14 15 16 17 18 19 20    12 13 14 15 16 17 18
 17 18 19 20 21 22 23    21 22 23 24 25 26 27    19 20 21 22 23 24 25
 24 25 26 27 28 29 30    28 29 30                26 27 28 29 30 31   
 31                                                                  

您的里程可能因std :: lib/OS支持本地化的程度而异.但是,现在您可以将日历打印成以月份为单位的月份(任意一年的12除数([1、2、3、4、6、12])之间的月份数,以任何一年为单位,以一周中的任意一天作为日历的第一天周,并使用任何语言环境(语言环境的模块化OS支持).

Your milage may vary on how well your std::lib/OS supports localization. But now you can print your calendar out in columns of months varying among any divisor of 12 ([1, 2, 3, 4, 6, 12]), using any year, using any day of the week as the first day of the week, and using any locale (modulo OS support for locales).

这是print_calendar_year(std::cout, 4, 2017_y);

 January 2017            February 2017           March 2017              April 2017          
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
  1  2  3  4  5  6  7              1  2  3  4              1  2  3  4                       1
  8  9 10 11 12 13 14     5  6  7  8  9 10 11     5  6  7  8  9 10 11     2  3  4  5  6  7  8
 15 16 17 18 19 20 21    12 13 14 15 16 17 18    12 13 14 15 16 17 18     9 10 11 12 13 14 15
 22 23 24 25 26 27 28    19 20 21 22 23 24 25    19 20 21 22 23 24 25    16 17 18 19 20 21 22
 29 30 31                26 27 28                26 27 28 29 30 31       23 24 25 26 27 28 29
                                                                         30                  

 May 2017                June 2017               July 2017               August 2017         
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
     1  2  3  4  5  6                 1  2  3                       1           1  2  3  4  5
  7  8  9 10 11 12 13     4  5  6  7  8  9 10     2  3  4  5  6  7  8     6  7  8  9 10 11 12
 14 15 16 17 18 19 20    11 12 13 14 15 16 17     9 10 11 12 13 14 15    13 14 15 16 17 18 19
 21 22 23 24 25 26 27    18 19 20 21 22 23 24    16 17 18 19 20 21 22    20 21 22 23 24 25 26
 28 29 30 31             25 26 27 28 29 30       23 24 25 26 27 28 29    27 28 29 30 31      
                                                 30 31                                       

 September 2017          October 2017            November 2017           December 2017       
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2     1  2  3  4  5  6  7              1  2  3  4                    1  2
  3  4  5  6  7  8  9     8  9 10 11 12 13 14     5  6  7  8  9 10 11     3  4  5  6  7  8  9
 10 11 12 13 14 15 16    15 16 17 18 19 20 21    12 13 14 15 16 17 18    10 11 12 13 14 15 16
 17 18 19 20 21 22 23    22 23 24 25 26 27 28    19 20 21 22 23 24 25    17 18 19 20 21 22 23
 24 25 26 27 28 29 30    29 30 31                26 27 28 29 30          24 25 26 27 28 29 30
                                                                         31                  

这篇关于日历程序的偏移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆