1
/* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2008 Sun Microsystems
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
* Common functions for dealing with calendrical calculations
27
#include "drizzled/global.h"
28
#include "drizzled/calendar.h"
30
/** Static arrays for number of days in a month and their "day ends" */
31
static const uint32_t __leap_days_in_month[12]= {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
32
static const uint32_t __normal_days_in_month[12]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
33
static const uint32_t __leap_days_to_end_month[13]= {0, 31, 60, 91, 121, 151, 182, 213, 244, 274, 305, 335, 366};
34
static const uint32_t __normal_days_to_end_month[13]= {0, 31, 59, 90, 120, 150, 181, 212, 243, 273, 304, 334, 365};
37
* Private utility macro for enabling a switch between
38
* Gregorian and Julian leap year date arrays.
40
#define __DAYS_IN_MONTH(y, c) (const uint32_t *) (IS_LEAP_YEAR((y),(c)) ? __leap_days_in_month : __normal_days_in_month)
41
#define __DAYS_TO_END_MONTH(y, c) (const uint32_t *) (IS_LEAP_YEAR((y),(c)) ? __leap_days_to_end_month : __normal_days_to_end_month)
44
* Calculates the Julian Day Number from the year, month
45
* and day at noon supplied in the Julian calendar.
47
* The following formula is used to calculate the Julian
48
* Day Number from a date in the Julian Calendar.
50
* The months January to December are 1 to 12.
51
* Astronomical year numbering is used, thus 1 BC is 0, 2 BC is −1,
52
* and 4713 BC is −4712. In all divisions (except for JD) the floor
53
* function is applied to the quotient (for dates since
54
* March 1, −4800 all quotients are non-negative, so we can also
57
* a = (14 - month) / 12
60
* JDN = day + ((153m + 2) / 5) + 365y + (y / 4) - 32083
62
* @cite http://en.wikipedia.org/wiki/Julian_day#Calculation
66
* Year month and day values are assumed to be valid. This
67
* method does no bounds checking or validation.
70
* @param Month of date
73
int64_t julian_day_number_from_julian_date(uint32_t year, uint32_t month, uint32_t day)
76
int64_t a= (14 - month) / 12;
77
int64_t y= year + 4800 - a;
78
int64_t m= month + (12 * a) - 3;
80
day_number= day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - 32083;
85
* Calculates the Julian Day Number from the year, month
86
* and day supplied. The calendar used by the supplied
87
* year, month, and day is assumed to be Gregorian Proleptic.
89
* The months January to December are 1 to 12.
90
* Astronomical year numbering is used, thus 1 BC is 0, 2 BC is −1,
91
* and 4713 BC is −4712. In all divisions (except for JD) the floor
92
* function is applied to the quotient (for dates since
93
* March 1, −4800 all quotients are non-negative, so we can also
96
* a = (14 - month) / 12
99
* JDN = day + ((153m + 2) / 5) + 365y + (y / 4) - (y / 100) + (y / 400) - 32045
101
* @cite http://en.wikipedia.org/wiki/Julian_day#Calculation
105
* Year month and day values are assumed to be valid. This
106
* method does no bounds checking or validation.
108
* @param Year of date
109
* @param Month of date
112
int64_t julian_day_number_from_gregorian_date(uint32_t year, uint32_t month, uint32_t day)
115
int64_t a= (14 - month) / 12;
116
int64_t y= year + 4800 - a;
117
int64_t m= month + (12 * a) - 3;
119
day_number= day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - (y / 100) + (y / 400) - 32045;
124
* Translates an absolute day number to a
125
* Julian day number. Note that a Julian day number
126
* is not the same as a date in the Julian proleptic calendar.
128
* @param The absolute day number
130
int64_t absolute_day_number_to_julian_day_number(int64_t absolute_day)
132
return absolute_day + JULIAN_DAY_NUMBER_AT_ABSOLUTE_DAY_ONE;
136
* Translates a Julian day number to an
137
* absolute day number. Note that a Julian day number
138
* is not the same as a date in the Julian proleptic calendar.
140
* @param The Julian day number
142
int64_t julian_day_number_to_absolute_day_number(int64_t julian_day)
144
return julian_day - JULIAN_DAY_NUMBER_AT_ABSOLUTE_DAY_ONE;
148
* Given a supplied Julian Day Number, populates a year, month, and day
149
* with the date in the Gregorian Proleptic calendar which corresponds to
150
* the given Julian Day Number.
152
* @cite Algorithm from http://en.wikipedia.org/wiki/Julian_day
154
* @param Julian Day Number
155
* @param Pointer to year to populate
156
* @param Pointer to month to populate
157
* @param Pointer to the day to populate
159
void gregorian_date_from_julian_day_number(int64_t julian_day
161
, uint32_t *month_out
164
int64_t j = julian_day + 32044;
165
int64_t g = j / 146097;
166
int64_t dg = j % 146097;
167
int64_t c = (dg / 36524 + 1) * 3 / 4;
168
int64_t dc = dg - c * 36524;
169
int64_t b = dc / 1461;
170
int64_t db = dc % 1461;
171
int64_t a = (db / 365 + 1) * 3 / 4;
172
int64_t da = db - a * 365;
173
int64_t y = g * 400 + c * 100 + b * 4 + a;
174
int64_t m = (da * 5 + 308) / 153 - 2;
175
int64_t d = da - (m + 4) * 153 / 5 + 122;
176
int64_t Y = y - 4800 + (m + 2) / 12;
177
int64_t M = (m + 2) % 12 + 1;
180
/* Push out parameters */
181
*year_out= (uint32_t) Y;
182
*month_out= (uint32_t) M;
183
*day_out= (uint32_t) D;
187
* Given a supplied Absolute Day Number, populates a year, month, and day
188
* with the date in the Gregorian Proleptic calendar which corresponds to
189
* the given Absolute Day Number.
191
* @param Absolute Day Number
192
* @param Pointer to year to populate
193
* @param Pointer to month to populate
194
* @param Pointer to the day to populate
196
void gregorian_date_from_absolute_day_number(int64_t absolute_day
198
, uint32_t *month_out
201
gregorian_date_from_julian_day_number(
202
absolute_day_number_to_julian_day_number(absolute_day)
209
* Functions to calculate the number of days in a
210
* particular year. The number of days in a year
211
* depends on the calendar used for the date.
213
* For the Julian proleptic calendar, a leap year
214
* is one which is evenly divisible by 4.
216
* For the Gregorian proleptic calendar, a leap year
217
* is one which is evenly divisible by 4, and if
218
* the year is evenly divisible by 100, it must also be evenly
223
* Returns the number of days in a particular year
224
* depending on the supplied calendar.
226
* @param year to evaluate
227
* @param calendar to use
229
inline uint32_t days_in_year(const uint32_t year, enum calendar calendar)
231
if (calendar == GREGORIAN)
232
return days_in_year_gregorian(year);
233
return days_in_year_julian(year);
237
* Returns the number of days in a particular Julian calendar year.
239
* @param year to evaluate
241
inline uint32_t days_in_year_julian(const uint32_t year)
243
/* Short-circuit. No odd years can be leap years... */
244
return (year & 3) == 0;
248
* Returns the number of days in a particular Gregorian year.
250
* @param year to evaluate
252
inline uint32_t days_in_year_gregorian(const uint32_t year)
254
/* Short-circuit. No odd years can be leap years... */
259
&& (year % 100 || ((year % 400 == 0) && year))
266
* Returns the number of the day in a week.
270
* Day Day Number Sunday first day?
271
* -------------- ----------- -----------------
287
* @param Julian Day Number
288
* @param Consider Sunday the first day of the week?
290
uint32_t day_of_week(int64_t day_number
291
, bool sunday_is_first_day_of_week)
293
uint32_t tmp= (uint32_t) (day_number % 7);
294
/* 0 returned from above modulo is a Monday */
295
if (sunday_is_first_day_of_week)
296
tmp= (tmp == 6 ? 0 : tmp + 1);
301
* Given a year, month, and day, returns whether the date is
302
* valid for the Gregorian proleptic calendar.
308
bool is_valid_gregorian_date(uint32_t year, uint32_t month, uint32_t day)
313
return (day <= __normal_days_in_month[month - 1]);
316
const uint32_t *p_months= __DAYS_IN_MONTH(year, (enum calendar) GREGORIAN);
317
return (day <= p_months[1]);
322
* Returns the number of days in a month, given
323
* a year and a month in the Gregorian calendar.
325
* @param Year in Gregorian Proleptic calendar
326
* @param Month in date
328
uint32_t days_in_gregorian_year_month(uint32_t year, uint32_t month)
330
const uint32_t *p_months= __DAYS_IN_MONTH(year, GREGORIAN);
331
return p_months[month - 1];
335
* Returns whether the supplied date components are within the
336
* range of the UNIX epoch.
338
* Times in the range of 1970-01-01T00:00:00 to 2038-01-19T03:14:07
347
bool in_unix_epoch_range(uint32_t year
354
if (month == 0 || day == 0)
356
if (year < UNIX_EPOCH_MAX_YEARS
357
&& year >= UNIX_EPOCH_MIN_YEARS)
359
if (year < UNIX_EPOCH_MIN_YEARS)
361
if (year == UNIX_EPOCH_MAX_YEARS)
371
/* We are on the final day of UNIX Epoch */
372
uint32_t seconds= (hour * 60 * 60)
375
if (seconds <= ((3 * 60 * 60) + (14 * 60) + 7))
384
* Returns the number of the week from a supplied year, month, and
385
* date in the Gregorian proleptic calendar.
387
* The week number returned will depend on the values of the
388
* various boolean flags passed to the function.
390
* The flags influence returned values in the following ways:
392
* sunday_is_first_day_of_week
394
* If TRUE, Sunday is first day of week
395
* If FALSE, Monday is first day of week
397
* week_range_is_ordinal
399
* If FALSE, the week is in range 0-53
401
* Week 0 is returned for the the last week of the previous year (for
402
* a date at start of january) In this case one can get 53 for the
403
* first week of next year. This flag ensures that the week is
404
* relevant for the given year.
406
* If TRUE, the week is in range 1-53.
408
* In this case one may get week 53 for a date in January (when
409
* the week is that last week of previous year) and week 1 for a
414
* If TRUE, the weeks are numbered according to ISO 8601:1988
416
* ISO 8601:1988 means that if the week containing January 1 has
417
* four or more days in the new year, then it is week 1;
418
* Otherwise it is the last week of the previous year, and the
419
* next week is week 1.
421
* If FALSE, the week that contains the first 'first-day-of-week' is week 1.
423
* @param Subject year
424
* @param Subject month
426
* @param Is sunday the first day of the week?
427
* @param Is the week range ordinal?
428
* @param Should we use ISO 8601:1988 rules?
429
* @param Pointer to a uint32_t to hold the resulting year, which
430
* may be incremented or decremented depending on flags
432
uint32_t week_number_from_gregorian_date(uint32_t year
435
, bool sunday_is_first_day_of_week
436
, bool week_range_is_ordinal
437
, bool use_iso_8601_1988
438
, uint32_t *year_out)
441
uint32_t day_number= julian_day_number_from_gregorian_date(year, month, day);
442
uint32_t first_day_of_year= julian_day_number_from_gregorian_date(year, 1, 1);
443
uint32_t tmp_years= year;
445
uint32_t week_day= day_of_week(first_day_of_year, sunday_is_first_day_of_week);
447
if (month == 1 && day <= (7 - week_day))
450
(! week_range_is_ordinal)
452
( (use_iso_8601_1988 && week_day != 0)
453
|| (!use_iso_8601_1988 && (week_day >= 4))
457
if (year_out != NULL)
458
*year_out= tmp_years;
461
week_range_is_ordinal= true;
463
tmp_days= days_in_year(tmp_years, GREGORIAN);
464
first_day_of_year-= tmp_days;
465
week_day= (week_day + (53 * 7) - tmp_days) % 7;
468
if ((!use_iso_8601_1988 && week_day != 0)
469
|| (use_iso_8601_1988 && week_day >= 4))
470
tmp_days= day_number - (first_day_of_year + (7 - week_day));
472
tmp_days= day_number - (first_day_of_year - week_day);
474
if (week_range_is_ordinal && tmp_days >= (52 * 7))
476
week_day= (week_day + days_in_year(tmp_years, GREGORIAN)) % 7;
477
if ((!use_iso_8601_1988 && week_day < 4)
478
|| (use_iso_8601_1988 && week_day == 0))
481
if (year_out != NULL)
482
*year_out= tmp_years;
486
if (year_out != NULL)
487
*year_out= tmp_years;
488
return (tmp_days / 7) + 1;