~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/calendar.cc

  • Committer: Brian Aker
  • Date: 2009-01-28 08:27:13 UTC
  • mfrom: (813.1.7 new-temporal)
  • Revision ID: brian@tangent.org-20090128082713-13yfi46omee0wbpx
Import work from Jay

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
3
 *
 
4
 *  Copyright (C) 2008 Sun Microsystems
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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
 
19
 */
 
20
 
 
21
/**
 
22
 * @file 
 
23
 *
 
24
 * Common functions for dealing with calendrical calculations
 
25
 */
 
26
 
 
27
#include "drizzled/global.h"
 
28
#include "drizzled/calendar.h"
 
29
 
 
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};
 
35
 
 
36
/** 
 
37
 * Private utility macro for enabling a switch between
 
38
 * Gregorian and Julian leap year date arrays.
 
39
 */
 
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)
 
42
 
 
43
/**
 
44
 * Calculates the Julian Day Number from the year, month 
 
45
 * and day at noon supplied in the Julian calendar.
 
46
 *
 
47
 * The following formula is used to calculate the Julian
 
48
 * Day Number from a date in the Julian Calendar.
 
49
 *
 
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 
 
55
 * apply truncation).
 
56
 *
 
57
 * a = (14 - month) / 12
 
58
 * y = year + 4800 - a
 
59
 * m = month + 12a - 3
 
60
 * JDN = day + ((153m + 2) / 5) + 365y + (y / 4) - 32083
 
61
 *
 
62
 * @cite http://en.wikipedia.org/wiki/Julian_day#Calculation
 
63
 *
 
64
 * @note
 
65
 *
 
66
 * Year month and day values are assumed to be valid.  This 
 
67
 * method does no bounds checking or validation.
 
68
 *
 
69
 * @param Year of date
 
70
 * @param Month of date
 
71
 * @param Day of date
 
72
 */
 
73
int64_t julian_day_number_from_julian_date(uint32_t year, uint32_t month, uint32_t day)
 
74
{
 
75
  int64_t day_number;
 
76
  int64_t a= (14 - month) / 12;
 
77
  int64_t y= year + 4800 - a;
 
78
  int64_t m= month + (12 * a) - 3;
 
79
 
 
80
  day_number= day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - 32083;
 
81
  return day_number;
 
82
}
 
83
 
 
84
/**
 
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.
 
88
 *
 
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 
 
94
 * apply truncation).
 
95
 *
 
96
 * a = (14 - month) / 12
 
97
 * y = year + 4800 - a
 
98
 * m = month + 12a - 3
 
99
 * JDN = day + ((153m + 2) / 5) + 365y + (y / 4) - (y / 100) + (y / 400) - 32045
 
100
 *
 
101
 * @cite http://en.wikipedia.org/wiki/Julian_day#Calculation
 
102
 *
 
103
 * @note
 
104
 *
 
105
 * Year month and day values are assumed to be valid.  This 
 
106
 * method does no bounds checking or validation.
 
107
 *
 
108
 * @param Year of date
 
109
 * @param Month of date
 
110
 * @param Day of date
 
111
 */
 
112
int64_t julian_day_number_from_gregorian_date(uint32_t year, uint32_t month, uint32_t day)
 
113
{
 
114
  int64_t day_number;
 
115
  int64_t a= (14 - month) / 12;
 
116
  int64_t y= year + 4800 - a;
 
117
  int64_t m= month + (12 * a) - 3;
 
118
 
 
119
  day_number= day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - (y / 100) + (y / 400) - 32045;
 
120
  return day_number;
 
121
}
 
122
 
 
123
/**
 
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.
 
127
 *
 
128
 * @param The absolute day number
 
129
 */
 
130
int64_t absolute_day_number_to_julian_day_number(int64_t absolute_day)
 
131
{
 
132
  return absolute_day + JULIAN_DAY_NUMBER_AT_ABSOLUTE_DAY_ONE;
 
133
}
 
134
 
 
135
/**
 
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.
 
139
 *
 
140
 * @param The Julian day number
 
141
 */
 
142
int64_t julian_day_number_to_absolute_day_number(int64_t julian_day)
 
143
{
 
144
  return julian_day - JULIAN_DAY_NUMBER_AT_ABSOLUTE_DAY_ONE;
 
145
}
 
146
 
 
147
/**
 
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.
 
151
 *
 
152
 * @cite Algorithm from http://en.wikipedia.org/wiki/Julian_day
 
153
 *
 
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
 
158
 */
 
159
void gregorian_date_from_julian_day_number(int64_t julian_day
 
160
                                         , uint32_t *year_out
 
161
                                         , uint32_t *month_out
 
162
                                         , uint32_t *day_out)
 
163
{
 
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;
 
178
  int64_t D = d + 1.5;
 
179
 
 
180
  /* Push out parameters */
 
181
  *year_out= (uint32_t) Y;
 
182
  *month_out= (uint32_t) M;
 
183
  *day_out= (uint32_t) D;
 
184
}
 
185
 
 
186
/**
 
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.
 
190
 *
 
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
 
195
 */
 
196
void gregorian_date_from_absolute_day_number(int64_t absolute_day
 
197
                                           , uint32_t *year_out
 
198
                                           , uint32_t *month_out
 
199
                                           , uint32_t *day_out)
 
200
{
 
201
  gregorian_date_from_julian_day_number(
 
202
      absolute_day_number_to_julian_day_number(absolute_day)
 
203
    , year_out
 
204
    , month_out
 
205
    , day_out);
 
206
}
 
207
 
 
208
/**
 
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.
 
212
 *
 
213
 * For the Julian proleptic calendar, a leap year 
 
214
 * is one which is evenly divisible by 4.
 
215
 *
 
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
 
219
 * divisible by 400.
 
220
 */
 
221
 
 
222
/**
 
223
 * Returns the number of days in a particular year
 
224
 * depending on the supplied calendar.
 
225
 *
 
226
 * @param year to evaluate
 
227
 * @param calendar to use
 
228
 */
 
229
inline uint32_t days_in_year(const uint32_t year, enum calendar calendar)
 
230
{
 
231
  if (calendar == GREGORIAN)
 
232
    return days_in_year_gregorian(year);
 
233
  return days_in_year_julian(year);
 
234
}
 
235
 
 
236
/**
 
237
 * Returns the number of days in a particular Julian calendar year.
 
238
 *
 
239
 * @param year to evaluate
 
240
 */
 
241
inline uint32_t days_in_year_julian(const uint32_t year)
 
242
{
 
243
  /* Short-circuit. No odd years can be leap years... */
 
244
  return (year & 3) == 0;
 
245
}
 
246
 
 
247
/**
 
248
 * Returns the number of days in a particular Gregorian year.
 
249
 *
 
250
 * @param year to evaluate
 
251
 */
 
252
inline uint32_t days_in_year_gregorian(const uint32_t year)
 
253
{
 
254
  /* Short-circuit. No odd years can be leap years... */
 
255
  if ((year & 1) == 1)
 
256
    return 365;
 
257
  return (            
 
258
            (year & 3) == 0 
 
259
            && (year % 100 || ((year % 400 == 0) && year)) 
 
260
            ? 366 
 
261
            : 365
 
262
         );
 
263
}
 
264
 
 
265
/**
 
266
 * Returns the number of the day in a week.
 
267
 *
 
268
 * Return values:
 
269
 *
 
270
 * Day            Day Number  Sunday first day?
 
271
 * -------------- ----------- -----------------
 
272
 * Sunday         0           true
 
273
 * Monday         1           true
 
274
 * Tuesday        2           true
 
275
 * Wednesday      3           true
 
276
 * Thursday       4           true
 
277
 * Friday         5           true
 
278
 * Saturday       6           true
 
279
 * Sunday         6           false
 
280
 * Monday         0           false
 
281
 * Tuesday        1           false
 
282
 * Wednesday      2           false
 
283
 * Thursday       3           false
 
284
 * Friday         4           false
 
285
 * Saturday       5           false
 
286
 *
 
287
 * @param Julian Day Number
 
288
 * @param Consider Sunday the first day of the week?
 
289
 */
 
290
uint32_t day_of_week(int64_t day_number
 
291
                   , bool sunday_is_first_day_of_week)
 
292
{
 
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);
 
297
  return tmp;
 
298
}
 
299
 
 
300
/**
 
301
 * Given a year, month, and day, returns whether the date is 
 
302
 * valid for the Gregorian proleptic calendar.
 
303
 *
 
304
 * @param The year
 
305
 * @param The month
 
306
 * @param The day
 
307
 */
 
308
bool is_valid_gregorian_date(uint32_t year, uint32_t month, uint32_t day)
 
309
{
 
310
  if (year < 1)
 
311
    return false;
 
312
  if (month != 2)
 
313
    return (day <= __normal_days_in_month[month - 1]);
 
314
  else
 
315
  {
 
316
    const uint32_t *p_months= __DAYS_IN_MONTH(year, (enum calendar) GREGORIAN);
 
317
    return (day <= p_months[1]);
 
318
  }
 
319
}
 
320
 
 
321
/**
 
322
 * Returns the number of days in a month, given
 
323
 * a year and a month in the Gregorian calendar.
 
324
 *
 
325
 * @param Year in Gregorian Proleptic calendar
 
326
 * @param Month in date
 
327
 */
 
328
uint32_t days_in_gregorian_year_month(uint32_t year, uint32_t month)
 
329
{
 
330
  const uint32_t *p_months= __DAYS_IN_MONTH(year, GREGORIAN);
 
331
  return p_months[month - 1];
 
332
}
 
333
 
 
334
/**
 
335
 * Returns whether the supplied date components are within the 
 
336
 * range of the UNIX epoch.
 
337
 *
 
338
 * Times in the range of 1970-01-01T00:00:00 to 2038-01-19T03:14:07
 
339
 *
 
340
 * @param Year
 
341
 * @param Month
 
342
 * @param Day
 
343
 * @param Hour
 
344
 * @param Minute
 
345
 * @param Second
 
346
 */
 
347
bool in_unix_epoch_range(uint32_t year
 
348
                       , uint32_t month
 
349
                       , uint32_t day
 
350
                       , uint32_t hour
 
351
                       , uint32_t minute
 
352
                       , uint32_t second)
 
353
{
 
354
  if (month == 0 || day == 0)
 
355
    return false;
 
356
  if (year < UNIX_EPOCH_MAX_YEARS
 
357
      && year >= UNIX_EPOCH_MIN_YEARS)
 
358
    return true;
 
359
  if (year < UNIX_EPOCH_MIN_YEARS)
 
360
    return false;
 
361
  if (year == UNIX_EPOCH_MAX_YEARS)
 
362
  {
 
363
    if (month > 1)
 
364
      return false;
 
365
    if (day > 19)
 
366
      return false;
 
367
    else if (day < 19)
 
368
      return true;
 
369
    else
 
370
    {
 
371
      /* We are on the final day of UNIX Epoch */
 
372
      uint32_t seconds= (hour * 60 * 60)
 
373
                      + (minute * 60)
 
374
                      + (second);
 
375
      if (seconds <= ((3 * 60 * 60) + (14 * 60) + 7))
 
376
        return true;
 
377
      return false;
 
378
    }
 
379
  }
 
380
  return false;
 
381
}
 
382
 
 
383
/**
 
384
 * Returns the number of the week from a supplied year, month, and
 
385
 * date in the Gregorian proleptic calendar.
 
386
 *
 
387
 * The week number returned will depend on the values of the
 
388
 * various boolean flags passed to the function.
 
389
 *
 
390
 * The flags influence returned values in the following ways:
 
391
 *
 
392
 * sunday_is_first_day_of_week
 
393
 *
 
394
 * If TRUE, Sunday is first day of week
 
395
 * If FALSE,    Monday is first day of week
 
396
 *
 
397
 * week_range_is_ordinal
 
398
 *
 
399
 * If FALSE, the week is in range 0-53
 
400
 *
 
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. 
 
405
 *
 
406
 * If TRUE, the week is in range 1-53.
 
407
 *
 
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
 
410
 * date in December.
 
411
 *
 
412
 * use_iso_8601_1988
 
413
 *
 
414
 * If TRUE, the weeks are numbered according to ISO 8601:1988
 
415
 *
 
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.
 
420
 *
 
421
 * If FALSE, the week that contains the first 'first-day-of-week' is week 1.
 
422
 *
 
423
 * @param Subject year
 
424
 * @param Subject month
 
425
 * @param Subject day
 
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
 
431
 */
 
432
uint32_t week_number_from_gregorian_date(uint32_t year
 
433
                                       , uint32_t month
 
434
                                       , uint32_t day
 
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)
 
439
{
 
440
  uint32_t tmp_days;
 
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;
 
444
 
 
445
  uint32_t week_day= day_of_week(first_day_of_year, sunday_is_first_day_of_week);
 
446
 
 
447
  if (month == 1 && day <= (7 - week_day))
 
448
  {
 
449
    if (
 
450
        (! week_range_is_ordinal) 
 
451
        && 
 
452
        ( (use_iso_8601_1988 && week_day != 0) 
 
453
          || (!use_iso_8601_1988 && (week_day >= 4))
 
454
        )
 
455
    )
 
456
    {
 
457
      if (year_out != NULL)
 
458
        *year_out= tmp_years;
 
459
      return 0;
 
460
    }
 
461
    week_range_is_ordinal= true;
 
462
    tmp_years--;
 
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;
 
466
  }
 
467
 
 
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));
 
471
  else
 
472
    tmp_days= day_number - (first_day_of_year - week_day);
 
473
 
 
474
  if (week_range_is_ordinal && tmp_days >= (52 * 7))
 
475
  {
 
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))
 
479
    {
 
480
      tmp_years++;
 
481
      if (year_out != NULL)
 
482
        *year_out= tmp_years;
 
483
      return 1;
 
484
    }
 
485
  }
 
486
  if (year_out != NULL)
 
487
    *year_out= tmp_years;
 
488
  return (tmp_days / 7) + 1;
 
489
}