~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/calendar.cc

  • Committer: Brian Aker
  • Date: 2009-02-04 18:28:00 UTC
  • mfrom: (813.1.26 new-temporal)
  • Revision ID: brian@tangent.org-20090204182800-ra3p8rlu4zrs9fvn
Merge from Monty.

Show diffs side-by-side

added added

removed removed

Lines of Context:
382
382
 
383
383
/**
384
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.
 
385
 * date in the Gregorian proleptic calendar.  We use strftime() and
 
386
 * the %U, %W, and %V format specifiers depending on the value
 
387
 * of the sunday_is_first_day_of_week parameter.
422
388
 *
423
389
 * @param Subject year
424
390
 * @param Subject month
425
391
 * @param Subject day
426
392
 * @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
 
int64_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
 
  int64_t tmp_days;
441
 
  int64_t day_number= julian_day_number_from_gregorian_date(year, month, day);
442
 
  int64_t first_day_of_year= julian_day_number_from_gregorian_date(year, 1, 1);
443
 
  uint32_t tmp_years= year;
444
 
 
445
 
  int64_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;
 
393
 * @param Pointer to a uint32_t to hold the resulting year, which 
 
394
 *        may be incremented or decremented depending on flags
 
395
 */
 
396
uint32_t week_number_from_gregorian_date(uint32_t year
 
397
                                       , uint32_t month
 
398
                                       , uint32_t day
 
399
                                       , bool sunday_is_first_day_of_week)
 
400
{
 
401
  struct tm broken_time;
 
402
 
 
403
  broken_time.tm_year= year;
 
404
  broken_time.tm_mon= month - 1; /* struct tm has non-ordinal months */
 
405
  broken_time.tm_mday= day;
 
406
 
 
407
  /* fill out the rest of our tm fields. */
 
408
  (void) mktime(&broken_time);
 
409
 
 
410
  char result[3]; /* 3 is enough space for a max 2-digit week number */
 
411
  size_t result_len= strftime(result
 
412
                            , sizeof(result)
 
413
                            , (sunday_is_first_day_of_week ? "%U" : "%W")
 
414
                            , &broken_time);
 
415
 
 
416
  if (result_len != 0)
 
417
    return (uint32_t) atoi(result);
 
418
  return 0;
 
419
}
 
420
 
 
421
/**
 
422
 * Returns the ISO week number of a supplied year, month, and
 
423
 * date in the Gregorian proleptic calendar.  We use strftime() and
 
424
 * the %V format specifier to do the calculation, which yields a
 
425
 * correct ISO 8601:1988 week number.
 
426
 *
 
427
 * The final year_out parameter is a pointer to an integer which will
 
428
 * be set to the year in which the week belongs, according to ISO8601:1988, 
 
429
 * which may be different from the Gregorian calendar year.
 
430
 *
 
431
 * @see http://en.wikipedia.org/wiki/ISO_8601
 
432
 *
 
433
 * @param Subject year
 
434
 * @param Subject month
 
435
 * @param Subject day
 
436
 * @param Pointer to a uint32_t to hold the resulting year, which 
 
437
 *        may be incremented or decremented depending on flags
 
438
 */
 
439
uint32_t iso_week_number_from_gregorian_date(uint32_t year
 
440
                                           , uint32_t month
 
441
                                           , uint32_t day
 
442
                                           , uint32_t *year_out)
 
443
{
 
444
  struct tm broken_time;
 
445
 
 
446
  if (year_out != NULL)
 
447
    *year_out= year;
 
448
 
 
449
  broken_time.tm_year= year;
 
450
  broken_time.tm_mon= month - 1; /* struct tm has non-ordinal months */
 
451
  broken_time.tm_mday= day;
 
452
 
 
453
  /* fill out the rest of our tm fields. */
 
454
  (void) mktime(&broken_time);
 
455
 
 
456
  char result[3]; /* 3 is enough space for a max 2-digit week number */
 
457
  size_t result_len= strftime(result
 
458
                            , sizeof(result)
 
459
                            , "%V"
 
460
                            , &broken_time);
 
461
 
 
462
 
 
463
  if (result_len == 0)
 
464
    return 0; /* Not valid for ISO8601:1988 */
 
465
 
 
466
  uint32_t week_number= (uint32_t) atoi(result);
 
467
 
 
468
  /* 
 
469
   * ISO8601:1988 states that if the first week in January
 
470
   * does not contain 4 days, then the resulting week number
 
471
   * shall be 52 or 53, depending on the number of days in the
 
472
   * previous year.  In this case, we adjust the outbound
 
473
   * year parameter down a year.
 
474
   */
 
475
  if (year_out != NULL)
 
476
    if (week_number == 53 || week_number == 52)
 
477
      if (month == 1)
 
478
        *year_out--;
 
479
 
 
480
  return week_number;
489
481
}