查漏补缺——与日期有关的字符串
文章目录
- 一、日期相关的字符串
- 1.1基础问题
- 1.1.1判断闰年
- 1.1.2月份的规律
- 1.1.3字符串解析出年月日
- 1.1.4格式化日期格式
- 1.2进阶问题
- 1.2.1验证日期的合法性
- 1.2.2一年过了多少天
- 1.2.3日期加天数
- 1.2.4转换日期格式
- 1.2.3日期之间间隔多少天
一、日期相关的字符串
1.1基础问题
1.1.1判断闰年
闰年 指的是:1、能被400整除,2、或者能被4整除但不能被100整除。
bool isLeapYear(int year) {return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
}
要计算某一年的总天数,只需判断该年是否为闰年即可:
- 闰年有 366 天(2 月有 29 天)
- 平年有 365 天(2 月有 28 天)
int daysInYear(int year) {return isLeapYear(year) ? 366 : 365;
}
1.1.2月份的规律
月份的规律是:
- 1、3、5、7、8、10、12 月固定有 31 天
- 4、6、9、11 月固定有 30 天
- 2 月特殊:闰年 29 天,平年 28 天(通过isLeapYear函数判断)
int months[12]={31,28/29,31,30,31,30,31,31,30,31,30,31};
if(isLeapYear(year)) months[1]=29;int daysInMonth(int year, int month) {switch (month) {case 2:return isLeapYear(year) ? 29 : 28;case 4: case 6: case 9: case 11:return 30;default:return 31;}
}
1.1.3字符串解析出年月日
给定一个 “YYYY-MM-DD” 格式的日期,将年月日分离出来。
void parseDate(const string& date, int& year, int& month, int& day) {stringstream ss(date);char dash; // 用于接收分隔符 '-'ss >> year >> dash >> month >> dash >> day;
}
1.1.4格式化日期格式
#include<iomanip>
string formatDate(int year, int month, int day) {stringstream ss;ss << setw(4) << setfill('0') << year << "-"<< setw(2) << setfill('0') << month << "-"<< setw(2) << setfill('0') << day;return ss.str();
}
1.2进阶问题
序号 | 题目 | 链接 |
---|---|---|
1 | 转变日期格式——正规 | https://leetcode.cn/problems/reformat-date/description/?envType=problem-list-v2&envId=string |
2 | 转换日期格式——转换为二进制 | https://leetcode.cn/problems/convert-date-to-binary/description/?envType=problem-list-v2&envId=string |
3 | 一年中的第几天 | https://leetcode.cn/problems/day-of-the-year/description/?envType=problem-list-v2&envId=string |
4 | 日期之间隔多少天 | https://leetcode.cn/problems/number-of-days-between-two-dates/description/?envType=problem-list-v2&envId=string |
5 | 统计共同度过的日子 | https://leetcode.cn/problems/count-days-spent-together/description/?envType=problem-list-v2&envId=string |
1.2.1验证日期的合法性
验证一个日期是否合法,需要检查年、月、日三个部分是否都符合实际规则。
- 年份范围 [1900, 2100]
- 月份范围 [01, 12]
- 日期在当月的有效天数范围内(考虑闰年对 2 月的影响)
bool isValidDate(int year, int month, int day) {if (year < 1) return false;if (month < 1 || month > 12) return false;if (day < 1 || day > daysInMonth(year, month)) return false;return true;
}
1.2.2一年过了多少天
int dayOfYear(string date) {//判断每个月多少天int months[12]={31,28,31,30,31,30,31,31,30,31,30,31};//解析字符串int y,m,d;char dash;stringstream ss(date);ss >> y >> dash >> m >> dash >> d;if((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)) months[1]=29;//月*天数+dayint sum=0;for(int i=0;i<m-1;i++){sum+=months[i];}return sum+d;
}
1.2.3日期加天数
给定一个 “YYYY-MM-DD” 格式的日期和一个整数 N,计算 N 天后的日期,同样以 “YYYY-MM-DD” 格式返回。
情况 | 输入 | 输出 |
---|---|---|
当月内的简单加法 | 输入日期:2023-10-05,N=3 | 结果:2023-10-08 |
跨月 | 输入日期:2023-04-25,N=10 | 结果:2023-05-05 |
跨年 | 输入日期:2023-11-15,N=50 | 结果:2024-01-04 |
处理的核心逻辑:
- 判断累加后的日
- 没有超过当月的最大天数:无需处理
- 超过了当月的最大天数,跨月:减去当月的天数,进入下一个月
- 如果月份超过 12(即month>12),则年份加 1,月份重置为 1(进入下一年)。
string addDays(string date, int N) {//将日期分隔出年月日int year, month, day;parseDate(date, year, month, day);// 加上N天day += N;// 处理日期进位(月和年)while (true) {int daysInCurrentMonth = getDaysInMonth(year, month);// 日期在当前月范围内,无需继续进位if (day <= daysInCurrentMonth) { break; }// 减去当前月的天数,进入下个月day -= daysInCurrentMonth;month++;// 处理月份进位到年if (month > 12) {month = 1;year++;}}return formatDate(year, month, day);
}
1.2.4转换日期格式
给你一个字符串 date ,它的格式为 Day Month Year ,请你将字符串转变为 YYYY-MM-DD 的格式。其中:
- Day 是集合 {“1st”, “2nd”, “3rd”, “4th”, …, “30th”, “31st”} 中的一个元素。
- Month 是集合 {“Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”} 中的一个元素。
- Year 的范围在 [1900, 2100] 之间。
string reformatDate(string date) {unordered_map<string,string> mp={{"Jan", "01"},{"Feb", "02"},{"Mar", "03"},{"Apr", "04"},{"May", "05"},{"Jun", "06"},{"Jul", "07"},{"Aug", "08"},{"Sep", "09"},{"Oct", "10"}, {"Nov", "11"},{"Dec", "12"}};//解析字符串string year,month,day;stringstream ss(date);ss >> day >> month >> year;//处理日后面的thday.pop_back();day.pop_back();if(day.size() == 1) day="0"+day;//处理月份month=mp[month];return year+"-"+month+"-"+day;
}
给你一个字符串 date,它的格式为 yyyy-mm-dd,表示一个公历日期。date 可以重写为二进制表示,只需要将年、月、日分别转换为对应的二进制表示(不带前导零)并遵循 year-month-day 的格式。
//十进制转换为二进制
string decimalToBinary(int num) {if (num == 0) { return "0";}string binary;while (num > 0) {binary += (num % 2 == 0) ? '0' : '1'; // 取余数,转换为字符num /= 2;}// 反转字符串得到正确的二进制顺序reverse(binary.begin(), binary.end());return binary;
}
string convertDateToBinary(string date) {//解析字符串int year, month, day;stringstream ss(date);char dash;ss >> year >> dash >> month >> dash >> day;//将数字转换为二进制并连接起来//return decimalToBinary(year) + "-" + decimalToBinary(month) + "-" + decimalToBinary(day);return format("{}-{}-{}", decimalToBinary(year), decimalToBinary(month), decimalToBinary(day));
}
1.2.3日期之间间隔多少天
请你编写一个程序来计算两个日期之间隔了多少天。
- 将两个日期都转换为从一个固定起始点(如公元 1 年 1 月 1 日)开始的总天数
- 然后计算差值的绝对值。
// 计算从公元1年1月1日到给定日期的总天数
int totalDaysFromStart(int year, int month, int day) {int days = 0;// 累加完整年份的天数for (int y = 1; y < year; ++y) {days += isLeapYear(y) ? 366 : 365;}// 累加当月之前的月份天数for (int m = 1; m < month; ++m) {days += daysInMonth(year, m);}// 累加当月的天数(1月1日是第1天)days += day;return days;
}
// 计算两个日期之间的天数差
int daysBetweenDates(const string& date1, const string& date2) {int y1, m1, d1;int y2, m2, d2;parseDate(date1, y1, m1, d1);parseDate(date2, y2, m2, d2);int days1 = totalDaysFromStart(y1, m1, d1);int days2 = totalDaysFromStart(y2, m2, d2);return abs(days1 - days2);
}
统计两个人共同度过的日子数。日期都是 “MM-DD” 格式(不包含年份)
- 所有 “MM-DD” 格式的日期转换为当年的第几天
- 找到两个区间的重叠部分:重叠开始是两个开始日期的最大值,重叠结束是两个结束日期的最小值
- 计算重叠区间的天数:如果重叠结束 >= 重叠开始,则天数为重叠结束 - 重叠开始 + 1,否则为 0
int days[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
//计算该日期从01-01开始的总天数
int f(string s){int month,day;stringstream ss(s);char dash;ss >> month >> dash >> day;int sum=0;for(int i=0;i<month-1;i++){sum+=days[i];}sum+=day;return sum;
}
int countDaysTogether(string arriveAlice, string leaveAlice, string arriveBob, string leaveBob) {string a = arriveAlice < arriveBob ? arriveBob : arriveAlice;string b = leaveAlice < leaveBob ? leaveAlice : leaveBob;return max(0,f(b)-f(a)+1);
}
给定两个 “YYYY-MM-DD” 格式的日期,计算它们之间的工作日天数(周一至周五,不含法定节假日)。
- 基姆拉尔森计算公式【根据年月日计算出星期几】:
- 由于公式的计算逻辑基于 “1 月、2 月视为上一年的 13 月、14 月”(简化闰年对 2 月的影响)
- 公式:h = ( D + [13×(M + 1)÷5] + Y + [Y÷4] - [Y÷100] + [Y÷400] ) % 7
- 公式计算出的 h 并非直接对应 “周一至周日”,需按以下规则转换:
公式结果 h | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
实际星期 | 六 | 日 | 一 | 二 | 三 | 四 | 五 |
// 计算给定日期是星期几(0=星期日, 1=星期一, ..., 6=星期六)
int getWeekday(int year, int month, int day) {// 基姆拉尔森计算公式if (month < 3) {month += 12;year--;}int h = (day + 13*(month + 1)/5 + year + year/4 - year/100 + year/400) % 7;return (h == 0) ? 6 : h - 1; // 转换为0=星期日, 1=星期一...6=星期六
}// 计算从日期1到日期2的工作日天数(包含起止日期)
int workdaysBetweenDates(const string& date1, const string& date2) {// 解析两个日期int y1, m1, d1;int y2, m2, d2;parseDate(date1, y1, m1, d1);parseDate(date2, y2, m2, d2);// 确保date1 <= date2bool swapped = false;if (y1 > y2 || (y1 == y2 && m1 > m2) || (y1 == y2 && m1 == m2 && d1 > d2)) {swap(y1, y2);swap(m1, m2);swap(d1, d2);swapped = true;}int workdays = 0;int currentY = y1, currentM = m1, currentD = d1;// 遍历两个日期之间的每一天while (true) {// 检查是否到达结束日期if (currentY == y2 && currentM == m2 && currentD == d2) {// 检查当前日期是否为工作日(1-5为周一至周五)int weekday = getWeekday(currentY, currentM, currentD);if (weekday >= 1 && weekday <= 5) { workdays++; }break;}// 检查当前日期是否为工作日int weekday = getWeekday(currentY, currentM, currentD);if (weekday >= 1 && weekday <= 5) { workdays++; }// 移动到下一天currentD++;if (currentD > daysInMonth(currentY, currentM)) {currentD = 1;currentM++;if (currentM > 12) {currentM = 1;currentY++;}}}return workdays;
}