摘要:儒略日就是指從公元前年月日開始所經過的天數,就被指定為公元前年月日到公元前年月日之間的小時,依次順推,每一天都被賦予一個唯一的數字。
??最近閑來無事,突然想了解下中國農歷與中國陽歷之間的關系,經過一番調研發現這里面的水還比較深,涉及天文學、歷史、宗教等一些知識,發現挺有意思的就準備做一系列的總結,主要是防止自己忘記了,而且搜索了一下簡書上的文章也么沒有相關文章進行描述,所以借此機會跟大家分享同大家討論,這篇文章是這個系列的第一篇,主要講是如何推算指定日期的星期問題
根據已知日期進行推算該方法主要是通過計算已知日期和指定日期之間的天數來推算,因為星期是一個非常固定的時間周期,7天一循環,因此通過計算出相差天數后,再對7進行取余操作就能推算出指定的日期的星期
根據公歷平閏年計算最常見的計算兩個日期之間的天數就是根據年數進行推算,但是由于有閏年的影像,因此需要考慮閏年的情況,閏年的判斷規則如下:
公歷年數是4的倍數,且不是100倍數
公歷年數是400倍數
滿足上述兩個條件中的一個既是閏年,該描述可以參考李永樂老師的視頻講解:閏年是怎么回事
依次遍歷年份計算天數經過分析我們發現兩個日期之間的天數可分為三部分:
起始日期所在年中剩余的天數
結束日期所在年中已經度過的天數
中間年份的整年的天數
實現代碼一上述是一個通用描述,對于起止時間正好在一個年的開始和結尾,該方案也能描述。
因為中間是一個有可能存在閏年的,所需要根據閏年規則對每個年份進行判斷,代碼如下:
/** * @param startYear 起始年份 * @param endYear 截止年份 * @return 從起止年份到截止年份前一年的天數 * 常量 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號開始加和,一直到截止年的前一年所有的天數,這樣的計算需要減去起始年已經過過去的天數 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; }
因為需要計算截止年月已經度過的天數,所以我復用了這個計算邏輯,以為剩余的天數 = 年內總天數 - 已經過的天數基于這個公式就能算出其實年份剩余的天數
/** * 計算一年中到指定日期時已經度過的天數 * @param year 年 * @param month 枚舉類型,定義了sort月數和 days天數兩個屬性 * @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; }
舉個例子驗證一下:已知1977年3月27日是星期天,計算2005年5月31日是星期幾,按照上述的算法進行計算得到天數是10292,然后用10292 % 7 = 2得到余數為2,那么2005年5月31日就是星期二。
實現代碼二上述方法需要依次遍歷起止年份之間的所有年份進行平閏年的判斷,比較復雜,判斷起來比較麻煩,其實我們只需要知道兩個年份之間的閏年個數就可以解決我們的問題了,閏年的個數的計算公式:(分數結構部分表示向下取整)
$$ leap = leftlfloorfrac y4
ight
floor - leftlfloorfrac y{100}
ight
floor + leftlfloorfrac y{400}
ight
floor$$
公式中的y表示的當前年份,該公式結果是從公元元年到y年中所有閏年的個數,如果y年是閏年的話,y年也算在內。
這個公式中臨界值確定需要注意,就是在起止年份,如果起止年份是閏年的話,要看起止年份是過了2月,這個直接關系到是+1天還是不加1天的問題。
這樣就不需要遍歷每個年份是否為閏年了,而直接計算天數即可,只需要將之前小節中計算整年天數代碼進行調整,如下:
/** * @param startYear 起始年份 * @param endYear 截止年份 * @return 從起止年份到截止年份前一年的天數 * 常量 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; }
年數-1是因為起止年份并不是完整年,計算之前的一年就不用考慮起止年份是否為閏年和月份是否過了2月份了,簡化了邏輯判斷。這樣代碼比之前的方法看起來精簡了不少。
代碼出處上述代碼片段可能不能滿足大家的需要,所以附上代碼出處
上面的方法是根據平閏年進行推算的,需要進行復雜的平閏年判斷,就算第二種方法也是簡化了起止年份之間的年份的閏年判斷,開始和結束年份的閏年判斷再計算度過了多少天的時候還是要進行判斷的,通過儒略日可以規避平閏年的判斷。
儒略日儒略日是一種不記年,不記月,只記日的歷法,是由法國學者Joseph Justus Scaliger(1540-1609)在1583年提出來的一種以天數為計量單位的流水日歷。儒略日就是指從公元前4713年1月1日UTC 12:00開始所經過的天數,JD0就被指定為公元前4713年1月1日 12:00到公元前4713年1月2日12:00之間的24小時,依次順推,每一天都被賦予一個唯一的數字。使用儒略日可以把不同歷法的年表統一起來,很方便地在各種歷法中追溯日期。如果計算兩個日期之間的天數,利用儒略日計算也很方便,先計算出兩個日期的儒略日數,然后直接相減就可以得到兩個日期相隔的天數。
儒略日公式$$ 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,同時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; } //計算 c 值 int c = 2 - Integer.valueOf(year/100) + Integer.valueOf(year/400); //因為儒略日的開始是從中午12點開始的,所以需要加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); }
這樣通過儒略日公式進行推算,代碼較之前代碼更加精簡
代碼出處儒略日計算代碼出處
直接定位日期所在星期前面所述的推送方法都是基于已知日期的星期,然后計算與所指定的日期之間的天數來進行推算,在已知日期之后向后推算,在已知日期之前的向前推算,不是很方便。基于公元元年1月1日為周一,那么前一天就是下面介紹一個直接定位日期所在星期日,基于這個想法退出下面的公式。
計算公式$$ 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 $$
/*** * 計算指定日其所在的星期 * 兼容1582年前后10月4日之前和1582年10月15之后的所有時間 * @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; } } // 公式會產生負數,修正即可 if(week < 0) week = (week + 7) % 7; return week; }代碼出處
儒略日定位計算代碼出處
這篇文章主要是從網上搜集來的知識并不是自己讀書的來,以下是主要文檔的出處
相關文檔:
https://blog.csdn.net/orbit/a...
https://blog.csdn.net/orbit/a...
http://www.365yg.com/i6550819...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71394.html
摘要:直接使用事件代理機制,將事件綁定在整個日歷的上即可,這樣事件只用在創建時初始化一次即可,簡單高效省內存。 首發我的博客 - https://blog.cdswyda.com/post/2017121010 日歷控件多的不勝枚舉,為什么我們還要再造一個輪子呢? 因為大多數日歷控件都是用于選擇日期的,有種需求是要在日歷上展示各種各樣的內容,這樣的日歷控件較少,而且試用下來并不滿意。 因此就...
摘要:,歡迎使用中文文檔在后面自我介紹是為了滿足移動端對各種場景的需求而生的,兼容性強,靈活度高。如空數組默認設置成當月日數組的每一位分別是年月日。 Calendar - A Flexible Calendar for Mobile Intro Calendar was born for several product requirements in the mobile. It’s f...
摘要:,歡迎使用中文文檔在后面自我介紹是為了滿足移動端對各種場景的需求而生的,兼容性強,靈活度高。如空數組默認設置成當月日數組的每一位分別是年月日。 Calendar - A Flexible Calendar for Mobile Intro Calendar was born for several product requirements in the mobile. It’s f...
摘要:已經實現并維護了核心基礎結構計劃的最佳實踐徽章。年中國開源峰會提案征集現已開放在中國開源峰會上,與會者將共同合作及共享信息,了解最新和最有趣的開源技術,包括容器云技術網絡微服務等并獲得如何在開源社區中導向和引領的信息。 從沙箱或孵化狀態畢業,或者作為一個新項目加入作為一個畢業項目,項目必須符合孵化階段標準以及: 有來自至少兩個機構的提交者。 已經實現并維護了核心基礎結構計劃(CII)...
閱讀 2260·2021-10-09 09:41
閱讀 3409·2021-09-13 10:34
閱讀 1920·2019-08-30 12:59
閱讀 557·2019-08-29 17:27
閱讀 1063·2019-08-29 16:07
閱讀 2956·2019-08-29 13:15
閱讀 1306·2019-08-29 13:14
閱讀 1561·2019-08-26 12:18