1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2019-2020 The DragonFly Project. All rights reserved. 5 * 6 * This code is derived from software contributed to The DragonFly Project 7 * by Aaron LI <aly@aaronly.me> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of The DragonFly Project nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific, prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * Reference: 37 * Calendrical Calculations, The Ultimate Edition (4th Edition) 38 * by Edward M. Reingold and Nachum Dershowitz 39 * 2018, Cambridge University Press 40 */ 41 42 /* 43 * Rata Die (R.D.), i.e., fixed date, is 1 at midnight (00:00) local time 44 * on January 1, AD 1 in the proleptic Gregorian calendar. 45 */ 46 47 #include <stdbool.h> 48 49 #include "basics.h" 50 #include "gregorian.h" 51 #include "utils.h" 52 53 /* 54 * Fixed date of the start of the (proleptic) Gregorian calendar. 55 * Ref: Sec.(2.2), Eq.(2.3) 56 */ 57 static const int epoch = 1; 58 59 /* 60 * Return true if $year is a leap year on the Gregorian calendar, 61 * otherwise return false. 62 * Ref: Sec.(2.2), Eq.(2.16) 63 */ 64 bool 65 gregorian_leap_year(int year) 66 { 67 int r4 = mod(year, 4); 68 int r400 = mod(year, 400); 69 return (r4 == 0 && (r400 != 100 && r400 != 200 && r400 != 300)); 70 } 71 72 /* 73 * Calculate the fixed date (RD) equivalent to the Gregorian date $date. 74 * Ref: Sec.(2.2), Eq.(2.17) 75 */ 76 int 77 fixed_from_gregorian(const struct date *date) 78 { 79 int rd = ((epoch - 1) + 365 * (date->year - 1) + 80 div_floor(date->year - 1, 4) - 81 div_floor(date->year - 1, 100) + 82 div_floor(date->year - 1, 400) + 83 div_floor(date->month * 367 - 362, 12)); 84 /* correct for the assumption that February always has 30 days */ 85 if (date->month <= 2) 86 return rd + date->day; 87 else if (gregorian_leap_year(date->year)) 88 return rd + date->day - 1; 89 else 90 return rd + date->day - 2; 91 } 92 93 /* 94 * Calculate the fixed date of January 1 in year $year. 95 * Ref: Sec.(2.2), Eq.(2.18) 96 */ 97 int 98 gregorian_new_year(int year) 99 { 100 struct date date = { year, 1, 1 }; 101 return fixed_from_gregorian(&date); 102 } 103 104 /* 105 * Calculate the Gregorian year corresponding to the fixed date $rd. 106 * Ref: Sec.(2.2), Eq.(2.21) 107 */ 108 int 109 gregorian_year_from_fixed(int rd) 110 { 111 int d0 = rd - epoch; 112 int d1 = mod(d0, 146097); 113 int d2 = mod(d1, 36524); 114 int d3 = mod(d2, 1461); 115 116 int n400 = div_floor(d0, 146097); 117 int n100 = div_floor(d1, 36524); 118 int n4 = div_floor(d2, 1461); 119 int n1 = div_floor(d3, 365); 120 121 int year = 400 * n400 + 100 * n100 + 4 * n4 + n1; 122 if (n100 == 4 || n1 == 4) 123 return year; 124 else 125 return year + 1; 126 } 127 128 /* 129 * Number of days from Gregorian date $date1 until $date2. 130 * Ref: Sec.(2.2), Eq.(2.24) 131 */ 132 int 133 gregorian_date_difference(const struct date *date1, 134 const struct date *date2) 135 { 136 return fixed_from_gregorian(date2) - fixed_from_gregorian(date1); 137 } 138 139 /* 140 * Calculate the Gregorian date (year, month, day) corresponding to the 141 * fixed date $rd. 142 * Ref: Sec.(2.2), Eq.(2.23) 143 */ 144 void 145 gregorian_from_fixed(int rd, struct date *date) 146 { 147 int correction, pdays; 148 149 date->year = gregorian_year_from_fixed(rd); 150 151 struct date d = { date->year, 3, 1 }; 152 if (rd < fixed_from_gregorian(&d)) 153 correction = 0; 154 else if (gregorian_leap_year(date->year)) 155 correction = 1; 156 else 157 correction = 2; 158 159 d.month = 1; 160 pdays = rd - fixed_from_gregorian(&d); 161 date->month = div_floor(12 * (pdays + correction) + 373, 367); 162 163 d.month = date->month; 164 date->day = rd - fixed_from_gregorian(&d) + 1; 165 } 166