├── .gitignore ├── go.mod ├── go.sum ├── util_test.go ├── LICENSE ├── doc.go ├── hijri_test.go ├── ummalqura_test.go ├── README.md ├── hijri.go └── ummalqura.go /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hablullah/go-hijri 2 | 3 | go 1.12 4 | 5 | require github.com/hablullah/go-juliandays v1.0.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0= 2 | github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc= 3 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 1 | package hijri_test 2 | 3 | import ( 4 | "encoding/csv" 5 | "io" 6 | "os" 7 | ) 8 | 9 | type TestData struct { 10 | Gregorian string 11 | Hijri string 12 | } 13 | 14 | func generateTestData(csvFilePath string) ([]TestData, error) { 15 | // Open test file 16 | f, err := os.Open(csvFilePath) 17 | if err != nil { 18 | return nil, err 19 | } 20 | defer f.Close() 21 | 22 | // Parse test file 23 | dataList := []TestData{} 24 | csvReader := csv.NewReader(f) 25 | 26 | for { 27 | record, err := csvReader.Read() 28 | if err == io.EOF { 29 | break 30 | } 31 | 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | dataList = append(dataList, TestData{ 37 | Gregorian: record[0], 38 | Hijri: record[1], 39 | }) 40 | } 41 | 42 | return dataList, nil 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Radhi Fadlillah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Hijri is package for converting Gregorian date into Hijri date and vice versa. Hijri or Islamic 2 | // calendar system itself is a lunar calendar used in many Muslim countries, with a year has 12 months 3 | // and 354 days or 355 days in a leap year. 4 | // 5 | // Hijri calendar only recognizes one era: A.H. (Latin "Anno Hegirae", which means "the year of the 6 | // migration," in reference to the migration of Muhammad (PBUH) from Mecca). With that said, Hijri 7 | // calendar is not proleptic, so there are no negative Hijri year. 8 | // 9 | // This package supports two kind of Hijri calendar, the arithmetic calendar and Umm al-Qura calendar. 10 | // 11 | // The arithmetic or tabular Hijri calendar (or simply Hijri) is a rule-based variation of the Islamic 12 | // calendar, in which months are worked out by arithmetic rules rather than by observation or astronomical 13 | // calculation. It is introduced by Muslim astronomers in the 8th century CE to predict the approximate 14 | // beginning of the months in the Islamic lunar calendar. 15 | // 16 | // It has a 30-year cycle with 11 leap years of 355 days and 19 years of 354 days. In the long term, it 17 | // is accurate to one day in about 2,500 solar years or 2,570 lunar years. It also deviates up to about 18 | // one or two days in the short term. However, there are several patterns of leap years to decide which 19 | // years within the 30 are leap. 20 | // 21 | // The Umm al-Qura calendar is astronomical-based calendar that used and created by Saudi Arabia. It is 22 | // also used by several neighbouring states on the Arabian Peninsula such as Bahrain and Qatar. For this 23 | // calendar, each month has either 29 or 30 days, but usually in no discernible order. 24 | // 25 | // The implementation of Umm al-Qura calendar in this package is based on Javascript code by R.H. van Gent 26 | // from Utrecht University. The date must be between 14 March 1937 (1 Muharram 1356 H) and 16 November 2077 27 | // (29 Dhu al-Hijjah 1500 H). 28 | package hijri 29 | -------------------------------------------------------------------------------- /hijri_test.go: -------------------------------------------------------------------------------- 1 | package hijri_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/hablullah/go-hijri" 9 | ) 10 | 11 | var hijriTestData []TestData 12 | 13 | func init() { 14 | var err error 15 | hijriTestData, err = generateTestData("test/hijri.csv") 16 | if err != nil { 17 | panic(err) 18 | } 19 | } 20 | 21 | func Test_Hijri_ConvertDate(t *testing.T) { 22 | if len(hijriTestData) == 0 { 23 | t.Fatal("no tests available for Hijri") 24 | } 25 | 26 | for _, data := range hijriTestData { 27 | gregorianDate, _ := time.Parse("2006-01-02", data.Gregorian) 28 | hijriDate, _ := hijri.CreateHijriDate(gregorianDate, hijri.Default) 29 | strHijriDate := fmt.Sprintf("%04d-%02d-%02d", 30 | hijriDate.Year, 31 | hijriDate.Month, 32 | hijriDate.Day) 33 | 34 | if strHijriDate != data.Hijri { 35 | t.Errorf("%s: want %s got %s\n", data.Gregorian, data.Hijri, strHijriDate) 36 | } 37 | } 38 | } 39 | 40 | func Test_Hijri_ToGregorian(t *testing.T) { 41 | if len(hijriTestData) == 0 { 42 | t.Fatal("no tests available for Hijri") 43 | } 44 | 45 | for _, data := range hijriTestData { 46 | var hijriDate hijri.HijriDate 47 | fmt.Sscanf(data.Hijri, "%d-%d-%d", 48 | &hijriDate.Year, 49 | &hijriDate.Month, 50 | &hijriDate.Day) 51 | 52 | result := hijriDate.ToGregorian().Format("2006-01-02") 53 | if result != data.Gregorian { 54 | t.Errorf("%s: want %s got %s\n", data.Hijri, data.Gregorian, result) 55 | } 56 | } 57 | } 58 | 59 | func Test_Hijri_Bidirectional(t *testing.T) { 60 | date := time.Date(622, 7, 16, 0, 0, 0, 0, time.UTC) 61 | for date.Year() <= 2120 { 62 | // Convert date to hijri 63 | hijriDate, err := hijri.CreateHijriDate(date, hijri.Default) 64 | if err != nil { 65 | date = date.AddDate(0, 0, 1) 66 | continue 67 | } 68 | 69 | // Convert back Hijri to Gregorian 70 | gregorianDate := hijriDate.ToGregorian() 71 | 72 | // Compare original and new gregorian 73 | strOriginal := date.Format("2006-01-02") 74 | strGregorian := gregorianDate.Format("2006-01-02") 75 | strHijri := fmt.Sprintf("%04d-%02d-%02d", hijriDate.Year, hijriDate.Month, hijriDate.Day) 76 | 77 | if strOriginal != strGregorian { 78 | t.Errorf("Original %s: Hijri %s, Gregorian %s\n", 79 | strOriginal, strHijri, strGregorian) 80 | } 81 | 82 | // Increase date 83 | date = date.AddDate(0, 0, 1) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /ummalqura_test.go: -------------------------------------------------------------------------------- 1 | package hijri_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/hablullah/go-hijri" 9 | ) 10 | 11 | var ummAlQuraTestData []TestData 12 | 13 | func init() { 14 | var err error 15 | ummAlQuraTestData, err = generateTestData("test/ummalqura.csv") 16 | if err != nil { 17 | panic(err) 18 | } 19 | } 20 | 21 | func Test_UmmAlQura_ConvertDate(t *testing.T) { 22 | if len(ummAlQuraTestData) == 0 { 23 | t.Fatal("no tests available for Umm al-Qura") 24 | } 25 | 26 | for _, data := range ummAlQuraTestData { 27 | gregorianDate, _ := time.Parse("2006-01-02", data.Gregorian) 28 | ummAlQuraDate, _ := hijri.CreateUmmAlQuraDate(gregorianDate) 29 | strUmmAlQuraDate := fmt.Sprintf("%04d-%02d-%02d", 30 | ummAlQuraDate.Year, 31 | ummAlQuraDate.Month, 32 | ummAlQuraDate.Day) 33 | 34 | if strUmmAlQuraDate != data.Hijri { 35 | t.Errorf("%s: want %s got %s\n", data.Gregorian, data.Hijri, strUmmAlQuraDate) 36 | } 37 | } 38 | } 39 | 40 | func Test_UmmAlQura_ToGregorian(t *testing.T) { 41 | if len(ummAlQuraTestData) == 0 { 42 | t.Fatal("no tests available for Umm al-Qura") 43 | } 44 | 45 | for _, data := range ummAlQuraTestData { 46 | var ummAlQuraDate hijri.UmmAlQuraDate 47 | fmt.Sscanf(data.Hijri, "%d-%d-%d", 48 | &ummAlQuraDate.Year, 49 | &ummAlQuraDate.Month, 50 | &ummAlQuraDate.Day) 51 | 52 | result := ummAlQuraDate.ToGregorian().Format("2006-01-02") 53 | if result != data.Gregorian { 54 | t.Errorf("%s: want %s got %s\n", data.Hijri, data.Gregorian, result) 55 | } 56 | } 57 | } 58 | 59 | func Test_UmmAlQura_Bidirectional(t *testing.T) { 60 | date := time.Date(1937, 3, 14, 0, 0, 0, 0, time.UTC) 61 | maxDate := time.Date(2077, 11, 17, 0, 0, 0, 0, time.UTC) 62 | for date.Before(maxDate) { 63 | // Convert date to Umm al-Qura 64 | ummAlQuraDate, err := hijri.CreateUmmAlQuraDate(date) 65 | if err != nil { 66 | date = date.AddDate(0, 0, 1) 67 | continue 68 | } 69 | 70 | // Convert back Umm al-Qura to Gregorian 71 | gregorianDate := ummAlQuraDate.ToGregorian() 72 | 73 | // Compare original and new gregorian 74 | strOriginal := date.Format("2006-01-02") 75 | strGregorian := gregorianDate.Format("2006-01-02") 76 | strHijri := fmt.Sprintf("%04d-%02d-%02d", ummAlQuraDate.Year, ummAlQuraDate.Month, ummAlQuraDate.Day) 77 | 78 | if strOriginal != strGregorian { 79 | t.Errorf("Original %s: Hijri %s, Gregorian %s\n", 80 | strOriginal, strHijri, strGregorian) 81 | } 82 | 83 | // Increase date 84 | date = date.AddDate(0, 0, 1) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go-Hijri 2 | 3 | [![Go Report Card][report-badge]][report-url] 4 | [![Go Reference][doc-badge]][doc-url] 5 | 6 | Go-Hijri is a Go package for converting Gregorian date to Hijrian date and vice-versa. There are two supported Hijrian calendar : 7 | 8 | - the arithmetic calendar which calculated based on arithmetic rules rather than by observation or astronomical calculation, 9 | - the Umm al-Qura calendar which calculated using astronomical rules that used and created by Saudi Arabia and most Islamic country. 10 | 11 | ## Usage Examples 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "time" 19 | 20 | "github.com/hablullah/go-hijri" 21 | ) 22 | 23 | func main() { 24 | // 1 January 2020 to arithmetic Hijri calendar 25 | newYear := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) 26 | hijriDate, _ := hijri.CreateHijriDate(newYear, hijri.Default) 27 | fmt.Printf("%s AD = %04d-%02d-%02d H (arithmetic)\n", 28 | newYear.Format("2006-01-02"), 29 | hijriDate.Year, 30 | hijriDate.Month, 31 | hijriDate.Day) 32 | 33 | // 1 January 2019 to Umm al-Qura calendar 34 | ummAlQuraDate, _ := hijri.CreateUmmAlQuraDate(newYear) 35 | fmt.Printf("%s AD = %s, %04d-%02d-%02d H (Umm al-Qura)\n", 36 | newYear.Format("2006-01-02"), 37 | ummAlQuraDate.Weekday.String(), 38 | ummAlQuraDate.Year, 39 | ummAlQuraDate.Month, 40 | ummAlQuraDate.Day) 41 | 42 | // 1 Ramadhan 1410 arithmetic Hijri to Gregorian 43 | stdRamadhan := hijri.HijriDate{Year: 1410, Month: 9, Day: 1} 44 | fmt.Printf("1410-09-01 H (arithmetic) = %s AD\n", 45 | stdRamadhan.ToGregorian().Format("2006-01-02")) 46 | 47 | // 1 Ramadhan 1442 Umm al-Qura to Gregorian 48 | ummAlQuraRamadhan := hijri.UmmAlQuraDate{Year: 1410, Month: 9, Day: 1} 49 | fmt.Printf("1410-09-01 H (Umm al-Qura) = %s AD\n", 50 | ummAlQuraRamadhan.ToGregorian().Format("2006-01-02")) 51 | } 52 | ``` 53 | 54 | Codes above will give us following results : 55 | 56 | ``` 57 | 2020-01-01 AD = 1441-05-05 H (arithmetic) 58 | 2020-01-01 AD = Thursday, 1441-05-06 H (Umm al-Qura) 59 | 1410-09-01 H (arithmetic) = 1990-03-28 AD 60 | 1410-09-01 H (Umm al-Qura) = 1990-03-27 AD 61 | ``` 62 | 63 | ## Resource 64 | 65 | 1. Anugraha, R. 2012. _Mekanika Benda Langit_. ([PDF][pdf-rinto-anugraha]) 66 | 2. Van Gent, R. H. 2019. _Islamic-Western Calendar Converter_. ([Website][web-van-gent-1]) 67 | 3. Van Gent, R. H. 2019. _The Umm al-Qura Calendar of Saudi Arabia_. ([Website][web-van-gent-2]) 68 | 4. Strous, Dr. 2019. _Astronomy Answers: Julian Days Number_. ([Website][web-astronomy-answers]) 69 | 70 | ## License 71 | 72 | Go-Hijri is distributed using [MIT](http://choosealicense.com/licenses/mit/) license. 73 | 74 | [report-badge]: https://goreportcard.com/badge/github.com/hablullah/go-hijri 75 | [report-url]: https://goreportcard.com/report/github.com/hablullah/go-hijri 76 | [doc-badge]: https://pkg.go.dev/badge/github.com/hablullah/go-hijri.svg 77 | [doc-url]: https://pkg.go.dev/github.com/hablullah/go-hijri 78 | 79 | [pdf-rinto-anugraha]: https://simpan.ugm.ac.id/s/GcxKuyZWn8Rshnn 80 | [web-van-gent-1]: https://webspace.science.uu.nl/~gent0113/islam/islam_tabcal.htm 81 | [web-van-gent-2]: https://webspace.science.uu.nl/~gent0113/islam/ummalqura.htm 82 | [web-astronomy-answers]: https://www.aa.quae.nl/en/reken/juliaansedag.html -------------------------------------------------------------------------------- /hijri.go: -------------------------------------------------------------------------------- 1 | package hijri 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/hablullah/go-juliandays" 8 | ) 9 | 10 | // LeapYearsPattern is patterns of leap years in the 30 year cycle. 11 | type LeapYearsPattern uint8 12 | 13 | const ( 14 | // Default is the most commonly used leap years pattern. In this pattern, leap year happened 15 | // on years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26 & 29. 16 | Default LeapYearsPattern = iota 17 | 18 | // Base15 is leap years pattern that used by Microsoft, and they named it as "Kuwaiti algorithm". 19 | // In this pattern, leap year happened on years 2, 5, 7, 10, 13, 15, 18, 21, 24, 26 & 29. 20 | Base15 21 | 22 | // Fattimid is leap years pattern that used in Fattimid empire. In this pattern, leap year 23 | // happened on years 2, 5, 8, 10, 13, 16, 19, 21, 24, 27 & 29. 24 | Fattimid 25 | 26 | // HabashAlHasib is leap years pattern that created using research from Habash al-Hasib, 27 | // an astronomer from Abbasid empire (766-869 in Iraq). In this pattern, leap year happened on 28 | // years 2, 5, 8, 11, 13, 16, 19, 21, 24, 27 & 30. 29 | HabashAlHasib 30 | ) 31 | 32 | // HijriDate is date that uses arithmetic Islamic calendar system. 33 | type HijriDate struct { 34 | Day int64 35 | Month int64 36 | Year int64 37 | Pattern LeapYearsPattern 38 | } 39 | 40 | // CreateHijriDate converts normal Gregorian date to Hijri date. Since Hijri calendar is not proleptic 41 | // any date before 16 July 622 CE (1 Muharram 1 H) will make this method throws error. 42 | func CreateHijriDate(date time.Time, leapPattern LeapYearsPattern) (HijriDate, error) { 43 | // Convert date to UTC and strip times from the date 44 | date = date.UTC() 45 | date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, time.UTC) 46 | 47 | // Calculate Julian Days 48 | julianDays, err := juliandays.FromTime(date) 49 | if err != nil { 50 | return HijriDate{}, err 51 | } 52 | 53 | // Get days since 1 Muharram 1 54 | islamicDays := int64(julianDays - 1948438.5) 55 | if islamicDays < 0 { 56 | return HijriDate{}, errors.New("date is before hijri calendar started") 57 | } 58 | 59 | // Check how many 30 years cycles to reach this day 60 | nCycles := islamicDays / 10631 61 | 62 | // Calculate leftover years outside 30 years cycle 63 | leftoverDays := islamicDays % 10631 64 | leftoverYears := leftoverDays / 354 65 | 66 | // Calculate the leftover days after years subtracted 67 | leftoverDays = leftoverDays % 354 68 | 69 | // Adjust leftover days based on leap years that happened within leftover years 70 | for year := int64(1); year <= leftoverYears; year++ { 71 | if isLeapYear(year, leapPattern) { 72 | leftoverDays-- 73 | } 74 | } 75 | 76 | // Calculate final hijri year 77 | hijriYear := nCycles*30 + leftoverYears 78 | if leftoverDays > 0 { 79 | hijriYear++ 80 | } else { 81 | leftoverDays += 354 82 | if isLeapYear(hijriYear, leapPattern) { 83 | leftoverDays++ 84 | } 85 | } 86 | 87 | // Calculate final hijri month and day 88 | var hijriDay, hijriMonth int64 89 | inLeapYear := isLeapYear(hijriYear, leapPattern) 90 | 91 | for month := int64(1); month <= 12; month++ { 92 | hijriMonth = month 93 | daysInMonth := int64(29 + month%2) 94 | if inLeapYear && month == 12 { 95 | daysInMonth = 30 96 | } 97 | 98 | leftoverDays -= daysInMonth 99 | if leftoverDays <= 0 { 100 | hijriDay = leftoverDays + daysInMonth 101 | break 102 | } 103 | } 104 | 105 | return HijriDate{ 106 | Day: hijriDay, 107 | Month: hijriMonth, 108 | Year: hijriYear, 109 | Pattern: leapPattern, 110 | }, nil 111 | } 112 | 113 | // ToGregorian convert Hijri date to Gregorian date using Golang standard time. 114 | func (h HijriDate) ToGregorian() time.Time { 115 | // Calculate the passed days from the passed hijri years 116 | passedYear := h.Year - 1 117 | nCycles := passedYear / 30 118 | leftoverYears := passedYear % 30 119 | passedDays := nCycles*10631 + leftoverYears*354 120 | 121 | // Consider leap years to the count of passed days 122 | for year := int64(1); year <= leftoverYears; year++ { 123 | if isLeapYear(year, h.Pattern) { 124 | passedDays++ 125 | } 126 | } 127 | 128 | // Increase the passed days from the passed hijri months 129 | passedMonths := h.Month - 1 130 | for month := int64(1); month <= passedMonths; month++ { 131 | passedDays += 29 + month%2 132 | } 133 | 134 | // Increase passed days using current hijri day 135 | passedDays += h.Day 136 | 137 | // Calculate Julian Days since Hijri epoch 138 | jd := 1948438.5 + float64(passedDays) 139 | return juliandays.ToTime(jd) 140 | } 141 | 142 | func isLeapYear(year int64, pattern LeapYearsPattern) bool { 143 | year = year % 30 144 | 145 | switch pattern { 146 | case Default: 147 | switch year { 148 | case 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29: 149 | return true 150 | } 151 | 152 | case Base15: 153 | switch year { 154 | case 2, 5, 7, 10, 13, 15, 18, 21, 24, 26, 29: 155 | return true 156 | } 157 | 158 | case Fattimid: 159 | switch year { 160 | case 2, 5, 8, 10, 13, 16, 19, 21, 24, 27, 29: 161 | return true 162 | } 163 | 164 | case HabashAlHasib: 165 | switch year { 166 | case 2, 5, 8, 11, 13, 16, 19, 21, 24, 27, 30: 167 | return true 168 | } 169 | } 170 | 171 | return false 172 | } 173 | -------------------------------------------------------------------------------- /ummalqura.go: -------------------------------------------------------------------------------- 1 | package hijri 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "time" 7 | 8 | "github.com/hablullah/go-juliandays" 9 | ) 10 | 11 | // UmmAlQuraDate is a date that uses astronomical-based Islamic calendar system that used in Saudi Arabia. 12 | type UmmAlQuraDate struct { 13 | Day int64 14 | Month int64 15 | Year int64 16 | Weekday time.Weekday 17 | } 18 | 19 | // CreateUmmAlQuraDate converts Gregorian date to Umm al-Qura date. 20 | func CreateUmmAlQuraDate(date time.Time) (UmmAlQuraDate, error) { 21 | // Convert date to UTC and set the time to noon 22 | date = date.UTC() 23 | date = time.Date(date.Year(), date.Month(), date.Day(), 12, 0, 0, 0, time.UTC) 24 | 25 | // Make sure date within allowed scope 26 | endOfUmmAlQura := time.Date(2077, 11, 16, 23, 59, 59, 0, time.UTC) 27 | startOfUmmAlQura := time.Date(1937, 3, 14, 0, 0, 0, 0, time.UTC) 28 | if date.After(endOfUmmAlQura) || date.Before(startOfUmmAlQura) { 29 | return UmmAlQuraDate{}, errors.New("date is outside Umm al-Qura scope") 30 | } 31 | 32 | // Calculate Julian Days (JD) 33 | jd, err := juliandays.FromTime(date) 34 | if err != nil { 35 | return UmmAlQuraDate{}, err 36 | } 37 | 38 | // Convert Julian Days to its Chronological Number (CJDN) 39 | cjdn := int64(jd) 40 | 41 | // From CJDN, calculate Modified Chronological Julian Date Number (MCJDN). MCJDN is a modification of 42 | // CJDN that used to simplify the notation. For more detail, check 43 | // http://www.csgnetwork.com/julianmodifdateconv.html 44 | mcjdn := cjdn - 2400000 45 | 46 | // Get the start of lunations day according to MCJDN data from the Umm al-Qura calendar. 47 | lunationIdx := -1 48 | for i := 0; i < len(ummalQuraLunationMCJDN); i++ { 49 | if ummalQuraLunationMCJDN[i] > mcjdn { 50 | lunationIdx = i 51 | break 52 | } 53 | } 54 | 55 | iln := float64(lunationIdx) + 16260 56 | ii := math.Floor((iln - 1) / 12) 57 | year := int64(ii + 1) 58 | month := int64(iln - 12*ii) 59 | day := mcjdn - ummalQuraLunationMCJDN[lunationIdx-1] + 1 60 | 61 | // Get weekday 62 | weekday := (((cjdn + 1%7) + 7) % 7) + 1 63 | 64 | return UmmAlQuraDate{ 65 | Day: day, 66 | Month: month, 67 | Year: year, 68 | Weekday: time.Weekday(weekday), 69 | }, nil 70 | } 71 | 72 | // ToGregorian convert Umm al-Qura date to Gregorian date using Golang standard time. 73 | func (uq UmmAlQuraDate) ToGregorian() time.Time { 74 | // Get lunation index 75 | ii := uq.Year - 1 76 | iln := uq.Month + 12*ii 77 | lunationIdx := iln - 16260 78 | 79 | // Get the Julian Days 80 | mcjdn := uq.Day - 1 + ummalQuraLunationMCJDN[lunationIdx-1] 81 | cjdn := mcjdn + 2400000 82 | jd := float64(cjdn) - 0.5 83 | 84 | return juliandays.ToTime(jd) 85 | } 86 | 87 | var ummalQuraLunationMCJDN = []int64{ 88 | 28607, 28636, 28665, 28695, 28724, 28754, 28783, 28813, 28843, 28872, 28901, 28931, 28960, 28990, 29019, 29049, 29078, 29108, 29137, 29167, 89 | 29196, 29226, 29255, 29285, 29315, 29345, 29375, 29404, 29434, 29463, 29492, 29522, 29551, 29580, 29610, 29640, 29669, 29699, 29729, 29759, 90 | 29788, 29818, 29847, 29876, 29906, 29935, 29964, 29994, 30023, 30053, 30082, 30112, 30141, 30171, 30200, 30230, 30259, 30289, 30318, 30348, 91 | 30378, 30408, 30437, 30467, 30496, 30526, 30555, 30585, 30614, 30644, 30673, 30703, 30732, 30762, 30791, 30821, 30850, 30880, 30909, 30939, 92 | 30968, 30998, 31027, 31057, 31086, 31116, 31145, 31175, 31204, 31234, 31263, 31293, 31322, 31352, 31381, 31411, 31441, 31471, 31500, 31530, 93 | 31559, 31589, 31618, 31648, 31676, 31706, 31736, 31766, 31795, 31825, 31854, 31884, 31913, 31943, 31972, 32002, 32031, 32061, 32090, 32120, 94 | 32150, 32180, 32209, 32239, 32268, 32298, 32327, 32357, 32386, 32416, 32445, 32475, 32504, 32534, 32563, 32593, 32622, 32652, 32681, 32711, 95 | 32740, 32770, 32799, 32829, 32858, 32888, 32917, 32947, 32976, 33006, 33035, 33065, 33094, 33124, 33153, 33183, 33213, 33243, 33272, 33302, 96 | 33331, 33361, 33390, 33420, 33450, 33479, 33509, 33539, 33568, 33598, 33627, 33657, 33686, 33716, 33745, 33775, 33804, 33834, 33863, 33893, 97 | 33922, 33952, 33981, 34011, 34040, 34069, 34099, 34128, 34158, 34187, 34217, 34247, 34277, 34306, 34336, 34365, 34395, 34424, 34454, 34483, 98 | 34512, 34542, 34571, 34601, 34631, 34660, 34690, 34719, 34749, 34778, 34808, 34837, 34867, 34896, 34926, 34955, 34985, 35015, 35044, 35074, 99 | 35103, 35133, 35162, 35192, 35222, 35251, 35280, 35310, 35340, 35370, 35399, 35429, 35458, 35488, 35517, 35547, 35576, 35605, 35635, 35665, 100 | 35694, 35723, 35753, 35782, 35811, 35841, 35871, 35901, 35930, 35960, 35989, 36019, 36048, 36078, 36107, 36136, 36166, 36195, 36225, 36254, 101 | 36284, 36314, 36343, 36373, 36403, 36433, 36462, 36492, 36521, 36551, 36580, 36610, 36639, 36669, 36698, 36728, 36757, 36786, 36816, 36845, 102 | 36875, 36904, 36934, 36963, 36993, 37022, 37052, 37081, 37111, 37141, 37170, 37200, 37229, 37259, 37288, 37318, 37347, 37377, 37406, 37436, 103 | 37465, 37495, 37524, 37554, 37584, 37613, 37643, 37672, 37701, 37731, 37760, 37790, 37819, 37849, 37878, 37908, 37938, 37967, 37997, 38027, 104 | 38056, 38085, 38115, 38144, 38174, 38203, 38233, 38262, 38292, 38322, 38351, 38381, 38410, 38440, 38469, 38499, 38528, 38558, 38587, 38617, 105 | 38646, 38676, 38705, 38735, 38764, 38794, 38823, 38853, 38882, 38912, 38941, 38971, 39001, 39030, 39059, 39089, 39118, 39148, 39178, 39208, 106 | 39237, 39267, 39297, 39326, 39355, 39385, 39414, 39444, 39473, 39503, 39532, 39562, 39592, 39621, 39650, 39680, 39709, 39739, 39768, 39798, 107 | 39827, 39857, 39886, 39916, 39946, 39975, 40005, 40035, 40064, 40094, 40123, 40153, 40182, 40212, 40241, 40271, 40300, 40330, 40359, 40389, 108 | 40418, 40448, 40477, 40507, 40536, 40566, 40595, 40625, 40655, 40685, 40714, 40744, 40773, 40803, 40832, 40862, 40892, 40921, 40951, 40980, 109 | 41009, 41039, 41068, 41098, 41127, 41157, 41186, 41216, 41245, 41275, 41304, 41334, 41364, 41393, 41422, 41452, 41481, 41511, 41540, 41570, 110 | 41599, 41629, 41658, 41688, 41718, 41748, 41777, 41807, 41836, 41865, 41894, 41924, 41953, 41983, 42012, 42042, 42072, 42102, 42131, 42161, 111 | 42190, 42220, 42249, 42279, 42308, 42337, 42367, 42397, 42426, 42456, 42485, 42515, 42545, 42574, 42604, 42633, 42662, 42692, 42721, 42751, 112 | 42780, 42810, 42839, 42869, 42899, 42929, 42958, 42988, 43017, 43046, 43076, 43105, 43135, 43164, 43194, 43223, 43253, 43283, 43312, 43342, 113 | 43371, 43401, 43430, 43460, 43489, 43519, 43548, 43578, 43607, 43637, 43666, 43696, 43726, 43755, 43785, 43814, 43844, 43873, 43903, 43932, 114 | 43962, 43991, 44021, 44050, 44080, 44109, 44139, 44169, 44198, 44228, 44258, 44287, 44317, 44346, 44375, 44405, 44434, 44464, 44493, 44523, 115 | 44553, 44582, 44612, 44641, 44671, 44700, 44730, 44759, 44788, 44818, 44847, 44877, 44906, 44936, 44966, 44996, 45025, 45055, 45084, 45114, 116 | 45143, 45172, 45202, 45231, 45261, 45290, 45320, 45350, 45380, 45409, 45439, 45468, 45498, 45527, 45556, 45586, 45615, 45644, 45674, 45704, 117 | 45733, 45763, 45793, 45823, 45852, 45882, 45911, 45940, 45970, 45999, 46028, 46058, 46088, 46117, 46147, 46177, 46206, 46236, 46265, 46295, 118 | 46324, 46354, 46383, 46413, 46442, 46472, 46501, 46531, 46560, 46590, 46620, 46649, 46679, 46708, 46738, 46767, 46797, 46826, 46856, 46885, 119 | 46915, 46944, 46974, 47003, 47033, 47063, 47092, 47122, 47151, 47181, 47210, 47240, 47269, 47298, 47328, 47357, 47387, 47417, 47446, 47476, 120 | 47506, 47535, 47565, 47594, 47624, 47653, 47682, 47712, 47741, 47771, 47800, 47830, 47860, 47890, 47919, 47949, 47978, 48008, 48037, 48066, 121 | 48096, 48125, 48155, 48184, 48214, 48244, 48273, 48303, 48333, 48362, 48392, 48421, 48450, 48480, 48509, 48538, 48568, 48598, 48627, 48657, 122 | 48687, 48717, 48746, 48776, 48805, 48834, 48864, 48893, 48922, 48952, 48982, 49011, 49041, 49071, 49100, 49130, 49160, 49189, 49218, 49248, 123 | 49277, 49306, 49336, 49365, 49395, 49425, 49455, 49484, 49514, 49543, 49573, 49602, 49632, 49661, 49690, 49720, 49749, 49779, 49809, 49838, 124 | 49868, 49898, 49927, 49957, 49986, 50016, 50045, 50075, 50104, 50133, 50163, 50192, 50222, 50252, 50281, 50311, 50340, 50370, 50400, 50429, 125 | 50459, 50488, 50518, 50547, 50576, 50606, 50635, 50665, 50694, 50724, 50754, 50784, 50813, 50843, 50872, 50902, 50931, 50960, 50990, 51019, 126 | 51049, 51078, 51108, 51138, 51167, 51197, 51227, 51256, 51286, 51315, 51345, 51374, 51403, 51433, 51462, 51492, 51522, 51552, 51582, 51611, 127 | 51641, 51670, 51699, 51729, 51758, 51787, 51816, 51846, 51876, 51906, 51936, 51965, 51995, 52025, 52054, 52083, 52113, 52142, 52171, 52200, 128 | 52230, 52260, 52290, 52319, 52349, 52379, 52408, 52438, 52467, 52497, 52526, 52555, 52585, 52614, 52644, 52673, 52703, 52733, 52762, 52792, 129 | 52822, 52851, 52881, 52910, 52939, 52969, 52998, 53028, 53057, 53087, 53116, 53146, 53176, 53205, 53235, 53264, 53294, 53324, 53353, 53383, 130 | 53412, 53441, 53471, 53500, 53530, 53559, 53589, 53619, 53648, 53678, 53708, 53737, 53767, 53796, 53825, 53855, 53884, 53914, 53943, 53973, 131 | 54003, 54032, 54062, 54092, 54121, 54151, 54180, 54209, 54239, 54268, 54297, 54327, 54357, 54387, 54416, 54446, 54476, 54505, 54535, 54564, 132 | 54593, 54623, 54652, 54681, 54711, 54741, 54770, 54800, 54830, 54859, 54889, 54919, 54948, 54977, 55007, 55036, 55066, 55095, 55125, 55154, 133 | 55184, 55213, 55243, 55273, 55302, 55332, 55361, 55391, 55420, 55450, 55479, 55508, 55538, 55567, 55597, 55627, 55657, 55686, 55716, 55745, 134 | 55775, 55804, 55834, 55863, 55892, 55922, 55951, 55981, 56011, 56040, 56070, 56100, 56129, 56159, 56188, 56218, 56247, 56276, 56306, 56335, 135 | 56365, 56394, 56424, 56454, 56483, 56513, 56543, 56572, 56601, 56631, 56660, 56690, 56719, 56749, 56778, 56808, 56837, 56867, 56897, 56926, 136 | 56956, 56985, 57015, 57044, 57074, 57103, 57133, 57162, 57192, 57221, 57251, 57280, 57310, 57340, 57369, 57399, 57429, 57458, 57487, 57517, 137 | 57546, 57576, 57605, 57634, 57664, 57694, 57723, 57753, 57783, 57813, 57842, 57871, 57901, 57930, 57959, 57989, 58018, 58048, 58077, 58107, 138 | 58137, 58167, 58196, 58226, 58255, 58285, 58314, 58343, 58373, 58402, 58432, 58461, 58491, 58521, 58551, 58580, 58610, 58639, 58669, 58698, 139 | 58727, 58757, 58786, 58816, 58845, 58875, 58905, 58934, 58964, 58994, 59023, 59053, 59082, 59111, 59141, 59170, 59200, 59229, 59259, 59288, 140 | 59318, 59348, 59377, 59407, 59436, 59466, 59495, 59525, 59554, 59584, 59613, 59643, 59672, 59702, 59731, 59761, 59791, 59820, 59850, 59879, 141 | 59909, 59939, 59968, 59997, 60027, 60056, 60086, 60115, 60145, 60174, 60204, 60234, 60264, 60293, 60323, 60352, 60381, 60411, 60440, 60469, 142 | 60499, 60528, 60558, 60588, 60618, 60647, 60677, 60707, 60736, 60765, 60795, 60824, 60853, 60883, 60912, 60942, 60972, 61002, 61031, 61061, 143 | 61090, 61120, 61149, 61179, 61208, 61237, 61267, 61296, 61326, 61356, 61385, 61415, 61445, 61474, 61504, 61533, 61563, 61592, 61621, 61651, 144 | 61680, 61710, 61739, 61769, 61799, 61828, 61858, 61888, 61917, 61947, 61976, 62006, 62035, 62064, 62094, 62123, 62153, 62182, 62212, 62242, 145 | 62271, 62301, 62331, 62360, 62390, 62419, 62448, 62478, 62507, 62537, 62566, 62596, 62625, 62655, 62685, 62715, 62744, 62774, 62803, 62832, 146 | 62862, 62891, 62921, 62950, 62980, 63009, 63039, 63069, 63099, 63128, 63157, 63187, 63216, 63246, 63275, 63305, 63334, 63363, 63393, 63423, 147 | 63453, 63482, 63512, 63541, 63571, 63600, 63630, 63659, 63689, 63718, 63747, 63777, 63807, 63836, 63866, 63895, 63925, 63955, 63984, 64014, 148 | 64043, 64073, 64102, 64131, 64161, 64190, 64220, 64249, 64279, 64309, 64339, 64368, 64398, 64427, 64457, 64486, 64515, 64545, 64574, 64603, 149 | 64633, 64663, 64692, 64722, 64752, 64782, 64811, 64841, 64870, 64899, 64929, 64958, 64987, 65017, 65047, 65076, 65106, 65136, 65166, 65195, 150 | 65225, 65254, 65283, 65313, 65342, 65371, 65401, 65431, 65460, 65490, 65520, 65549, 65579, 65608, 65638, 65667, 65697, 65726, 65755, 65785, 151 | 65815, 65844, 65874, 65903, 65933, 65963, 65992, 66022, 66051, 66081, 66110, 66140, 66169, 66199, 66228, 66258, 66287, 66317, 66346, 66376, 152 | 66405, 66435, 66465, 66494, 66524, 66553, 66583, 66612, 66641, 66671, 66700, 66730, 66760, 66789, 66819, 66849, 66878, 66908, 66937, 66967, 153 | 66996, 67025, 67055, 67084, 67114, 67143, 67173, 67203, 67233, 67262, 67292, 67321, 67351, 67380, 67409, 67439, 67468, 67497, 67527, 67557, 154 | 67587, 67617, 67646, 67676, 67705, 67735, 67764, 67793, 67823, 67852, 67882, 67911, 67941, 67971, 68000, 68030, 68060, 68089, 68119, 68148, 155 | 68177, 68207, 68236, 68266, 68295, 68325, 68354, 68384, 68414, 68443, 68473, 68502, 68532, 68561, 68591, 68620, 68650, 68679, 68708, 68738, 156 | 68768, 68797, 68827, 68857, 68886, 68916, 68946, 68975, 69004, 69034, 69063, 69092, 69122, 69152, 69181, 69211, 69240, 69270, 69300, 69330, 157 | 69359, 69388, 69418, 69447, 69476, 69506, 69535, 69565, 69595, 69624, 69654, 69684, 69713, 69743, 69772, 69802, 69831, 69861, 69890, 69919, 158 | 69949, 69978, 70008, 70038, 70067, 70097, 70126, 70156, 70186, 70215, 70245, 70274, 70303, 70333, 70362, 70392, 70421, 70451, 70481, 70510, 159 | 70540, 70570, 70599, 70629, 70658, 70687, 70717, 70746, 70776, 70805, 70835, 70864, 70894, 70924, 70954, 70983, 71013, 71042, 71071, 71101, 160 | 71130, 71159, 71189, 71218, 71248, 71278, 71308, 71337, 71367, 71397, 71426, 71455, 71485, 71514, 71543, 71573, 71602, 71632, 71662, 71691, 161 | 71721, 71751, 71781, 71810, 71839, 71869, 71898, 71927, 71957, 71986, 72016, 72046, 72075, 72105, 72135, 72164, 72194, 72223, 72253, 72282, 162 | 72311, 72341, 72370, 72400, 72429, 72459, 72489, 72518, 72548, 72577, 72607, 72637, 72666, 72695, 72725, 72754, 72784, 72813, 72843, 72872, 163 | 72902, 72931, 72961, 72991, 73020, 73050, 73080, 73109, 73139, 73168, 73197, 73227, 73256, 73286, 73315, 73345, 73375, 73404, 73434, 73464, 164 | 73493, 73523, 73552, 73581, 73611, 73640, 73669, 73699, 73729, 73758, 73788, 73818, 73848, 73877, 73907, 73936, 73965, 73995, 74024, 74053, 165 | 74083, 74113, 74142, 74172, 74202, 74231, 74261, 74291, 74320, 74349, 74379, 74408, 74437, 74467, 74497, 74526, 74556, 74585, 74615, 74645, 166 | 74675, 74704, 74733, 74763, 74792, 74822, 74851, 74881, 74910, 74940, 74969, 74999, 75029, 75058, 75088, 75117, 75147, 75176, 75206, 75235, 167 | 75264, 75294, 75323, 75353, 75383, 75412, 75442, 75472, 75501, 75531, 75560, 75590, 75619, 75648, 75678, 75707, 75737, 75766, 75796, 75826, 168 | 75856, 75885, 75915, 75944, 75974, 76003, 76032, 76062, 76091, 76121, 76150, 76180, 76210, 76239, 76269, 76299, 76328, 76358, 76387, 76416, 169 | 76446, 76475, 76505, 76534, 76564, 76593, 76623, 76653, 76682, 76712, 76741, 76771, 76801, 76830, 76859, 76889, 76918, 76948, 76977, 77007, 170 | 77036, 77066, 77096, 77125, 77155, 77185, 77214, 77243, 77273, 77302, 77332, 77361, 77390, 77420, 77450, 77479, 77509, 77539, 77569, 77598, 171 | 77627, 77657, 77686, 77715, 77745, 77774, 77804, 77833, 77863, 77893, 77923, 77952, 77982, 78011, 78041, 78070, 78099, 78129, 78158, 78188, 172 | 78217, 78247, 78277, 78307, 78336, 78366, 78395, 78425, 78454, 78483, 78513, 78542, 78572, 78601, 78631, 78661, 78690, 78720, 78750, 78779, 173 | 78808, 78838, 78867, 78897, 78926, 78956, 78985, 79015, 79044, 79074, 79104, 79133, 79163, 79192, 79222, 79251, 79281, 79310, 79340, 79369, 174 | 79399, 79428, 79458, 79487, 79517, 79546, 79576, 79606, 79635, 79665, 79695, 79724, 79753, 79783, 79812, 79841, 79871, 79900, 79930, 79960, 175 | 79990, 176 | } 177 | --------------------------------------------------------------------------------