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  }