class MyDate {
private int year;
private int month;
private int day;
private int hour;
private int minute;
private int second;
private final long timePerSecond = 1000;
private final long timePerMinute = 1000*60;
private final long timePerHour = 3600*1000;
private final long timePerDay = 24*3600*1000;
private final long timePerPingNian = 365*timePerDay;
private final long timePerLeapYear = 366*timePerDay;
private final long timeOf1970And1971 = timePerPingNian+timePerPingNian;
private final long timePer4Year = (long)(365.25*4*timePerDay);
private final long timeEvery400Year = timePer4Year*100 - 3*timePerDay;
private final long timeOf1970To2000 = timePer4Year*7+timeOf1970And1971;
private final int[] daysPerMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
private boolean setMinuteAndSecond(long milliTime) {
assert milliTime<timePerDay;
if(milliTime>=timePerDay)
return false;
hour = (int)(milliTime/timePerHour);
long t = milliTime%timePerHour;
minute = (int)(t/timePerMinute);
t = t%timePerMinute;
second = (int)(t/timePerSecond);
return true;
}
private boolean setMonthAndDay(long milliTime, boolean isLeapYear) {
//this func should handle the case where milliTime == 0;
//this func get the milliseconds within 1 year;
assert milliTime<=timePerLeapYear && isLeapYear || milliTime<=timePerPingNian && !isLeapYear;
if(milliTime>timePerLeapYear && isLeapYear || milliTime>timePerPingNian && !isLeapYear) {
return false;
}
daysPerMonth[1] = isLeapYear ? 29 : 28; //set the days of February;
int cnt = -1;
do{
milliTime -= daysPerMonth[++cnt]*timePerDay;
}while (milliTime>=0);
month = cnt+1; //for that the cnt begin from 0, but month begin from 1;
long timeOfLastMonth = daysPerMonth[cnt]*timePerDay+milliTime;
day = (int)(timeOfLastMonth/timePerDay)+1;
setMinuteAndSecond(timeOfLastMonth % timePerDay);
return true;
}
private boolean setDataFile(long milliTime) {
if(milliTime<0)
return false;
boolean is1970Or1971 = milliTime<timeOf1970And1971 ? true : false;
boolean is20Century = milliTime<timeOf1970To2000 ? true: false;
//if equal is 2000 1 1 0:0:0
if(is1970Or1971) {
year = milliTime>=timePerPingNian ? 1971 : 1970;
setMonthAndDay(year==1971 ? milliTime-timePerPingNian : milliTime, false);
return true;
}
if(is20Century) {
long t = (milliTime-timeOf1970And1971)/timePer4Year;
long t1 = milliTime-timeOf1970And1971-t*timePer4Year;
long t2 = t1-timePerLeapYear;
boolean isLeapYear = t2<0;
long t3 = isLeapYear ? 0 : t2/timePerPingNian+1;
year = (int)(1972+t*4+t3);
long l = isLeapYear ? t1 : (t1-t3*timePerPingNian-timePerDay);
setMonthAndDay(l, isLeapYear);
return true;
}
long p = milliTime-timeOf1970To2000;
long t1 = p/timeEvery400Year;
long p1 = p-t1*timeEvery400Year;
long t2 = p1/timePer4Year;
long tN = t2/25;
//in every 400 years, other year such as xy00(y!=0) is not leap year;
long p2 = p1-t2*timePer4Year;
p2 += tN*timePerDay;
long t3 = p2-timePerLeapYear;
boolean isLeapYear = t3<0;
long t4 = isLeapYear ? 0 : t3/timePerPingNian+1;
year = (int)(2000+t1*400+t2*4+t4);
long lt = isLeapYear ? p2 : (p2-t4*timePerPingNian-timePerDay);
setMonthAndDay(lt, isLeapYear);
return true;
}
}
思路
设置时分秒(函数setMinuteAndSecond)
- 函数的输入是 小于 每天毫秒数 的一个整数
- 因为是从0:0:0开始计时,所以很好算
- 直接除以每小时毫秒数,结果就是 要求的小时数
- 然后把取模每小时毫秒数的结果除以每分钟毫秒数,算出来的结果就是分钟数
- 以此类推,算出秒数
设置月和日(函数setMonthAndDay)
- 函数的输入为 小于 每年毫秒数 一个整数
- 依次减去每个月的毫秒数,当减去某个月后,得到负数,说明月份就是这个月。注意,如果减去后得到0,则是下个月的第一毫秒,因为毫秒数是相对1970.1.1.0:0:0计算的。举个例子,毫秒数==一个平年的毫秒数,那么加上1970.1.1.0:0:0后,就变成1971.1.1.0:0:0,而不是1970.12.31.23:59:59
- 通过上面那个流程,得到属于一个月内的毫秒数(注意,比如说某个月的毫秒数是K,那么0~(K-1)都是属于这个月,但是K不是),然后除以每天的毫秒数,得到 P 。同样,如果整除,说明日期数是 P+1,如果不整除,那也是 P+1(注意,日期是从1开始,而不是从0开始)
设置年份(在函数setDataFile中)
对1970/1971特殊处理,直接判断是否毫秒数是否 ≥ 一个平年的毫秒数,如果是,那么就是1971。注意,等于也是,原因如上所述。然后调用setMonthAndDay函数,设置日月
对1972.1.1.0:0:0到2000.1.1.0:0:0 特殊处理。先减去1970和1971的总的毫秒数,得到的结果除以每4年的毫秒数,得到H。同样,无论是否整除,年份数是在 [4H+1972,4H+1972+3] ,整除就是 4H+1972 这一年的第一毫秒(0时0分0秒)。因为1970.1.1.0:0:0加上1970和1971总的毫秒数已经是1972.1.1.0:0:0了,再加上整数年 4H 就是(1972+4H).1.1.0:0:0了。
然后减去一个闰年的毫秒数,因为从1972年算起,每四年的第一年都是闰年,如果结果是负数,说明是闰年(同样的,如果是0,那么并不是闰年,而是闰年下一年的第一毫秒)。否则,把得到结果除以每个平年的毫秒数得到 P ,就是从闰年的下一年 开始的第 P 年,所以,年份就是 1972+4H+1+P
然后,因为当年份数是100的倍数时,只有当年份可以被400整除,才是闰年。而2000年正好就是每400年里的第一年,所以要先计算,毫秒数里总的有多少个400年,如果有 t 个,那么年份数就是
[2000+400t,2000+400t+399] 接下来就计算总的毫秒数减去1970到2000的毫秒数,再减去总共多少个400年的毫秒数,剩下的毫秒数里,算算有多少个4年,因为每4年是闰年。如果算出来的结果是 d ,那么年份数就是
[2000+400t+4d,2000+400t+4d+3] 接下来判断剩下的毫秒数是否小于一个闰年的毫秒数,如果是,那么就是闰年,年份数是 2000+400t+4d 。如果不是,就减去一个闰年的毫秒数,然后算一算剩下的有多少个平年的毫秒数,假设算出来的结果是 b ,那么年份数就是
2000+400t+4d+b+1 接下来调用setMonthAndDay函数设置月份日期