简体   繁体   English

查找给定的日期,月,月和月份中的“ n”次出现

[英]Find the date given the year, the month and the “nth” occurrance of day within the month C/C++

In order that a device (with limited memory) is able to manage its own timezone and daylight savings, I'm trying to calculate daylight savings triggers for 85 time zones based on a simplified description of each timezone. 为了使设备(内存有限)能够管理自己的时区和夏时制,我尝试根据每个时区的简化描述来计算85个时区的夏时制触发器。 I have access to minimal C and C++ libraries within the device. 我可以访问设备中的最小C和C ++库。 The format of the timezone (inc. DST) description for each time zone is as follows: 每个时区的时区描述格式(包括DST)如下:

  • UTC - the base time and date from system clock UTC-系统时钟的基准时间和日期
  • GMTOffsetMinutes - offset from GMT with DST inactive GMTOffsetMinutes-从DMT无效的GMT偏移
  • DSTDeltaMinutes - modifier to above with DST active (as applicable to TZ) DSTDeltaMinutes-启用DST时的上述修改器(适用于TZ)
  • DSTStartMonth - month in which DST becomes active DSTStartMonth-DST启用的月份
  • DSTStartNthOccurranceOfDay - the nth occurrence of the day name in month DSTStartNthOccurranceOfDay-日期名称在月份中的第n次出现
  • DSTDayOfWeek - Sun = 0 through to Sat = 6 DSTDayOfWeek-太阳= 0到星期六= 6
  • DSTStartHour - hour at which DST becomes active DSTStartHour-DST启用的小时数
  • DSTStartMinute - minute at which DST becomes active DSTStartMinute-DST激活的分钟
  • and corresponding EndMonth, EndNth..., EndHour, EndMinute 以及相应的EndMonth,EndNth ...,EndHour,EndMinute

I have found numerous examples going the other way, ie starting with the date, but they involve using the modulus, keeping the remainder and dropping the quotient hence I have been unable to transpose the formula to suit my needs. 我发现无数示例都从日期开始,但是它们涉及使用模数,保留余数并减去商,因此我无法调换公式以满足我的需要。

I also tried to reuse the standard "Jan = 6, Feb = 2, Mar = 2, Apr = 5, May = 0, etc. modifier table and year modifiers from the "tell me what day the 25th of June, 2067 is?" party trick and developed the following algorithm. 我还尝试重复使用标准“ 1月= 6、2月= 2、3月= 2、4月= 5、5月= 0,依此类推。”的修饰符表和年份修饰符来自“告诉我2067年6月25日是哪一天?方技巧并开发了以下算法。

Date = DayOfWeek + ((NthOccuranceOfDay - 1) x 7 ) - MonthCode - YearCode

This worked for the first 6 random test dates I selected but then I started to see dates for which it failed. 这对我选择的前6个随机测试日期有效,但随后我开始查看失败的日期。 Is it possible that the basic algorithm is sound but I'm missing a further modifier or maybe that I'm applying the modifiers incorrectly? 基本算法是否合理,但是我缺少进一步的修饰符,或者我错误地应用了修饰符,这可能吗?

Is there another solution I could utilize? 有没有我可以利用的其他解决方案?

Using this open source, cross platform date library , one can write: 使用这种开源的跨平台日期库 ,可以编写:

#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';
}

which will output: 将输出:

2015-03-08
2015-11-01

The formulas this library is based on are in the public domain and documented here . 该库所基于的公式在公共领域中,并在此处进行记录

The algorithms paper has very complete unit tests validating the date algorithms over a range of millions of years (a far larger range than is necessary). 该算法文件具有非常完整的单元测试,可在数百万年的范围内验证日期算法(远远超出了必需的范围)。

Sometimes daylight savings rules are written in terms of the last weekday of a month. 有时,夏令时规则是按照一个月的最后一个工作日来编写的。 That is just as easily handled: 这同样容易处理:

year_month_day ymd = sys_days(sun[last]/nov/2015);
std::cout << ymd << '\n';  // 2015-11-29

That formula will be off by one week (or even two weeks) if MonthCode + YearCode is greater than or equal to DayOfWeek , because in that case you will be counting NthOccurenceOfDay from a negative date. 如果MonthCode + YearCode大于或等于DayOfWeek ,则该公式将MonthCode一周(甚至两周)的时间,因为在这种情况下,您将从否定日期开始计算NthOccurenceOfDay

As an alternative, with no tables, you can compute the day of week of the first of the month using, for example, Zeller's algorithm : 作为替代方案,没有表格,您可以使用例如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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM