【数据结构算法笔记】----日期差值处理(平年闰年处理、大月小月处理、二月的天数)

题目描述: 有两个日期,求两个日期之间的天数,如果两个日期是连续的,则规定他们之间的天数为两天。输入格式:YYYYMMDD

具体思路:不妨假设第一个日期早于第二个日期(否则交换即可)。这种求日期之间相差天数的题目有一个很直接的思路,即令日期不断加1天,直到第一个日期等于第二个日期为止,即可统计出答案。具体处理时,如果当加了一天之后天数d等于当前月份m所拥有的天数加1,那么就令月份m加1、同时置天数d为1号(即把日期变为下一个月的1号); 如果此时月份m变为了13,那么就令年份y加1、同时置月份m为1月 (即把日期变为下一年的1月)。
      为了方便直接取出每个月的天数,不妨给定一个二维数组int month[13][2],用来存放每个月的天数,其中第二维为0时表示平年,为1时表示闰年。


注意: 如果想要加快速度,只需要先把第一个日期的年份不断加1,直到与第二个日期的年份相差1为止,期间根据平年或是闰年来累加365天或者366天即可。之后再进行不断令天数加1的操作。

​
#include<stdio.h>
int mouth [13][2]={{0,0},{31,31},{28,29},{31,31},{30,30},{31,31},{30,30},
    {31,31},{31,31},{30,30},{31,31},{30,30},{31,31}}; /*初始值为0,{平年,闰年}的区别*/
bool isrun(int year){
    return (year%4==0&&year%100!=0)||(year%400==0);}/*判断某年是否为闰年,返回bool值*/ 
int main(){
	int y1,m1,d1;
	int y2,m2,d2;
	int time1,time2;
	while(scanf("%d%d",&time1,&time2)!=EOF){/*输入的值正确,才进行以下操作*/
	  
		if(time1>time2){  /*因为计算差值是day+1的方式,则time1必须小于time2*/
	    	int t=time1;
	    	time1=time2;
	    	time2=t;
		} 
	    y1=time1/10000,m1=time1%10000/100,d1=time1%100;/*将年月日分开取出来*/
		y2=time2/10000,m2=time2%10000/100,d2=time2%100;
		int ans=1;                                     /*看题*/
		while(!((y1==y2)&&(m1==m2)&&(d1==d2))){
			d1++;
			if (d1==mouth[m1][isrun(y1)]+1){/*日期满了进一,月份满了进一*/
				m1++;
				d1=1;	}
			if (m1==13){y1++;m1=1;}
			ans++;
		} 
        printf("%d",ans);
    }
	return 0; 	
}

​

 

<think>好的,用户需要一个工具方法,用来计算两个Date类型日期之间的差值,以年为单位,保留一位小数。首先,我得确定如何准确计算年份差。年份不像份或天数那样固定,因为有闰年天数不同的问题。直接按毫秒数计算可能不够准确,比如考虑闰年的话,每年的天数可能不同。 那可能需要分步骤处理。首先,比较两个日期大小,确保开始日期早于结束日期,如果不是的话,交换它们的位置,保证计算的是正数。然后,计算两个日期之间的年份差。比如,如果开始日期是2020-03-01,结束日期是2023-03-01,刚好是3年,差3.0年。但如果结束日期是2023-09-01,那超过3年但不到4年,这时候需要计算具体多出的份。 不过,用户希望的是按年为单位,保留一位小数。可能需要先算出完整的年份,然后处理剩余的时间。比如,如果相差3年又6个,就是3.5年。但问题是如何准确计算这剩余的份或天数转换为小数年。 另一个方法是计算总天数,然后除以365。但这样会有误差,因为闰年有366天。例如,两年中有一个闰年,总天数是731,除以365得约2.0027,这样可能不够准确。用户可能需要的是实际年份的差异,而不是按平均天数计算。 或者,可以考虑逐年的比较。比如,先计算两个日期的年份差,然后检查结束日期份和日期是否在开始日期之前。例如,开始日期是2020-05-01,结束日期是2023-03-01。年份差是3年,但结束日期份是3,早于5,所以实际差是2年10个,即大约2.83年,保留一位小数是2.8年。这时候可能需要计算完整的年份,再处理剩下的份和天数。 可能的步骤是: 1. 确定开始日期早于结束日期,否则交换。 2. 计算年份差,比如结束年份减去开始年份。 3. 比较结束日期份和日期是否小于开始日期份和日期。如果是的话,年份差减1,然后计算剩下的份。 4. 剩下的份可以转换为年的小数部分,比如剩下6个就是0.5年。 5. 如果结束日期份和日期大于等于开始日期的,则直接加上份和天数的小数部分。 不过,这里有个问题,天数如何处理?比如,两个日期相差1年零15天,这时候小数部分应该是15/365≈0.04,总共1.04年,四舍五入到一位小数是1.0年。但如果用户希望更精确的话,可能需要考虑份和天数的总和。 或者,更简单的方式是计算两个日期之间的毫秒差,然后除以一年的平均毫秒数(比如365.25天/年 * 24*60*60*1000),再保留一位小数。但这种方法可能会有误差,因为每年的实际天数不同。 用户可能希望的是按自然年计算,例如,2020-06-01到2021-06-01是1年,到2021-12-01则是0.5年(6个),这时候需要考虑份差。 因此,正确的做法应该是先计算完整的年份数,然后处理剩余的份和天数,转换为小数部分。 具体实现步骤可能如下: 1. 确保startDate在endDate之前,否则交换。 2. 计算年份差:years = endYear - startYear 3. 检查结束日期份是否小于开始日期份,或者份相同但天数较小。如果是的话,年份差减1,并计算剩余份。 4. 剩余份加上天数转换的部分,除以12得到小数年。 5. 总年数加上小数部分,四舍五入到一位小数。 比如,startDate是2020-03-15,endDate是2023-05-20。年份差是3年。然后比较份:5比3大,所以不用减年份。剩下的份是2个(从3到5),但天数20-15=5天。这部分转换为份:5天大约0.164个(假设每30天),总剩余是2.164个,除以12得到0.180年,总和是3.180,保留一位小数是3.2年。 但这样的处理是否准确?可能用户更希望的是,如果超过整年后的份数,比如6个就是0.5年,而不考虑具体天数。或者是否需要考虑每个的实际天数? 这可能会有不同的实现方式,根据用户的需求。例如,银行计算利息可能有不同的方法。用户这里可能希望简单的份差,即满一个算1/12年,而不考虑具体天数,或者按实际天数计算。 这个问题需要进一步明确。比如,如果两个日期相差1年零1天,应该算1.0年还是1.003年?用户示例中是6个算0.5年,说明可能希望按计算,而不是按天数。因此,可能应该先计算完整的年份和份,然后按份的小数部分处理。 那具体步骤可以是: - 计算两个日期之间的份差,然后转换为年数,保留一位小数。 例如,份差为18个,即1.5年。或者份差为6个,是0.5年。如果存在余下的天数,则按天数占该天数的比例计算。例如,差3个零5天,那么5天占该(比如当是31天的话)5/31≈0.161,总份是3.161,除以12得到0.2635年,加上整年部分。 这样处理的话,可能需要先计算总份差,再处理剩余天数。 具体实现可能如下: 1. 交换日期确保start <= end 2. 计算总份差:months = (endYear - startYear)*12 + (endMonth - startMonth) 3. 如果endDay < startDay,则months -=1,并计算剩余的天数:days = endDay + (startDay所在的总天数 - startDay) + endDay之后的剩余天数?或者更简单的方式:比如,如果start是2020-03-15,end是2023-05-10,那么份差是3年*12 + (5-3)=38个,但endDay(10) < startDay(15),所以份减1到37个,剩余天数是(3的总天数-15)+10。例如,3有31天,所以31-15=16天,加上10天,共26天。然后计算这26天占5天数(31天)的比例,即26/31≈0.8387个。总份为37.8387,转换为年数即37.8387/12≈3.1532年,保留一位小数3.2年。 这样处理的话,会比较精确,但代码实现需要考虑每个天数,特别是闰年二月份的情况。 因此,代码实现步骤可能包括: 1. 确定开始日期和结束日期,确保start <= end。 2. 计算年份差和份差,同时处理天数。 3. 计算总份差,并处理剩余天数。 4. 将总份转换为年数,保留一位小数。 接下来,如何用Java代码实现这个逻辑? Java中可以使用Calendar类或者Java 8的LocalDate类来处理日期。建议使用LocalDate,因为它更现代且更易于操作。 步骤分解: 1. 将Date转换为LocalDate(如果使用Java 8及以上)。 2. 比较两个LocalDate,确保startDate <= endDate。 3. 计算Period between startDate和endDate,得到年份、份、天数。 4. 处理剩余的天数:计算这些天数占该天数的比例,转换为份的小数部分。 5. 总份数 = 年份*12 + 份 + 天数比例。 6. 年数 = 总份 / 12,保留一位小数。 例如,startDate是2020-03-15,endDate是2023-05-10。 Period得到3年1个25天?或者需要重新计算? 可能Period.between(startDate, endDate) 会给出3年1个25天?或者要看具体日期差异。比如,从2020-03-15到2023-05-10,应该是3年1个25天?或者计算方式不同? 或者,可能更准确的方式是使用ChronoUnit计算份差,然后处理剩余天数。 例如: long totalMonths = ChronoUnit.MONTHS.between(startDate, endDate); 然后, endDate.minusMonths(totalMonths) 得到剩余的天数差。 但这种方法可能需要调整,比如: LocalDate temp = startDate.plusMonths(totalMonths); long days = ChronoUnit.DAYS.between(temp, endDate); 然后,计算这days天占temp所在份的总天数的比例。 例如,startDate是2020-03-15,endDate是2023-05-10。totalMonths是38个(3*12 + 2=38,因为从3到5是2个),但可能ChronoUnit.MONTHS.between返回的是38个。然后temp是startDate.plusMonths(38)=2020-03-15 +38个=2023-05-15?此时endDate是2023-05-10,所以days是-5天?这说明可能需要重新计算。 可能更好的方法是: 使用Period来获取年、、日的差异,然后处理。 例如: Period period = Period.between(startDate, endDate); int years = period.getYears(); int months = period.getMonths(); int days = period.getDays(); 然后,计算总份数为 years*12 + months,然后处理天数。但是,这种方法可能不准确,因为Period的between方法返回的是基于日期的相对差值,不考虑天数的影响。例如,2020-03-15到2023-05-10的Period可能是3年1个25天?或者需要具体看日期。 或者,更好的办法是使用ChronoUnit计算总份差,然后处理剩余天数。 例如: long totalMonths = ChronoUnit.MONTHS.between(startDate, endDate); LocalDate temp = startDate.plusMonths(totalMonths); long days = ChronoUnit.DAYS.between(temp, endDate); 这样,totalMonths是完整的份差,temp是startDate加上这些份后的日期,然后days是剩余的天数。如果endDate在temp之后,days是正数,否则是负数。 然后,计算这部分days占该天数的比例。比如,temp是2023-05-15,endDate是2023-05-10,那么days是-5天,这说明可能前面的totalMonths多算了一个,需要调整? 或者例子中,如果startDate是2020-03-15,endDate是2023-05-10: ChronoUnit.MONTHS.between返回的值是38个(从2020-03到2023-05),此时temp是2020-03-15 +38 months = 2023-05-15。此时,endDate是2023-05-10,所以days = -5天。这说明需要将totalMonths减1,然后计算剩余天数。这可能意味着需要调整计算方式。 这似乎比较复杂,可能需要另一种方法。例如: 计算份差,直到temp日期不超过endDate: long totalMonths = 0; LocalDate temp = startDate; while (temp.plusMonths(1).isBefore(endDate)) { temp = temp.plusMonths(1); totalMonths++; } // 此时temp是startDate + totalMonths个,且temp <= endDate < temp +1 month // 然后计算剩余天数 long days = ChronoUnit.DAYS.between(temp, endDate); 这样,totalMonths是正确的份数,days是剩余的天数。然后,计算这days占temp.plusMonths(1)份的天数的比例。例如,temp是2023-05-15,那么加上一个是2023-06-15,这个天数是30天?所以days是0到30之间? 不过,这样的循环可能在处理大时间跨度时效率不高,但对于工具方法来说,可能可以接受。或者是否有更高效的方式? 或者,使用ChronoUnit.MONTHS.between(startDate, endDate)得到totalMonths,然后temp = startDate.plusMonths(totalMonths),如果temp在endDate之后,那么totalMonths减1,并重新计算temp和days。 例如: long totalMonths = ChronoUnit.MONTHS.between(startDate, endDate); LocalDate temp = startDate.plusMonths(totalMonths); if (temp.isAfter(endDate)) { totalMonths--; temp = startDate.plusMonths(totalMonths); } long days = ChronoUnit.DAYS.between(temp, endDate); 这样,可以确保temp <= endDate < temp.plusMonths(1) 然后,计算days占temp.plusMonths(1)份的总天数的比例。 例如,temp是2023-05-15,endDate是2023-05-10。那么totalMonths会是38个吗?或者原startDate是2020-03-15,加上38个是2023-05-15,而endDate是2023-05-10,此时temp.isAfter(endDate)为true,所以totalMonths减到37个,temp变为2020-03-15 +37个=2023-04-15。然后days是ChronoUnit.DAYS.between(2023-04-15, 2023-05-10)等于25天。那这个25天占四份的天数(30天)的比例是25/30≈0.833个,所以总份是37 + 0.833≈37.833,总年数是37.833 /12≈3.153年,保留一位小数是3.2年。 这样处理的话,代码的逻辑是: 1. 转换Date为LocalDate(需要处理旧版Date到新版的时间转换)。 2. 确保startDate <= endDate。 3. 计算totalMonths = ChronoUnit.MONTHS.between(startDate, endDate) 4. temp = startDate.plusMonths(totalMonths) 5. 如果temp超过endDate,则totalMonths减1,并调整temp。 6. 计算剩余天数days = endDate - temp。 7. 获取temp之后一个的总天数(即temp.plusMonths(1)所在份的天数)。 8. 计算份的小数部分:days / 总天数。 9. 总年数 = (totalMonths + daysFraction) /12 10. 四舍五入到一位小数。 现在,如何获取temp之后那个的总天数?例如,temp是2023-04-15,那么下一个的第一天是2023-05-01,该的总天数是31天。可以使用: LocalDate nextMonth = temp.plusMonths(1); int lengthOfMonth = nextMonth.lengthOfMonth(); 或者,当前temp所在份的下一个天数?比如,temp是2023-04-15,那么下个是五,有31天。所以是的,temp.plusMonths(1).lengthOfMonth()可以得到。 或者,是否应该考虑temp所在份的下一个天数?比如,当temp是2023-02-28,那么下个是三,有31天。是的,所以计算方式正确。 综上,Java代码的大致步骤: public static double getDateDifferenceInYears(Date date1, Date date2) { // 转换Date到LocalDate LocalDate startDate = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate endDate = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // 确保startDate <= endDate if (startDate.isAfter(endDate)) { LocalDate temp = startDate; startDate = endDate; endDate = temp; } long totalMonths = ChronoUnit.MONTHS.between(startDate, endDate); LocalDate temp = startDate.plusMonths(totalMonths); // 如果temp超过endDate,需要减少一个 if (temp.isAfter(endDate)) { totalMonths--; temp = startDate.plusMonths(totalMonths); } long days = ChronoUnit.DAYS.between(temp, endDate); // 计算下个的总天数 int daysInMonth = temp.plusMonths(1).lengthOfMonth(); double monthsFraction = days / (double) daysInMonth; double totalYears = (totalMonths + monthsFraction) / 12.0; // 四舍五入到一位小数 return Math.round(totalYears * 10) / 10.0; } 这样处理的话,测试用例: 例如,相差6个的情况: startDate = 2023-01-01,endDate = 2023-07-01。totalMonths=6个,temp=2023-07-01,刚好等于endDate,所以days=0。总年数是6/12=0.5年,正确。 另一个例子,startDate=2020-03-15,endDate=2023-05-10: totalMonths = ChronoUnit.MONTHS.between(2020-03-15, 2023-05-10) = 38个。temp = 2020-03-15 +38个 = 2023-05-15。因为2023-05-15在endDate(2023-05-10)之后,所以totalMonths减到37,temp变为2023-04-15。days=2023-04-15到2023-05-10的天数是25天。下个是五,有31天。monthsFraction=25/31≈0.806。总份=37 +0.806=37.806,除以12得≈3.1505,四舍五入到一位小数是3.2年。 这可能符合预期。 但需要注意,当份差刚好是整数时,如startDate和endDate在同一天,比如2020-03-15到2023-03-15,totalMonths=36,temp=2023-03-15,等于endDate,所以days=0,总年数3.0年。 另一个情况:startDate是2020-02-29(闰年),endDate是2021-02-28。此时,ChronoUnit.MONTHS.between返回12个?因为2020-02-29到2021-02-28是不足12个?或者ChronoUnit.MONTHS.between会计算为11个? 需要测试。例如,LocalDate start = LocalDate.of(2020, 2, 29); end = LocalDate.of(2021, 2, 28)。ChronoUnit.MONTHS.between(start, end)的结果是11个,因为加上11个后是2021-01-29,再加一个是2021-02-29(不存在),所以ChronoUnit计算为11个。此时temp=start.plusMonths(11)=2021-01-29。days=ChronoUnit.DAYS.between(temp, end)= 30天(从2021-01-29到2021-02-28是30天?需要具体计算。2021年129日到228日,共30天?是的,因为1有31天,31-29=2天,加上28天是30天。下个是2,所以daysInMonth是28天。monthsFraction=30/28≈1.071,这会导致总份=11 +1.071=12.071,除以12得1.0059,四舍五入到一位小数是1.0年。但实际日期差是365天(2020-02-29到2021-02-28是366天吗?因为2020是闰年,所以2020-02-29到2021-02-28是366天?或者因为228日不算闰日?此时,按实际天数计算,可能更准确,但用户希望的是按自然计算。在这种情况下,代码可能计算出1.0年,但实际相差的天数是366天,除以365.25约等于1.002年,保留一位小数是1.0年。但根据上述代码,这里会得到12.071/12≈1.0059,四舍五入到一位小数是1.0年。这可能可以接受。 另一个测试案例:startDate是2020-03-31,endDate是2020-04-30。totalMonths=1个?因为ChronoUnit.MONTHS.between返回1个。temp=2020-04-30?或者startDate.plusMonths(1)是2020-04-30吗?是的,因为331日加一个是430日。此时endDate是2020-04-30,temp等于endDate,所以days=0,总年数是1/12≈0.0833,保留一位小数是0.1年。 但如果endDate是2020-04-29,那么totalMonths=0?因为ChronoUnit.MONTHS.between(startDate, endDate)会返回0,因为加上1个会超过。例如,startDate是2020-03-31,endDate是2020-04-29。ChronoUnit.MONTHS.between返回0个。temp=startDate.plusMonths(0)=2020-03-31。days=29天(从331到429是29天吗?因为331到41是1天,到429是29天?是的。然后计算下个(4)的天数是30天?不,temp是3,temp.plusMonths(1)是4天数30天?是的,四份有30天。所以monthsFraction=29/30≈0.9667。总份=0 +0.9667=0.9667,除以12≈0.0805年,即0.1年。 这可能符合预期。 综上,这个代码的逻辑可以处理大部分情况,但需要注意处理闰年和不同份的天数。 接下来,关于保留一位小数,使用Math.round(totalYears *10)/10.0。例如,3.15会四舍五入为3.2,而3.14则变为3.1。是否用户希望四舍五入?是的,通常保留一位小数时四舍五入是合适的。 最后,将Date转换为LocalDate时,需要注意时区问题。Date类型可能包含时间部分,但用户可能只关心日期部分。所以,转换为LocalDate时应使用系统默认时区,或者明确指定时区。例如: LocalDate start = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); 这样处理可能正确,但假设用户传入的Date对象的时间部分被忽略,只考虑日期部分。 综上,编写代码时需要注意以下几点: - 确保处理了开始日期在结束日期之后的情况。 - 正确计算份差和剩余天数- 处理不同份的天数,包括闰年- 正确转换Date到LocalDate,考虑时区。</think>以下是一个用于计算两个Date类型日期差值(以年为单位,保留一位小数)的Java工具方法: ```java import java.time.*; import java.util.Date; public class DateUtils { public static double getYearDifference(Date date1, Date date2) { // 转换Date为LocalDate(考虑时区) LocalDate startDate = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate endDate = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // 确保startDate早于endDate if (startDate.isAfter(endDate)) { LocalDate temp = startDate; startDate = endDate; endDate = temp; } // 计算完整份差 long totalMonths = ChronoUnit.MONTHS.between(startDate, endDate); LocalDate tempDate = startDate.plusMonths(totalMonths); // 处理超出的天数 if (tempDate.isAfter(endDate)) { totalMonths--; tempDate = startDate.plusMonths(totalMonths); } // 计算剩余天数 long days = ChronoUnit.DAYS.between(tempDate, endDate); // 获取下一个的总天数(用于计算小数部分) int daysInNextMonth = tempDate.plusMonths(1).lengthOfMonth(); // 计算总年数(含小数) double totalYears = (totalMonths + days / (double) daysInNextMonth) / 12.0; // 四舍五入保留1位小数 return Math.round(totalYears * 10) / 10.0; } } ``` ### 使用示例 ```java public static void main(String[] args) { Date date1 = Date.from(LocalDate.of(2020, 3, 15).atStartOfDay(ZoneId.systemDefault()).toInstant()); Date date2 = Date.from(LocalDate.of(2023, 5, 10).atStartOfDay(ZoneId.systemDefault()).toInstant()); double diff = DateUtils.getYearDifference(date1, date2); System.out.println(diff); // 输出3.2 } ``` ### 实现说明 1. **日期转换**:将`Date`转换为`LocalDate`,忽略时间部分 2. **日期对齐**:确保开始日期早于结束日期,保证计算正差值 3. **份差计算**:使用`ChronoUnit.MONTHS`获取精确的整数 4. **天数处理**:计算超出整部分的天数,并按比例折算为份 5. **精度控制**:通过四舍五入保留1位小数 ### 特殊场景处理 - 闰年二月自动计算正确天数 - 日期(如331日→430日)自动对齐 - 精确处理不足整天数差异 该方法通过Java 8日期API实现精确计算,建议使用JDK 8及以上版本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡巴卡ldf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值