摘要:儒略日就是指從公元前年月日開始所經(jīng)過的天數(shù),就被指定為公元前年月日到公元前年月日之間的小時(shí),依次順推,每一天都被賦予一個(gè)唯一的數(shù)字。
??最近閑來無事,突然想了解下中國農(nóng)歷與中國陽歷之間的關(guān)系,經(jīng)過一番調(diào)研發(fā)現(xiàn)這里面的水還比較深,涉及天文學(xué)、歷史、宗教等一些知識,發(fā)現(xiàn)挺有意思的就準(zhǔn)備做一系列的總結(jié),主要是防止自己忘記了,而且搜索了一下簡書上的文章也么沒有相關(guān)文章進(jìn)行描述,所以借此機(jī)會(huì)跟大家分享同大家討論,這篇文章是這個(gè)系列的第一篇,主要講是如何推算指定日期的星期問題
根據(jù)已知日期進(jìn)行推算該方法主要是通過計(jì)算已知日期和指定日期之間的天數(shù)來推算,因?yàn)樾瞧谑且粋€(gè)非常固定的時(shí)間周期,7天一循環(huán),因此通過計(jì)算出相差天數(shù)后,再對7進(jìn)行取余操作就能推算出指定的日期的星期
根據(jù)公歷平閏年計(jì)算最常見的計(jì)算兩個(gè)日期之間的天數(shù)就是根據(jù)年數(shù)進(jìn)行推算,但是由于有閏年的影像,因此需要考慮閏年的情況,閏年的判斷規(guī)則如下:
公歷年數(shù)是4的倍數(shù),且不是100倍數(shù)
公歷年數(shù)是400倍數(shù)
滿足上述兩個(gè)條件中的一個(gè)既是閏年,該描述可以參考李永樂老師的視頻講解:閏年是怎么回事
依次遍歷年份計(jì)算天數(shù)經(jīng)過分析我們發(fā)現(xiàn)兩個(gè)日期之間的天數(shù)可分為三部分:
起始日期所在年中剩余的天數(shù)
結(jié)束日期所在年中已經(jīng)度過的天數(shù)
中間年份的整年的天數(shù)
實(shí)現(xiàn)代碼一上述是一個(gè)通用描述,對于起止時(shí)間正好在一個(gè)年的開始和結(jié)尾,該方案也能描述。
因?yàn)橹虚g是一個(gè)有可能存在閏年的,所需要根據(jù)閏年規(guī)則對每個(gè)年份進(jìn)行判斷,代碼如下:
/** * @param startYear 起始年份 * @param endYear 截止年份 * @return 從起止年份到截止年份前一年的天數(shù) * 常量 YearDays.LEAP_YEAR_DAYS = 366 * 常量 YearDays.NONLEAP_YEAR_DAYS = 365 * @throws CalendarException */ public static int calculateDaysOfYears(int startYear, int endYear) throws CalendarException { if(startYear > endYear) { throw new CalendarException(String.format("illegal parameter year, startYear=%s endYear=%s", startYear, endYear)); } int difference = endYear - startYear; int totalDays = 0; //從起始年的1月1號開始加和,一直到截止年的前一年所有的天數(shù),這樣的計(jì)算需要減去起始年已經(jīng)過過去的天數(shù) for(int i= startYear; i < endYear; i++) { boolean isLeap = CalendarTool.isLeapyear(i); if (isLeap) { totalDays += YearDays.LEAP_YEAR_DAYS; } else { totalDays += YearDays.NONLEAP_YEAR_DAYS; } } return totalDays; }
因?yàn)樾枰?jì)算截止年月已經(jīng)度過的天數(shù),所以我復(fù)用了這個(gè)計(jì)算邏輯,以為剩余的天數(shù) = 年內(nèi)總天數(shù) - 已經(jīng)過的天數(shù)基于這個(gè)公式就能算出其實(shí)年份剩余的天數(shù)
/** * 計(jì)算一年中到指定日期時(shí)已經(jīng)度過的天數(shù) * @param year 年 * @param month 枚舉類型,定義了sort月數(shù)和 days天數(shù)兩個(gè)屬性 * @param day 所在月的日期 * @return */ public static int calculatePassedDays(int year, CalendarMonth month, int day) throws CalendarException { CalendarTool.checkParam(year, month, day); int monthValue = month.getSort(); int passedTotal = 0; for(int i = 1; i < monthValue; i++) { CalendarMonth passedMonth = CalendarMonth.getMonthBySort(i); if(passedMonth.equals(CalendarMonth.FEBRUARY)) { boolean isLeap = CalendarTool.isLeapyear(year); if(isLeap) { passedTotal += 29; continue; } } passedTotal += passedMonth.getDays(); } return passedTotal + day; }
舉個(gè)例子驗(yàn)證一下:已知1977年3月27日是星期天,計(jì)算2005年5月31日是星期幾,按照上述的算法進(jìn)行計(jì)算得到天數(shù)是10292,然后用10292 % 7 = 2得到余數(shù)為2,那么2005年5月31日就是星期二。
實(shí)現(xiàn)代碼二上述方法需要依次遍歷起止年份之間的所有年份進(jìn)行平閏年的判斷,比較復(fù)雜,判斷起來比較麻煩,其實(shí)我們只需要知道兩個(gè)年份之間的閏年個(gè)數(shù)就可以解決我們的問題了,閏年的個(gè)數(shù)的計(jì)算公式:(分?jǐn)?shù)結(jié)構(gòu)部分表示向下取整)
$$ leap = leftlfloorfrac y4
ight
floor - leftlfloorfrac y{100}
ight
floor + leftlfloorfrac y{400}
ight
floor$$
公式中的y表示的當(dāng)前年份,該公式結(jié)果是從公元元年到y(tǒng)年中所有閏年的個(gè)數(shù),如果y年是閏年的話,y年也算在內(nèi)。
這個(gè)公式中臨界值確定需要注意,就是在起止年份,如果起止年份是閏年的話,要看起止年份是過了2月,這個(gè)直接關(guān)系到是+1天還是不加1天的問題。
這樣就不需要遍歷每個(gè)年份是否為閏年了,而直接計(jì)算天數(shù)即可,只需要將之前小節(jié)中計(jì)算整年天數(shù)代碼進(jìn)行調(diào)整,如下:
/** * @param startYear 起始年份 * @param endYear 截止年份 * @return 從起止年份到截止年份前一年的天數(shù) * 常量 YearDays.LEAP_YEAR_DAYS = 366 * 常量 YearDays.NONLEAP_YEAR_DAYS = 365 * @throws CalendarException */ public static int calculateDays(int startYear, int endYear) throws CalendarException { startYear -= 1; endYear -= 1; int startLeaps = Integer.valueOf(startYear / 4) - Integer.valueOf(startYear / 100) + Integer.valueOf(startYear / 400); int endLeaps = Integer.valueOf(endYear / 4) - Integer.valueOf(endYear / 100) + Integer.valueOf(endYear / 400); int totalDays = endLeaps * YearDays.LEAP_YEAR_DAYS + (endYear - endLeaps) * YearDays.NONLEAP_YEAR_DAYS -(startLeaps * YearDays.LEAP_YEAR_DAYS + (startYear - startLeaps) * YearDays.NONLEAP_YEAR_DAYS); return totalDays; }
年數(shù)-1是因?yàn)槠鹬鼓攴莶⒉皇峭暾?,?jì)算之前的一年就不用考慮起止年份是否為閏年和月份是否過了2月份了,簡化了邏輯判斷。這樣代碼比之前的方法看起來精簡了不少。
代碼出處上述代碼片段可能不能滿足大家的需要,所以附上代碼出處
上面的方法是根據(jù)平閏年進(jìn)行推算的,需要進(jìn)行復(fù)雜的平閏年判斷,就算第二種方法也是簡化了起止年份之間的年份的閏年判斷,開始和結(jié)束年份的閏年判斷再計(jì)算度過了多少天的時(shí)候還是要進(jìn)行判斷的,通過儒略日可以規(guī)避平閏年的判斷。
儒略日儒略日是一種不記年,不記月,只記日的歷法,是由法國學(xué)者Joseph Justus Scaliger(1540-1609)在1583年提出來的一種以天數(shù)為計(jì)量單位的流水日歷。儒略日就是指從公元前4713年1月1日UTC 12:00開始所經(jīng)過的天數(shù),JD0就被指定為公元前4713年1月1日 12:00到公元前4713年1月2日12:00之間的24小時(shí),依次順推,每一天都被賦予一個(gè)唯一的數(shù)字。使用儒略日可以把不同歷法的年表統(tǒng)一起來,很方便地在各種歷法中追溯日期。如果計(jì)算兩個(gè)日期之間的天數(shù),利用儒略日計(jì)算也很方便,先計(jì)算出兩個(gè)日期的儒略日數(shù),然后直接相減就可以得到兩個(gè)日期相隔的天數(shù)。
儒略日公式$$ julian = leftlfloor 1461 imes frac{y + 4716}{4}
ight
floor + leftlfloor 153 imes frac{m + 1}{4}
ight
floor + d + c - 1524.5 $$
y表示年份
m表示月份,如果m≤2則m修正為m+12,同時(shí)y修正為y-1
d表示日期
c的求值公式
$$ c = 2 - leftlfloorfrac{y}{100}
ight
floor + leftlfloorfrac{y}{400}
ight
floor $$
public static int calculateDaysWithJulian(int year, CalendarMonth month, int day) throws CalendarException { CalendarTool.checkParam(year, month, day); int monthVal = month.getSort(); if(month.compare(CalendarMonth.FEBRUARY) <= 0) { monthVal = month.getSort() + 12; year = year - 1; } //計(jì)算 c 值 int c = 2 - Integer.valueOf(year/100) + Integer.valueOf(year/400); //因?yàn)槿迓匀盏拈_始是從中午12點(diǎn)開始的,所以需要加0.5天, 0.0000115740天=1秒 day += 0.5000115740; return (int) (Integer.valueOf(1461 * (year + 4716) / 4) + Integer.valueOf(153 * (monthVal + 1)/5) + day + c - 1514.5); }
這樣通過儒略日公式進(jìn)行推算,代碼較之前代碼更加精簡
代碼出處儒略日計(jì)算代碼出處
直接定位日期所在星期前面所述的推送方法都是基于已知日期的星期,然后計(jì)算與所指定的日期之間的天數(shù)來進(jìn)行推算,在已知日期之后向后推算,在已知日期之前的向前推算,不是很方便。基于公元元年1月1日為周一,那么前一天就是下面介紹一個(gè)直接定位日期所在星期日,基于這個(gè)想法退出下面的公式。
計(jì)算公式$$ weekday = ( y + leftlfloorfrac{y}{4}
ight
floor + leftlfloorfrac{c}{4}
ight
floor -2c + leftlfloor 13 imes frac{m+1}{5}
ight
floor + d - 1 ) \% 7 $$
上述公式適用1582年10月15日及之后的日期,對于1582年10月4及之前的公式:
$$ weekday = ( y + leftlfloorfrac{y}{4}
ight
floor + leftlfloorfrac{c}{4}
ight
floor -2c + leftlfloor 13 imes frac{m+1}{5}
ight
floor + d + 3 ) \% 7 $$
/*** * 計(jì)算指定日其所在的星期 * 兼容1582年前后10月4日之前和1582年10月15之后的所有時(shí)間 * @param year * @param month * @param day * @return * @throws CalendarException */ public static int calculateweek(int year, CalendarMonth month, int day) throws CalendarException { int m = month.getSort(); if(month.compare(CalendarMonth.MARCH) < 0) { m = month.getSort() + 12; year = year - 1; } int c = year / 100; int y = year % 100; int week = 0; if(year == 1582 && month.equals(CalendarMonth.OCTOBER) && day > 4 && day < 15) { throw new CalendarException(String.format("illegal date exception, date=%s-%s-%s not exist", year, month.getSort(), day)); } else { if(year < 1582 || (year == 1582 && month.compare(CalendarMonth.OCTOBER) < 0) || (year == 1582 && month.equals(CalendarMonth.OCTOBER) && day <= 4)) { week = Integer.valueOf(y + Integer.valueOf(y / 4) + Integer.valueOf(c / 4) - 2*c + Integer.valueOf(13 * (m + 1) / 5) + day + 3) % 7; } else { week = Integer.valueOf(y + Integer.valueOf(y / 4) + Integer.valueOf(c / 4) - 2*c + Integer.valueOf(13 * (m + 1) / 5) + day - 1) % 7; } } // 公式會(huì)產(chǎn)生負(fù)數(shù),修正即可 if(week < 0) week = (week + 7) % 7; return week; }代碼出處
儒略日定位計(jì)算代碼出處
這篇文章主要是從網(wǎng)上搜集來的知識并不是自己讀書的來,以下是主要文檔的出處
相關(guān)文檔:
https://blog.csdn.net/orbit/a...
https://blog.csdn.net/orbit/a...
http://www.365yg.com/i6550819...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/71394.html
摘要:直接使用事件代理機(jī)制,將事件綁定在整個(gè)日歷的上即可,這樣事件只用在創(chuàng)建時(shí)初始化一次即可,簡單高效省內(nèi)存。 首發(fā)我的博客 - https://blog.cdswyda.com/post/2017121010 日歷控件多的不勝枚舉,為什么我們還要再造一個(gè)輪子呢? 因?yàn)榇蠖鄶?shù)日歷控件都是用于選擇日期的,有種需求是要在日歷上展示各種各樣的內(nèi)容,這樣的日歷控件較少,而且試用下來并不滿意。 因此就...
摘要:,歡迎使用中文文檔在后面自我介紹是為了滿足移動(dòng)端對各種場景的需求而生的,兼容性強(qiáng),靈活度高。如空數(shù)組默認(rèn)設(shè)置成當(dāng)月日數(shù)組的每一位分別是年月日。 Calendar - A Flexible Calendar for Mobile Intro Calendar was born for several product requirements in the mobile. It’s f...
摘要:,歡迎使用中文文檔在后面自我介紹是為了滿足移動(dòng)端對各種場景的需求而生的,兼容性強(qiáng),靈活度高。如空數(shù)組默認(rèn)設(shè)置成當(dāng)月日數(shù)組的每一位分別是年月日。 Calendar - A Flexible Calendar for Mobile Intro Calendar was born for several product requirements in the mobile. It’s f...
摘要:已經(jīng)實(shí)現(xiàn)并維護(hù)了核心基礎(chǔ)結(jié)構(gòu)計(jì)劃的最佳實(shí)踐徽章。年中國開源峰會(huì)提案征集現(xiàn)已開放在中國開源峰會(huì)上,與會(huì)者將共同合作及共享信息,了解最新和最有趣的開源技術(shù),包括容器云技術(shù)網(wǎng)絡(luò)微服務(wù)等并獲得如何在開源社區(qū)中導(dǎo)向和引領(lǐng)的信息。 從沙箱或孵化狀態(tài)畢業(yè),或者作為一個(gè)新項(xiàng)目加入作為一個(gè)畢業(yè)項(xiàng)目,項(xiàng)目必須符合孵化階段標(biāo)準(zhǔn)以及: 有來自至少兩個(gè)機(jī)構(gòu)的提交者。 已經(jīng)實(shí)現(xiàn)并維護(hù)了核心基礎(chǔ)結(jié)構(gòu)計(jì)劃(CII)...
閱讀 2317·2021-10-09 09:41
閱讀 3497·2021-09-13 10:34
閱讀 1990·2019-08-30 12:59
閱讀 615·2019-08-29 17:27
閱讀 1123·2019-08-29 16:07
閱讀 3025·2019-08-29 13:15
閱讀 1378·2019-08-29 13:14
閱讀 1632·2019-08-26 12:18