计算星期几,终于

作者: Dave Taylor

与我们面临的许多挑战一样,最新的项目已经扩展到比我最初收到读者查询时预期的文章更多。问题看起来相当简单:给定月份、日期和星期几,计算与这些条件匹配的最近年份。

有一些晦涩和复杂的公式可以做到这一点,但是,我决定从当前年份倒退循环到所查询的月份,解析和分析方便的 cal 程序的输出,这会很有趣。

真正的挑战在于 cal 程序从未真正被设计为产生易于解析的输出,因此弄清楚星期几(DOW,我们一直在缩写它)基本上涉及计算前导空格的数量,或者以其他方式补偿一个平均月份,其中第一天从一周的中期开始,而不是整齐地从星期日开始。

cal 的算法友好版本将具有输出,其中月份的第一天之前的日期将可选地输出为零或下划线,这使得它更容易。但事实并非如此,因此我们必须进行补偿。

计算星期几

上次,我们完成了一个 shell 函数,该函数期望将日期、月份和年份作为参数,并返回该年该月该特定日期的星期几。换句话说,2011 年 5 月 16 日是星期一


      May 2011
Su Mo Tu We Th Fr Sa
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

此实例中函数的实际返回值是 2,因此 1 = 星期日,2 = 星期一,依此类推。

给定用户指定的期望星期几,以及一种简单的递减年份直到我们找到匹配项的方法,再加上已显示的函数,应该相对容易地将所有部分组合在一起,并最终创建——详细说明特定日期在特定星期几的脚本。

我不会重新发布之前帖子中的所有代码(完整的脚本有 83 行),但这是最后最突出的部分,即逐年回溯以找出哪个年份具有匹配日历条目的部分


echo Looking for $weekday, $day, $month \($monthnum\) \
    starting in $mostrecent 
# now we need to loop backwards through years until a match 
year=$mostrecent
DOW=-1                  # start with a dead value 
while [ $DOW -ne $desiredDOW ]
do
  figureDOW $day $monthnum $year
# echo "> $day $month occurred on a $DOW in $year"
  year=$(( $year - 1 ))
done 
echo "Got it! $day $month occurred on a $weekday 
 ↪most recently in ${year}:"
cal $month $year

请注意,当我们找到匹配项时,我们不仅会打印出哪个年份的该日期是指定的星期几,而且我们还会打印出该月的日历作为视觉确认。

一些示例运行说明


$ whatyear Friday February 9
Got it! 9 feb occurred on a fri most recently in 2006:

   February 2006
Su Mo Tu We Th Fr Sa
          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

$ whatyear wed aug 3
Got it! 3 aug occurred on a wed most recently in 2004:

    August 2004
Su Mo Tu We Th Fr Sa
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

由于我们将星期几的名称和月份名称都转换为小写,然后截断前三个字母后的任何内容,您可以看到“Friday”和“wed”都可以工作,这是一个不错的额外好处。具有更灵活输入选项的应用程序显然更受欢迎,并使每个人的生活更轻松。

仍然有些东西坏了

一个日期破坏了脚本,因为它不是每年都会出现:2 月 29 日。以下是问题的核心


$ cal feb 2010
   February 2010
Su Mo Tu We Th Fr Sa
    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”的匹配项时,没有匹配的输出,并且我们在脚本中拥有的条件测试无法处理空字符串。

这不好看


$ whatyear mon feb 29
./whatyear.sh: line 21: [: -eq: unary operator expected
./whatyear.sh: line 72: [: -ne: unary operator expected
Got it! 29 feb occurred on a mon most recently in 2010:

   February 2010
Su Mo Tu We Th Fr Sa
    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 号的二月匹配——太逊了。

但是,修复它可能比它值得的麻烦更多,并且肯定会让我们蔓延到后续文章中。相反,我鼓励您获取整个源代码库,并探索如何自己修复它。是的,我正在放弃!

下次,我将开始一个新的 shell 脚本编写挑战,并且像往常一样,我鼓励您给我发送一封简短的电子邮件,其中包含您对我们开发什么内容或您正在面临的任何特别有趣的脚本编写问题的一些想法。

Dave Taylor 在 UNIX 和 Linux 系统上编写 shell 脚本已经很长时间了。他是 Learning Unix for Mac OS XWicked Cool Shell Scripts 的作者。您可以在 Twitter 上找到他 @DaveTaylor,您可以通过他的技术问答网站联系他:Ask Dave Taylor

加载 Disqus 评论