查找给定的日期,即月份和月份 C/C++ 内"nth"出现的一天

Find the date given the year, the month and the "nth" occurrance of day within the month C/C++

本文关键字:C++ nth 一天 日期 查找      更新时间:2023-10-16

为了使设备(内存有限)能够管理自己的时区和夏令时,我试图根据每个时区的简化描述计算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;
}