// bdlt_prolepticdateimputil.h                                        -*-C++-*-
#ifndef INCLUDED_BDLT_PROLEPTICDATEIMPUTIL
#define INCLUDED_BDLT_PROLEPTICDATEIMPUTIL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide low-level support functions for date-value manipulation.
//
//@CLASSES:
//  bdlt::ProlepticDateImpUtil: low-level date-related stateless functions
//
//@SEE_ALSO: bdlt_date
//
//@DESCRIPTION: This component provides a utility `struct`,
// `bdlt::ProlepticDateImpUtil`, that defines a suite of low-level,
// date-related functions, which can be used to validate, manipulate, and
// convert among values in three different formats:
// ```
// YMD: year/month/day date
//  YD: year/day-of-year date
//   S: serial date
// ```
// The supplied functionality can also be used (e.g.) for determining leap
// years, finding the last day in a given month, and for determining the day of
// the week for a given date.  Note that in this component a "date" is
// understood to represent a valid day in the (YMD) range `0001/01/01` to
// `9999/12/31` according to the *proleptic* *Gregorian* *calendar*:
// ```
// http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
// ```
//
///Representations, Valid Dates, and Leap Years
///--------------------------------------------
// The "Calendar Date", or "year-month-day (ymd)", is the canonical
// representation and is denoted as "YYYY/MM/DD", with valid years being in the
// range `[1 .. 9999]`.  Within a valid year, valid months and valid days are
// confined to the respective ranges `[1 .. 12]` and `[1 .. 31]`.  Valid dates
// in this representation range from `0001/01/01` to `9999/12/31`, governed by
// the proleptic Gregorian calendar.  Specifically, within the 4th, 6th, 9th,
// and 11th months (respectively, April, June, September, and November) of any
// year, valid days are in the range `[1 .. 30]`.  Valid days for all other
// months of any year, with exception of the 2nd month (February), are in the
// range `[1 .. 31]`.
//
// In a *leap* *year*, February has 29 days instead of the usual 28.  Thus, the
// range of valid days for February in a leap year is `[1 .. 29]`; otherwise,
// the range of valid days for February is `[1 .. 28]`.  The proleptic
// Gregorian calendar retroactively applies the leap year rules instituted by
// the Gregorian Reformation to all years.  In particular, a year in the range
// `[1 .. 9999]` is a leap year if it is divisible by 4, but *not* divisible by
// 100, *unless* it is *also* divisible by 400.  (Expressed conversely, all
// years *not* divisible by 4 are non-leap years, as are all century years
// *not* divisible by 400 (e.g., 1900).
//
// The "Day-Of-Year Date", or "year-day (yd)" representation, denoted by
// "YYYY/DDD", represents dates by their year (again, in the range
// `[1 .. 9999]`) and the day of year, in the range `[1 .. 366]`.  Valid date
// values in this representation range from `0001/001` to `9999/365`, with a
// day-of-year value of 366 permitted for leap years only.
//
// The "Serial Date" representation depicts dates as consecutive integers
// beginning with 1 (representing `0001/01/01`).  In this representation, valid
// date values are in the range `[1 .. 3652059]`, with 3652059 representing
// `9999/12/31`.
//
///Caching
///-------
// To achieve maximal runtime performance, several of the functions in this
// component reserve the right to be implemented using statically cached (i.e.,
// tabulated, pre-calculated) values (which is inherently thread-safe).  For
// all functions where a cache may be used, `bdlt::ProlepticDateImpUtil` also
// explicitly provides a `NoCache` version (e.g., `ymdToSerialNoCache`) that is
// guaranteed NOT to use a cache.  Although the "normal" (potentially cached)
// functions typically gain huge performance advantages, the `NoCache` versions
// may conceivably be preferred by the performance-minded user who is
// *reasonably* *certain* that the vast majority of date values of interest
// will miss the cache (thus incurring a small, but unnecessary overhead for
// the cache-hit tests).  Note, however, that the `NoCache` function variants
// are provided primarily for testing and for generating the cache in the first
// place (see this component's test driver).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Use as a General Purpose Utility
///- - - - - - - - - - - - - - - - - - - - - -
// The primary purpose of this component is to support the implementation of a
// general-purpose, value-semantic (vocabulary) "Date" type.  However, it also
// provides many low-level utility functions suitable for direct use by other
// clients.  In this example we employ several of the functions from this
// component to ask questions about particular dates in one of the three
// supported formats.
//
// First, what day of the week was January 3, 2010?
// ```
// assert(2 == bdlt::ProlepticDateImpUtil::ymdToDayOfWeek(2010, 3, 1));
//                                                          // 2 means Monday.
// ```
// Then, was the year 2000 a leap year?
// ```
// assert(true == bdlt::ProlepticDateImpUtil::isLeapYear(2000));
//                                                          // Yes, it was.
// ```
// Next, was February 29, 1900 a valid date in history?
// ```
// assert(false == bdlt::ProlepticDateImpUtil::isValidYearMonthDay(1900,
//                                                                    2,
//                                                                   29));
//                                                          // No, it was not.
// ```
// Then, what was the last day of February in 1600?
// ```
// assert(29 == bdlt::ProlepticDateImpUtil::lastDayOfMonth(1600, 2));
//                                                          // The 29th.
// ```
// Next, how many leap years occurred from 1959 to 2012, inclusive?
// ```
// assert(14 == bdlt::ProlepticDateImpUtil::numLeapYears(1959, 2012));
//                                                          // There were 14.
// ```
// Now, on what day of the year will February 29, 2020 fall?
// ```
// assert(60 == bdlt::ProlepticDateImpUtil::ymdToDayOfYear(2020, 2, 29));
//                                                          // The 60th one.
// ```
// Finally, in what month did the 120th day of 2011 fall?
// ```
// assert(4 == bdlt::ProlepticDateImpUtil::ydToMonth(2011, 120));
//                                                          // 4 means April.
// ```
//
///Example 2: Implement a Value-Semantic Date Type
///- - - - - - - - - - - - - - - - - - - - - - - -
// Using the functions supplied in this component, we can easily implement a
// C++ class that represents abstract (*mathematical*) date values and performs
// common operations on them.  The internal representation could be any of the
// three supported by this component.  In this example, we choose to represent
// the date value internally as a "serial date".
//
// First, we define a partial interface of our date class, `MyDate`, omitting
// many methods, free operators, and `friend` declarations that do not
// contribute substantively to illustrating use of this component:
// ```
// /// This class represents a valid date, in the proleptic Gregorian
// /// calendar, in the range `[0001/01/01 .. 9999/12/31]`.
// class MyDate {
//
//     // DATA
//     int d_serialDate;  // 1 = 0001/01/01, 2 = 0001/01/02, etc.
//
//     // FRIENDS
//     friend bool operator==(const MyDate&, const MyDate&);
//     // ...
//
//   private:
//     // PRIVATE CREATORS
//
//     /// Create a `MyDate` object initialized with the value indicated by
//     /// the specified `serialDate`.  The behavior is undefined unless
//     /// `serialDate` represents a valid `MyDate` value.
//     explicit MyDate(int serialDate);
//
//   public:
//     // CLASS METHODS
//
//     /// Return `true` if the specified `year`, `month`, and `day`
//     /// represent a valid value for a `MyDate` object, and `false`
//     /// otherwise.
//     static bool isValid(int year, int month, int day);
//
//     // CREATORS
//
//     /// Create a `MyDate` object having the earliest supported valid
//     /// date value, i.e., "0001/01/01".
//     MyDate();
//
//     /// Create a `MyDate` object having the value represented by the
//     /// specified `year`, `month`, and `day`.  The behavior is undefined
//     /// unless `isValid(year, month, day)` returns `true`.
//     MyDate(int year, int month, int day);
//
//     // ...
//
//     // MANIPULATORS
//
//     // ...
//
//     /// Set this `MyDate` object to have the value represented by the
//     /// specified `year`, `month`, and `day`.  The behavior is undefined
//     /// unless `isValid(year, month, day)` returns `true`.
//     void setYearMonthDay(int year, int month, int day);
//
//     // ACCESSORS
//
//     /// Load, into the specified `year`, `month`, and `day`, the
//     /// individual attribute values of this `MyDate` object.
//     void getYearMonthDay(int *year, int *month, int *day) const;
//
//     /// Return the day of the month in the range `[1 .. 31]` of this
//     /// `MyDate` object.
//     int day() const;
//
//     /// Return the month of the year in the range `[1 .. 12]` of this
//     /// `MyDate` object.
//     int month() const;
//
//     /// Return the year in the range `[1 .. 9999]` of this `MyDate`
//     /// object.
//     int year() const;
//
//     // ...
// };
//
// // FREE OPERATORS
//
// /// Return `true` if the specified `lhs` and `rhs` `MyDate` objects have
// /// the same value, and `false` otherwise.  Two dates have the same
// /// value if each of the corresponding `year`, `month`, and `day`
// /// attributes respectively have the same value.
// bool operator==(const MyDate& lhs, const MyDate& rhs);
//
// // ...
// ```
// Then, we provide an implementation of the `MyDate` methods and associated
// free operators declared above, using `bsls_assert` to identify preconditions
// and invariants where appropriate.  Note the use of various
// `bdlt::ProlepticDateImpUtil` functions in the code:
// ```
// // PRIVATE CREATORS
// inline
// MyDate::MyDate(int serialDate)
// : d_serialDate(serialDate)
// {
//     BSLS_ASSERT_SAFE(bdlt::ProlepticDateImpUtil::isValidSerial(
//                                                             d_serialDate));
// }
//
// // CLASS METHODS
// inline
// bool MyDate::isValid(int year, int month, int day)
// {
//     return bdlt::ProlepticDateImpUtil::isValidYearMonthDay(year,
//                                                            month,
//                                                            day);
// }
//
// // CREATORS
// inline
// MyDate::MyDate()
// : d_serialDate(1)
// {
// }
//
// inline
// MyDate::MyDate(int year, int month, int day)
// : d_serialDate(bdlt::ProlepticDateImpUtil::ymdToSerial(year, month, day))
// {
//     BSLS_ASSERT_SAFE(isValid(year, month, day));
// }
//
// // ...
//
// // MANIPULATORS
//
// // ...
//
// inline
// void MyDate::setYearMonthDay(int year, int month, int day)
// {
//     BSLS_ASSERT_SAFE(isValid(year, month, day));
//
//     d_serialDate = bdlt::ProlepticDateImpUtil::ymdToSerial(year,
//                                                            month,
//                                                            day);
// }
//
// // ACCESSORS
// inline
// void MyDate::getYearMonthDay(int *year, int *month, int *day) const
// {
//     BSLS_ASSERT_SAFE(year);
//     BSLS_ASSERT_SAFE(month);
//     BSLS_ASSERT_SAFE(day);
//
//     bdlt::ProlepticDateImpUtil::serialToYmd(year,
//                                             month,
//                                             day,
//                                             d_serialDate);
// }
//
// inline
// int MyDate::day() const
// {
//     return bdlt::ProlepticDateImpUtil::serialToDay(d_serialDate);
// }
//
// inline
// int MyDate::month() const
// {
//     return bdlt::ProlepticDateImpUtil::serialToMonth(d_serialDate);
// }
//
// inline
// int MyDate::year() const
// {
//     return bdlt::ProlepticDateImpUtil::serialToYear(d_serialDate);
// }
//
// // FREE OPERATORS
// inline
// bool operator==(const MyDate& lhs, const MyDate& rhs)
// {
//     return lhs.d_serialDate == rhs.d_serialDate;
// }
// ```
// Next, we illustrate basic use of our `MyDate` class, starting with the
// creation of a default object, `d1`:
// ```
// MyDate d1;                        assert(   1 == d1.year());
//                                   assert(   1 == d1.month());
//                                   assert(   1 == d1.day());
// ```
// Now, we set `d1` to July 4, 1776 via the `setYearMonthDay` method, but we
// first verify that it is a valid date using `isValid`:
// ```
//                                   assert(MyDate::isValid(1776, 7, 4));
// d1.setYearMonthDay(1776, 7, 4);   assert(1776 == d1.year());
//                                   assert(   7 == d1.month());
//                                   assert(   4 == d1.day());
// ```
// Finally, using the value constructor, we create `d2` to have the same value
// as `d1`:
// ```
// MyDate d2(1776, 7, 4);            assert(1776 == d2.year());
//                                   assert(   7 == d2.month());
//                                   assert(   4 == d2.day());
//                                   assert(  d1 == d2);
// ```
// Note that equality comparison of `MyDate` objects is very efficient, being
// comprised of a comparison of two `int` values.  Similarly, the `MyDate`
// methods and free operators (not shown) that add a (signed) number of days to
// a date are also very efficient.  However, one of the trade-offs of storing a
// date internally as a serial value is that operations involving conversion
// among the serial value and one or more of the `year`, `month`, and `day`
// attributes (e.g., `setYearMonthDay`, `getYearMonthDay`) entail considerably
// more computation.

#include <bdlscm_version.h>

#include <bsls_assert.h>
#include <bsls_review.h>

namespace BloombergLP {
namespace bdlt {

                           // ===========================
                           // struct ProlepticDateImpUtil
                           // ===========================

/// This `struct` provides a namespace for a suite of pure functions that
/// perform low-level operations on date values in a variety of formats:
/// year/month/day, year/day-of-year, and serial date.  Dates in the range
/// `[0001/01/01 .. 9999/12/31]` that are valid per the proleptic Gregorian
/// calendar are supported, with serial date 1 (3652059) corresponding to
/// `0001/01/01` (`9999/12/31`).  Note that all of the functions, whether or
/// not implemented in terms of a static cache, are stateless, and, as such,
/// are inherently thread-safe.
struct ProlepticDateImpUtil {

  private:
    // PRIVATE TYPES
    struct YearMonthDay {
        short d_year;
        char  d_month;
        char  d_day;
    };

    enum {
        k_MAX_SERIAL_DATE = 3652059
    };

    // PRIVATE CLASS DATA
    static const int          s_firstCachedYear;
    static const int          s_lastCachedYear;
    static const int          s_firstCachedSerialDate;
    static const int          s_lastCachedSerialDate;
    static const int          s_cachedSerialDate[][13];
    static const YearMonthDay s_cachedYearMonthDay[];
    static const char         s_cachedDaysInMonth[][13];

  public:
    // CLASS METHODS

    /// Return `true` if the specified `year` is a leap year, and `false`
    /// otherwise.  The behavior is undefined unless `1 <= year <= 9999`.
    static bool isLeapYear(int year);

    /// Return the last day of the specified `month` in the specified
    /// `year`.  The behavior is undefined unless `1 <= year <= 9999` and
    /// `1 <= month <= 12`.  Note that the value returned will be in the
    /// range `[28 .. 31]`.
    static int lastDayOfMonth(int year, int month);

    /// Return the number of leap years occurring between the specified
    /// `year1` and `year2`, inclusive.  The behavior is undefined unless
    /// `1 <= year1 <= 9999`, `1 <= year2 <= 9999`, and `year1 <= year2`.
    static int numLeapYears(int year1, int year2);

                        // Is Valid Date

    /// Return `true` if the specified `serialDay` represents a valid date
    /// value, and `false` otherwise.  Note that valid date values are (as
    /// fully defined in the component-level documentation) in the range
    /// `[1 .. 3652059]`.
    static bool isValidSerial(int serialDay);

    /// Return `true` if the specified `year` and `dayOfYear` represents a
    /// valid date value, and `false` otherwise.  Note that valid date
    /// values are (as fully defined in the component-level documentation)
    /// in the range `[0001/01 .. 9999/366]`.
    static bool isValidYearDay(int year, int dayOfYear);

    /// Return `true` if the specified `year`, `month`, and `day` represents
    /// a valid date value, and `false` otherwise.  Note that valid date
    /// values are (as fully defined in the component-level documentation)
    /// in the range `[0001/01/01 .. 9999/12/31]`.
    static bool isValidYearMonthDay(int year, int month, int day);

    /// Return `true` if the specified `year`, `month`, and `day` represents
    /// a valid date value, and `false` otherwise.  Note that valid date
    /// values are (as fully defined in the component-level documentation)
    /// in the range `[0001/01/01 .. 9999/12/31]`.  Also note that this
    /// function is guaranteed not to use any date-cache optimizations.
    static bool isValidYearMonthDayNoCache(int year, int month, int day);

                        // To Serial Date (s)

    /// Return the serial date representation of the date value indicated by
    /// the specified `year` and `dayOfYear`.  The behavior is undefined
    /// unless `isValidYearDay(year, dayOfYear)` returns `true`.
    static int ydToSerial(int year, int dayOfYear);

    /// Return the serial date representation of the date value indicated by
    /// the specified `year`, `month`, and `day`.  The behavior is undefined
    /// unless `isValidYearMonthDay(year, month, day)` returns `true`.
    static int ymdToSerial(int year, int month, int day);

    /// Return the serial date representation of the date value indicated by
    /// the specified `year`, `month`, and `day`.  The behavior is undefined
    /// unless `isValidYearMonthDay(year, month, day)` returns `true`.  Note
    /// that this function is guaranteed not to use any date-cache
    /// optimizations.
    static int ymdToSerialNoCache(int year, int month, int day);

                        // To Day-Of-Year Date (yd)

    /// Return the day of the year of the date value indicated by the
    /// specified `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.
    static int serialToDayOfYear(int serialDay);

    /// Load, into the specified `year` and `dayOfYear`, the year-day
    /// representation of the date value indicated by the specified
    /// `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.
    static void serialToYd(int *year, int *dayOfYear, int serialDay);

    /// Return the day of the year of the date value indicated by the
    /// specified `year`, `month`, and `day`.  The behavior is undefined
    /// unless `isValidYearMonthDay(year, month, day)` returns `true`.
    static int ymdToDayOfYear(int year, int month, int day);

                        // To Calendar Date (ymd)

    /// Return the day (of the month) of the date value indicated by the
    /// specified `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.
    static int serialToDay(int serialDay);

    /// Return the day (of the month) of the date value indicated by the
    /// specified `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.  Note that this function
    /// is guaranteed not to use any date-cache optimizations.
    static int serialToDayNoCache(int serialDay);

    /// Return the month of the date value indicated by the specified
    /// `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.
    static int serialToMonth(int serialDay);

    /// Return the month of the date value indicated by the specified
    /// `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.  Note that this function
    /// is guaranteed not to use any date-cache optimizations.
    static int serialToMonthNoCache(int serialDay);

    /// Return the year of the date value indicated by the specified
    /// `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.
    static int serialToYear(int serialDay);

    /// Return the year of the date value indicated by the specified
    /// `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.  Note that this function
    /// is guaranteed not to use any date-cache optimizations.
    static int serialToYearNoCache(int serialDay);

    /// Load, into the specified `year`, `month`, and `day`, the date value
    /// indicated by the specified `serialDay`.  The behavior is undefined
    /// unless `isValidSerial(serialDay)` returns `true`.
    static void serialToYmd(int *year, int *month, int *day, int serialDay);

    /// Load, into the specified `year`, `month`, and `day`, the date value
    /// indicated by the specified `serialDay`.  The behavior is undefined
    /// unless `isValidSerial(serialDay)` returns `true`.  Note that this
    /// function is guaranteed not to use any date-cache-optimizations.
    static void serialToYmdNoCache(int *year,
                                   int *month,
                                   int *day,
                                   int  serialDay);

    /// Return the day (of the month) of the date value indicated by the
    /// specified `year` and `dayOfYear`.  The behavior is undefined unless
    /// `isValidYearDay(year, dayOfYear)` returns `true`.
    static int ydToDay(int year, int dayOfYear);

    /// Load, into the specified `month` and `day`, the (partial) calendar
    /// date representation of the date value indicated by the specified
    /// `year` and `dayOfYear`.  The behavior is undefined unless
    /// `isValidYearDay(year, dayOfYear)` returns `true`.
    static void ydToMd(int *month, int *day, int year, int dayOfYear);

    /// Return the month of the date value indicated by the specified `year`
    /// and `dayOfYear`.  The behavior is undefined unless
    /// `isValidYearDay(year, dayOfYear)` returns `true`.
    static int ydToMonth(int year, int dayOfYear);

                        // To Day of Week '[SUN = 1, MON .. SAT]'

    /// Return, as an integer (with `1 = SUN`, `2 = MON`, ..., `7 = SAT`),
    /// the day (of the week) of the date value indicated by the specified
    /// `serialDay`.  The behavior is undefined unless
    /// `isValidSerial(serialDay)` returns `true`.
    static int serialToDayOfWeek(int serialDay);

    /// Return, as an integer (with `1 = SUN`, `2 = MON`, ..., `7 = SAT`),
    /// the day (of the week) of the date value indicated by the specified
    /// `year` and `dayOfYear`.  The behavior is undefined unless
    /// `isValidYearDay(year, dayOfYear)` returns `true`.
    static int ydToDayOfWeek(int year, int dayOfYear);

    /// Return, as an integer (with `1 = SUN`, `2 = MON`, ..., `7 = SAT`),
    /// the day (of the week) of the date value indicated by the specified
    /// `year`, `month`, and `day`.  The behavior is undefined unless
    /// `isValidYearMonthDay(year, month, day)` returns `true`.
    static int ymdToDayOfWeek(int year, int month, int day);
};

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================

                           // ---------------------------
                           // struct ProlepticDateImpUtil
                           // ---------------------------

// CLASS METHODS
inline
bool ProlepticDateImpUtil::isLeapYear(int year)
{
    BSLS_REVIEW(1 <= year);
    BSLS_REVIEW(     year <= 9999);

    // Note the relative probabilities, from most likely to least likely:
    //: o Is not a leap year.
    //: o Is not the turn of any century (e.g., is not 1900).
    //: o Is a multiple of 400 (e.g., is 2000).

    return 0 == year % 4 && (0 != year % 100 || 0 == year % 400);
}

                        // Is Valid Date

inline
bool ProlepticDateImpUtil::isValidSerial(int serialDay)
{
    return static_cast<unsigned>(serialDay) - 1 < k_MAX_SERIAL_DATE;
}

inline
bool ProlepticDateImpUtil::isValidYearMonthDay(int year, int month, int day)
{
    if (s_firstCachedYear <= year && year <= s_lastCachedYear) {
        // Check 'month' and 'day'; the cache cannot catch out-of-range issues.

        if (month < 1 || month > 12 || day < 1) {
            return false;                                             // RETURN
        }

        return day <= s_cachedDaysInMonth[year - s_firstCachedYear][month];
                                                                      // RETURN
    }
    else {
        return isValidYearMonthDayNoCache(year, month, day);          // RETURN
    }
}

                        // To Day-Of-Year Date (yd)

inline
int ProlepticDateImpUtil::serialToDayOfYear(int serialDay)
{
    BSLS_REVIEW(isValidSerial(serialDay));

    int dayOfYear, year;
    serialToYd(&year, &dayOfYear, serialDay);
    return dayOfYear;
}

                        // To Calendar Date (ymd)

inline
int ProlepticDateImpUtil::serialToDayNoCache(int serialDay)
{
    BSLS_REVIEW(isValidSerial(serialDay));

    int year, month, day;
    serialToYmdNoCache(&year, &month, &day, serialDay);
    return day;
}

inline
int ProlepticDateImpUtil::serialToMonthNoCache(int serialDay)
{
    BSLS_REVIEW(isValidSerial(serialDay));

    int year, month, day;
    serialToYmdNoCache(&year, &month, &day, serialDay);
    return month;
}

inline
int ProlepticDateImpUtil::serialToYearNoCache(int serialDay)
{
    BSLS_REVIEW(isValidSerial(serialDay));

    int year, dayOfYear;
    serialToYd(&year, &dayOfYear, serialDay);
    return year;
}

inline
void ProlepticDateImpUtil::serialToYmdNoCache(int *year,
                                              int *month,
                                              int *day,
                                              int  serialDay)
{
    BSLS_REVIEW(year);
    BSLS_REVIEW(month);
    BSLS_REVIEW(day);
    BSLS_REVIEW(isValidSerial(serialDay));

    int dayOfYear;
    serialToYd(year, &dayOfYear, serialDay);
    ydToMd(month, day, *year, dayOfYear);
}

inline
int ProlepticDateImpUtil::ydToDay(int year, int dayOfYear)
{
    BSLS_ASSERT_SAFE(isValidYearDay(year, dayOfYear));

    int month, day;
    ydToMd(&month, &day, year, dayOfYear);
    return day;
}

inline
int ProlepticDateImpUtil::ydToMonth(int year, int dayOfYear)
{
    BSLS_ASSERT_SAFE(isValidYearDay(year, dayOfYear));

    int month, day;
    ydToMd(&month, &day, year, dayOfYear);
    return month;
}

                        // To Day of Week '[SUN = 1, MON .. SAT]'

inline
int ProlepticDateImpUtil::serialToDayOfWeek(int serialDay)
{
    BSLS_REVIEW(isValidSerial(serialDay));

    // 0001/01/01 was a Monday (MON == 2).

    return 1 + serialDay % 7;
}

inline
int ProlepticDateImpUtil::ydToDayOfWeek(int year, int dayOfYear)
{
    BSLS_ASSERT_SAFE(isValidYearDay(year, dayOfYear));

    return serialToDayOfWeek(ydToSerial(year, dayOfYear));
}

inline
int ProlepticDateImpUtil::ymdToDayOfWeek(int year, int month, int day)
{
    BSLS_ASSERT_SAFE(isValidYearMonthDay(year, month, day));

    return serialToDayOfWeek(ymdToSerial(year, month, day));
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
