查找给定的日期,即月份和月份 C/C++ 内"nth"出现的一天
Find the date given the year, the month and the "nth" occurrance of day within the month C/C++
为了使设备(内存有限)能够管理自己的时区和夏令时,我试图根据每个时区的简化描述计算85个时区的夏令时触发器。我可以访问设备内的最小C和c++库。各时区的时区(包括夏令时)说明格式如下:
- UTC -来自系统时钟的基准时间和日期
- GMTOffsetMinutes -与GMT的偏移量
- dstdeltaminminutes -修改符为以上,DST有效(适用于TZ)
- DSTStartMonth -夏令时开始生效的月份
- dststartnthoccurrence ofday -日期名称在月份中出现的第n次
- DSTDayOfWeek - Sun = 0到Sat = 6
- dststarthh -夏令时开始生效的时间
- DSTStartMinute -夏令时开始活动的时间
- 和对应的EndMonth, EndNth…, EndHour, EndMinute
我发现了许多相反的例子,即从日期开始,但它们涉及使用模数,保留余数并去掉商,因此我无法转换公式以满足我的需要。
我还尝试重用标准的"Jan = 6, Feb = 2, Mar = 2, Apr = 5, May = 0等修饰语表和"告诉我2067年6月25日是几号?"派对技巧中的年份修饰语,并开发了以下算法。
Date = DayOfWeek + ((NthOccuranceOfDay - 1) x 7 ) - MonthCode - YearCode
这适用于我选择的前6个随机测试日期,但随后我开始看到它失败的日期。有没有可能基本算法是合理的,但我遗漏了一个进一步的修饰符,或者我错误地应用了修饰符?
我可以使用其他解决方案吗?
使用这个开源、跨平台的日期库,可以编写:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day us_daylight_starts = sys_days(sun[2]/mar/2015);
year_month_day us_daylight_ends = sys_days(sun[1]/nov/2015);
std::cout << us_daylight_starts << 'n';
std::cout << us_daylight_ends << 'n';
}
将输出:
2015-03-08
2015-11-01
此库所基于的公式属于公共领域,并在此记录。
算法论文有非常完整的单元测试,在数百万年的范围内验证日期算法(远远大于必要的范围)。
有时夏令时规则是根据一个月的最后一个工作日编写的。这很容易处理:
year_month_day ymd = sys_days(sun[last]/nov/2015);
std::cout << ymd << 'n'; // 2015-11-29
如果MonthCode
+ YearCode
大于或等于DayOfWeek
,该公式将偏离一周(甚至两周),因为在这种情况下,您将从负日期计算NthOccurenceOfDay
。
作为一种替代方法,在没有表的情况下,您可以使用Zeller的算法计算每月第一天的星期几:
int NthOccurrence(int year, int month, int n, int dayOfWeek) {
// year is the current year (eg. 2015)
// month is the target month (January == 1...December == 12)
// Finds the date of the nth dayOfWeek (Sun == 0...Sat == 6)
// Adjust month and year
if (month < 3) { --year, month += 12; }
// The gregorian calendar is a 400-year cycle
year = year % 400;
// There are no leap years in years 100, 200 and 300 of the cycle.
int century = year / 100;
int leaps = year / 4 - century;
// A normal year is 52 weeks and 1 day, so the calendar advances one day.
// In a leap year, it advances two days.
int advances = year + leaps;
// This is either magic or carefully contrived,
// depending on how you look at it:
int month_offset = (13 * (month + 1)) / 5;
// From which, we can compute the day of week of the first of the month:
int first = (month_offset + advances) % 7;
// If the dayOfWeek we're looking for is at least the day we just
// computed, we just add the difference. Otherwise, we need to add 7.
// Then we just add the desired number of weeks.
int offset = dayOfWeek - first;
if (offset < 0) offset += 7;
return 1 + offset + (n - 1) * 7;
}