服务订购到期日——今天遇见的一个很有意思的面试题

302 阅读3分钟

题目

服务订购到期日

在真实的业务场景中,客户可以在任意一天订购我们的服务,订购周期可以是一个月、一个季度、半年或一年,在订购日后的这么多月的当天凌晨 0 点订单到期。

以下举例说明订购的过期计算方式:

  • 在 2018-11-10 订购一个月,订购将在 2018-12-10 过期
  • 在 2018-12-10 订购一个月,订购将在 2019-01-10 过期
  • 在 2019-01-30 订购一个月,订购将在 2019-02-28 过期
  • 在 2019-04-30 订购一个月,订购将在 2019-05-30 过期
  • 在 2019-05-31 订购三个月,订购将在 2019-08-31 过期

问题1(无需编程)

假定我们每天可以收到 4 个订单,其中一个是一个月周期的,一个是一个季度周期的,一个是半年周期的,一个是一年周期的。这样的订单从很久以前,比如五年前就开始了。

请通过分析回答:在哪一天到期的订单会最多,有多少个?具体是哪几个订单?

并对分析过程进行解释。

问题2

请实现一个方法,给定订购的年月日 (year, month, day),已知订购时长是一个月,请返回订单的到期日。year, month, day 均为整数,输入已保证是正确的日期。

注:Javascript 不能使用 Date 对象和第三方库。

Javascript 请实现以下方法:

function getExpirationDate(year, month, day) {
    // TODO
    return [year, month, day];
}

题解

日期的问题就是要考虑好特殊情况就行主要就是2月份的28号和29号

下面是我的思路

问题1

一个月前 简称为 1m
一个季度前 简称为 3m
半年前 简称为 6m
一年前 简称为 12m

首先考虑普通日期的情况

  • 非2月份的1号-29号和2月份的1号-27号

在这些普通日期中最多情况下是:1m、3m、6m、12m,各一个过期也就是最多4个

特殊情况

  • 30号的情况

30号不为月底和普通情况差不多

30号为月底情况下,如果上个月也是30为月底那么将只有一个1m的订单,如果上个月是31为月底,那么将有两个1m的订单分别是上个月30号和31的订单,结合实际情况,30号最多订单情况为:2个1m、2个3m、2个6m、1个12m,也就是7个订单

  • 31号的情况

如果前一个月没有31号,将不会有1m的订单,所以31号的订单最多情况不会超过30号的最多情况

  • 2月份的28号

在这一天过期的订单将有

1m订单1.28、1.29、1.30、1.31

3m订单 11.28、11.29、11.30

6m订单 8.28、8.29、8.30、8.31

12m订单 上一年如果是闰年将有 2.28、2.29,如果是平年 2.28

  • 2月份的29号

和28号的逻辑一样,但是肯定超不过28号的订单

答案

订单最多的一天是2.28号且上一年是闰年且今年不能是订单开始的第一年,最多是13个订单,具体是1.28、1.29、1.30、1.31、11.28、11.29、11.30、8.28、8.29、8.30、8.31、2.28、2.29

问题2

function getExpirationDate(year, month, day) {
	let newYear, newMonth, newDay;
	if (month === 12) {
		newYear = year + 1;
		newMonth = 1;
		newDay = day;
	} else {
		newYear = year;
		newMonth = month + 1;
		let lastMonthDay = getMonthLastDay(year, month);
		let nextMonthLastDay = getMonthLastDay(newYear, newMonth);
		if (day === lastMonthDay || day > nextMonthLastDay) {
			newDay = nextMonthLastDay;
		} else {
			newDay = day;
		}
	}

	return [newYear, newMonth, newDay];
}
function isRunYear(year) {
	return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
function getMonthLastDay(year, month) {
	if (month === 2) {
		return isRunYear(year) ? 29 : 28;
	}
	return [1, 3, 5, 7, 8, 10, 12].includes(month) ? 31 : 30;
}

console.log(getExpirationDate(2020, 1, 30)); //2020.02.29
console.log(getExpirationDate(2020, 2, 29)); //2020.03.29
console.log(getExpirationDate(2021, 2, 28)); //2021.03.28
console.log(getExpirationDate(2021, 3, 31)); //2021.04.30
console.log(getExpirationDate(2021, 12, 31)); //2022.01.31