github.com/go-chrono/chrono@v0.0.0-20240102183611-532f0d0d7c34/local_date.go (about) 1 package chrono 2 3 // LocalDate is a date without a time zone or time component, according to ISO 8601. 4 // It represents a year-month-day in the proleptic Gregorian calendar, 5 // but cannot represent an instant on a timeline without additional time offset information. 6 // 7 // The date is encoded as a Julian Day Number (JDN). Since LocalDate is stored as an integer, 8 // any two LocalDates can be compared to each other to determine their relationship, 9 // and the difference can be calculated to determine the number of days between them. 10 // Additionally, standard addition and subtraction operators can be used to shift a LocalDate by a number of days. 11 // 12 // To make usage of LocalDate easier, the default value, 0, represents the date of the Unix epoch, 1st January 1970. 13 // This differs from the Richards interpretation of JDNs, where 0 represents the date 24th November 4714 BCE (24/11/-4713). 14 // 15 // According to ISO 8601, 0000 is a valid year, whereas in the Gregorian calendar, year 0 does not exist. 16 // The user must be aware of this difference when interfacing between LocalDate and the BCE/CE notation. 17 // Thus, when using LocalDate, year 0 is intepreted to mean 1 BCE, and year -1 is 2 BCE, and so on. 18 // In order to format a string according to the Gregorian calendar, use Format("%EY %EC"). 19 type LocalDate int32 20 21 // LocalDateOf returns the LocalDate that represents the specified year, month and day. 22 // This function panics if the provided date would overflow the internal type, 23 // or if it is earlier than the first date that can be represented by this type - 24th November -4713 (4714 BCE). 24 func LocalDateOf(year int, month Month, day int) LocalDate { 25 if !isDateValid(year, int(month), day) { 26 panic("invalid date") 27 } 28 29 out, err := makeDate(year, int(month), day) 30 if err != nil { 31 panic(err.Error()) 32 } 33 return LocalDate(out) 34 } 35 36 // OfDayOfYear returns the LocalDate that represents the specified day of the year. 37 // This function panics if the provided date would overflow the internal type, 38 // or if it is earlier than the first date that can be represented by this type - 24th November -4713 (4714 BCE). 39 func OfDayOfYear(year, day int) LocalDate { 40 d, err := ofDayOfYear(year, day) 41 if err != nil { 42 panic(err.Error()) 43 } 44 return LocalDate(d) 45 } 46 47 // OfFirstWeekday returns the LocalDate that represents the first of the specified weekday of the supplied month and year. 48 // This function panics if the provided date would overflow the internal type, 49 // or if it earlier than the first date that can be represented by this type - 24th November -4713 (4714 BCE). 50 // 51 // By providing January as the month, the result is therefore also the first specified weekday of the year. 52 // And by adding increments of 7 to the result, it is therefore possible to find the nth instance of a particular weekday. 53 func OfFirstWeekday(year int, month Month, weekday Weekday) LocalDate { 54 v := makeJDN(int64(year), int64(month), 1) 55 wd := (v + unixEpochJDN) % 7 56 57 if diff := int64(weekday) - wd - 1; diff < 0 { 58 v += 7 - (diff * -1) 59 } else if diff > 0 { 60 v += diff 61 } 62 63 if v < minJDN || v > maxJDN { 64 panic("invalid date") 65 } 66 return LocalDate(v) 67 } 68 69 // OfISOWeek returns the LocalDate that represents the supplied ISO 8601 year, week number, and weekday. 70 // See LocalDate.ISOWeek for further explanation of ISO week numbers. 71 func OfISOWeek(year, week int, day Weekday) (LocalDate, error) { 72 out, err := ofISOWeek(year, week, int(day)) 73 return LocalDate(out), err 74 } 75 76 // Date returns the ISO 8601 year, month and day represented by d. 77 func (d LocalDate) Date() (year int, month Month, day int) { 78 year, _month, day, err := fromDate(int64(d)) 79 if err != nil { 80 panic(err.Error()) 81 } 82 return year, Month(_month), day 83 } 84 85 // IsLeapYear reports whether d is a leap year (contains 29th February, and thus 266 days instead of 265). 86 func (d LocalDate) IsLeapYear() bool { 87 year, _, _ := d.Date() 88 return isLeapYear(year) 89 } 90 91 // Weekday returns the day of the week specified by d. 92 func (d LocalDate) Weekday() Weekday { 93 return Weekday(getWeekday(int32(d))) 94 } 95 96 // YearDay returns the day of the year specified by d, in the range [1,365] for non-leap years, and [1,366] in leap years. 97 func (d LocalDate) YearDay() int { 98 out, err := getYearDay(int64(d)) 99 if err != nil { 100 panic(err.Error()) 101 } 102 return out 103 } 104 105 // ISOWeek returns the ISO 8601 year and week number in which d occurs. 106 // Week ranges from 1 to 53 (even for years that are not themselves leap years). 107 // Jan 01 to Jan 03 of year n might belong to week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1 of year n+1. 108 func (d LocalDate) ISOWeek() (isoYear, isoWeek int) { 109 var err error 110 if isoYear, isoWeek, err = getISOWeek(int64(d)); err != nil { 111 panic(err.Error()) 112 } 113 return 114 } 115 116 // AddDate returns the date corresponding to adding the given number of years, months, and days to d. 117 func (d LocalDate) AddDate(years, months, days int) LocalDate { 118 out, err := addDateToDate(int64(d), years, months, days) 119 if err != nil { 120 panic(err.Error()) 121 } 122 return LocalDate(out) 123 } 124 125 // CanAddDate returns false if AddDate would panic if passed the same arguments. 126 func (d LocalDate) CanAddDate(years, months, days int) bool { 127 _, err := addDateToDate(int64(d), years, months, days) 128 return err == nil 129 } 130 131 func (d LocalDate) String() string { 132 year, month, day := d.Date() 133 return simpleDateStr(year, int(month), day) 134 } 135 136 // Format returns a textual representation of the date value formatted according to the layout defined by the argument. 137 // See the constants section of the documentation to see how to represent the layout format. 138 // Time format specifiers encountered in the layout results in a panic. 139 func (d LocalDate) Format(layout string) string { 140 out, err := formatDateTimeOffset(layout, (*int32)(&d), nil, nil) 141 if err != nil { 142 panic(err.Error()) 143 } 144 return out 145 } 146 147 // Parse a formatted string and store the value it represents in d. 148 // See the constants section of the documentation to see how to represent the layout format. 149 // Time format specifiers encountered in the layout results in a panic. 150 func (d *LocalDate) Parse(layout, value string) error { 151 v := int64(*d) 152 if err := parseDateAndTime(layout, value, &v, nil, nil); err != nil { 153 return err 154 } 155 156 *d = LocalDate(v) 157 return nil 158 } 159 160 // MinLocalDate returns the earliest supported date. 161 func MinLocalDate() LocalDate { 162 return LocalDate(minJDN) 163 } 164 165 // MaxLocalDate returns the latest supported date. 166 func MaxLocalDate() LocalDate { 167 return LocalDate(maxJDN) 168 }