Logo Search packages:      
Sourcecode: quantlib version File versions  Download package

date.cpp

/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2004 Ferdinando Ametrano
 Copyright (C) 2000-2005 StatPro Italia srl

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/reference/license.html>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include <ql/date.hpp>
#include <ql/Utilities/dataformatters.hpp>
#include <sstream>
#include <iomanip>
#include <ctime>
#if defined(BOOST_NO_STDC_NAMESPACE)
    namespace std { using ::time; using ::time_t; using ::tm; using ::gmtime; }
#endif

namespace QuantLib {

00032     bool operator<(const Period& p1, const Period& p2) {
        if (p1.units() == p2.units())
            return (p1.length() < p2.length());
        if (p1.units() == Days) {
            if (p2.units() == Weeks)
                return (p1.length() < p2.length() * 7);
            else if (p2.units() == Years)
                return (p1.length() < p2.length() * 365);
            else if (p2.units() == Months)
                QL_FAIL("undecidable comparison between days and months");
            else
                QL_FAIL("unknown units");
        } else if (p1.units() == Weeks) {
            if (p2.units() == Days)
                return (p1.length() * 7 < p2.length());
            else if (p2.units() == Months || p2.units() == Years)
                QL_FAIL("undecidable comparison between "
                        "weeks and months/years");
            else
                QL_FAIL("unknown units");
        } else if (p1.units() == Months) {
            if (p2.units() == Years)
                return (p1.length() < p2.length() * 12);
            else if (p2.units() == Days || p2.units() == Weeks)
                QL_FAIL("undecidable comparison between "
                            "months and days/weeks");
            else
                QL_FAIL("unknown units");
        } else if (p1.units() == Years) {
            if (p2.units() == Days)
                return (p1.length() * 365 < p2.length());
            else if (p2.units() == Months)
                return (p1.length() * 12 < p2.length());
            else if (p2.units() == Weeks)
                QL_FAIL("undecidable outcome comparing years and weeks");
            else
                QL_FAIL("unknown units");
        } else {
            QL_FAIL("unknown units");
        }
    }


    // constructors
00076     Date::Date()
    : serialNumber_(BigInteger(0)) {}

00079     Date::Date(BigInteger serialNumber)
    : serialNumber_(serialNumber) {
        QL_REQUIRE(serialNumber >= minimumSerialNumber() &&
                   serialNumber <= maximumSerialNumber(),
                   "Date " << serialNumber << " outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
    }

00087     Date::Date(Day d, Month m, Year y) {
        QL_REQUIRE(y > 1900 && y < 2100,
                   "year " << y << " out of bound. It must be in [1901,2099]");
        QL_REQUIRE(Integer(m) > 0 && Integer(m) < 13,
                   "month " << Integer(m)
                   << " outside January-December range [1,12]");

        bool leap = isLeap(y);
        Day len = monthLength(m,leap), offset = monthOffset(m,leap);
        QL_REQUIRE(d <= len && d > 0,
                   "day outside month (" << Integer(m) << ") day-range "
                   << "[1," << len << "]");

        serialNumber_ = d + offset + yearOffset(y);
    }

    Month Date::month() const {
        Day d = dayOfYear(); // dayOfYear is 1 based
        Integer m = d/30 + 1;
        bool leap = isLeap(year());
        while (d <= monthOffset(Month(m),leap))
            m--;
        while (d > monthOffset(Month(m+1),leap))
            m++;
        return Month(m);
    }

    Year Date::year() const {
        Year y = (serialNumber_ / 365)+1900;
        // yearOffset(y) is December 31st of the preceding year
        if (serialNumber_ <= yearOffset(y))
            y--;
        return y;
    }

00122     Date& Date::operator+=(BigInteger days) {
        BigInteger serial = serialNumber_ + days;
        QL_REQUIRE(serial >= minimumSerialNumber() &&
                   serial <= maximumSerialNumber(),
                   "Date " << serial << "outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
        serialNumber_ = serial;
        return *this;
    }

00132     Date& Date::operator+=(const Period& p) {
        serialNumber_ = advance(*this,p.length(),p.units()).serialNumber();
        return *this;
    }

00137     Date& Date::operator-=(BigInteger days) {
        BigInteger serial = serialNumber_ - days;
        QL_REQUIRE(serial >= minimumSerialNumber() &&
                   serial <= maximumSerialNumber(),
                   "Date " << serial << "outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
        serialNumber_ = serial;
        return *this;
    }

00147     Date& Date::operator-=(const Period& p) {
        serialNumber_ = advance(*this,-p.length(),p.units()).serialNumber();
        return *this;
    }

00152     Date& Date::operator++() {
        BigInteger serial = serialNumber_ + 1;
        QL_REQUIRE(serial >= minimumSerialNumber() &&
                   serial <= maximumSerialNumber(),
                   "Date " << serial << "outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
        serialNumber_ = serial;
        return *this;
    }

00162     Date Date::operator++(int ) {
        Date temp = *this;
        BigInteger serial = serialNumber_ + 1;
        QL_REQUIRE(serial >= minimumSerialNumber() &&
                   serial <= maximumSerialNumber(),
                   "Date " << serial << "outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
        serialNumber_ = serial;
        return temp;
    }

00173     Date& Date::operator--() {
        BigInteger serial = serialNumber_ - 1;
        QL_REQUIRE(serial >= minimumSerialNumber() &&
                   serial <= maximumSerialNumber(),
                   "Date " << serial << "outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
        serialNumber_ = serial;
        return *this;
    }

00183     Date Date::operator--(int ) {
        Date temp = *this;
        BigInteger serial = serialNumber_ - 1;
        QL_REQUIRE(serial >= minimumSerialNumber() &&
                   serial <= maximumSerialNumber(),
                   "Date " << serial << "outside allowed range ["
                   << minDate() << "-" << maxDate() << "]");
        serialNumber_ = serial;
        return temp;
    }

00194     Date Date::todaysDate() {
        std::time_t t;

        if (std::time(&t) == std::time_t(-1)) // -1 means time() didn't work
            return Date();
        std::tm *gt = std::gmtime(&t);
        return Date(Day(gt->tm_mday),
                    Month(gt->tm_mon+1),
                    Year(gt->tm_year+1900));
    }

00205     Date Date::minDate() {
        static const Date minimumDate(minimumSerialNumber());
        return minimumDate;
    }

00210     Date Date::maxDate() {
        static const Date maximumDate(maximumSerialNumber());
        return maximumDate;
    }

    Date Date::advance(const Date& date, Integer n, TimeUnit units) {
        switch (units) {
          case Days:
            return date + n;
          case Weeks:
            return date + 7*n;
          case Months: {
            Day d = date.dayOfMonth();
            Integer m = Integer(date.month())+n;
            Year y = date.year();
            while (m > 12) {
                m -= 12;
                y += 1;
            }
            while (m < 1) {
                m += 12;
                y -= 1;
            }

            QL_ENSURE(y >= 1900 && y <= 2099,
                      "year " << y << " out of bounds. "
                      << "It must be in [1901,2099]");

            Integer length = monthLength(Month(m), isLeap(y));
            if (d > length)
                d = length;

            return Date(d, Month(m), y);
          }
          case Years: {
              Day d = date.dayOfMonth();
              Month m = date.month();
              Year y = date.year()+n;

              QL_ENSURE(y >= 1900 && y <= 2099,
                        "year " << y << " out of bounds. "
                        << "It must be in [1901,2099]");

              if (d == 29 && m == February && !isLeap(y))
                  d = 28;

              return Date(d,m,y);
          }
          default:
            QL_FAIL("undefined time units");
        }
    }

00263     bool Date::isLeap(Year y) {
        static const bool YearIsLeap[] = {
            // 1900 is leap in agreement with Excel's bug
            // 1900 is out of valid date range anyway
            // 1900-1909
             true,false,false,false, true,false,false,false, true,false,
            // 1910-1919
            false,false, true,false,false,false, true,false,false,false,
            // 1920-1929
             true,false,false,false, true,false,false,false, true,false,
            // 1930-1939
            false,false, true,false,false,false, true,false,false,false,
            // 1940-1949
             true,false,false,false, true,false,false,false, true,false,
            // 1950-1959
            false,false, true,false,false,false, true,false,false,false,
            // 1960-1969
             true,false,false,false, true,false,false,false, true,false,
            // 1970-1979
            false,false, true,false,false,false, true,false,false,false,
            // 1980-1989
             true,false,false,false, true,false,false,false, true,false,
            // 1990-1999
            false,false, true,false,false,false, true,false,false,false,
            // 2000-2009
             true,false,false,false, true,false,false,false, true,false,
            // 2010-2019
            false,false, true,false,false,false, true,false,false,false,
            // 2020-2029
             true,false,false,false, true,false,false,false, true,false,
            // 2030-2039
            false,false, true,false,false,false, true,false,false,false,
            // 2040-2049
             true,false,false,false, true,false,false,false, true,false,
            // 2050-2059
            false,false, true,false,false,false, true,false,false,false,
            // 2060-2069
             true,false,false,false, true,false,false,false, true,false,
            // 2070-2079
            false,false, true,false,false,false, true,false,false,false,
            // 2080-2089
             true,false,false,false, true,false,false,false, true,false,
            // 2090-2099
            false,false, true,false,false,false, true,false,false,false,
            // 2100
            false
        };
        return YearIsLeap[y-1900];
    }

00313     Date Date::nextIMMdate(const Date& date) {
        Year y = date.year();
        Month m = date.month();

        Size skipMonths = 3-(m%3);
        if (skipMonths != 3 || date.dayOfMonth() > 21) {
            skipMonths += Size(m);
            if (skipMonths<=12) {
                m = Month(skipMonths);
            } else {
                m = Month(skipMonths-12);
                y += 1;
            }
        // date is in a IMM month and in the IMM week [15,21]
        } else if (date.dayOfMonth() > 14) {
            Date nextWednesday = nextWeekday(date, Wednesday);
            if (nextWednesday.dayOfMonth() <= 21)
                return nextWednesday;
            else {
                if (m <= 9) {
                    m = Month(Size(m)+3);
                } else {
                    m = Month(Size(m)-9);
                    y += 1;
                }
            }
        }

        return nthWeekday(3, Wednesday, m, y);
    }

00344     Date Date::nextWeekday(const Date& d, Weekday dayOfWeek) {
        Weekday wd = d.weekday();
        return d + ((wd>dayOfWeek ? 7 : 0) - wd + dayOfWeek);
    }

00349     Date Date::nthWeekday(Size nth, Weekday dayOfWeek,
                          Month m, Year y) {
        QL_REQUIRE(nth>0,
                   "zeroth day of week in a given (month, year) is undefined");
        Weekday first = Date(1, m, y).weekday();
        Size skip = nth - (dayOfWeek>=first ? 1 : 0);
        return Date(1 + dayOfWeek-first + skip*7, m, y);
    }

    Integer Date::monthLength(Month m, bool leapYear) {
        static const Integer MonthLength[] = {
            31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };
        static const Integer MonthLeapLength[] = {
            31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };
        return (leapYear? MonthLeapLength[m-1] : MonthLength[m-1]);
    }

    Integer Date::monthOffset(Month m, bool leapYear) {
        static const Integer MonthOffset[] = {
              0,  31,  59,  90, 120, 151,   // Jan - Jun
            181, 212, 243, 273, 304, 334,   // Jun - Dec
            365     // used in dayOfMonth to bracket day
        };
        static const Integer MonthLeapOffset[] = {
              0,  31,  60,  91, 121, 152,   // Jan - Jun
            182, 213, 244, 274, 305, 335,   // Jun - Dec
            366     // used in dayOfMonth to bracket day
        };
        return (leapYear? MonthLeapOffset[m-1] : MonthOffset[m-1]);
    }

    BigInteger Date::yearOffset(Year y) {
        // the list of all December 31st in the preceding year
        // e.g. for 1901 yearOffset[1] is 366, that is, December 31 1900
        static const BigInteger YearOffset[] = {
            // 1900-1909
                0,  366,  731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
            // 1910-1919
             3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
            // 1920-1929
             7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862,10227,10593,
            // 1930-1939
            10958,11323,11688,12054,12419,12784,13149,13515,13880,14245,
            // 1940-1949
            14610,14976,15341,15706,16071,16437,16802,17167,17532,17898,
            // 1950-1959
            18263,18628,18993,19359,19724,20089,20454,20820,21185,21550,
            // 1960-1969
            21915,22281,22646,23011,23376,23742,24107,24472,24837,25203,
            // 1970-1979
            25568,25933,26298,26664,27029,27394,27759,28125,28490,28855,
            // 1980-1989
            29220,29586,29951,30316,30681,31047,31412,31777,32142,32508,
            // 1990-1999
            32873,33238,33603,33969,34334,34699,35064,35430,35795,36160,
            // 2000-2009
            36525,36891,37256,37621,37986,38352,38717,39082,39447,39813,
            // 2010-2019
            40178,40543,40908,41274,41639,42004,42369,42735,43100,43465,
            // 2020-2029
            43830,44196,44561,44926,45291,45657,46022,46387,46752,47118,
            // 2030-2039
            47483,47848,48213,48579,48944,49309,49674,50040,50405,50770,
            // 2040-2049
            51135,51501,51866,52231,52596,52962,53327,53692,54057,54423,
            // 2050-2059
            54788,55153,55518,55884,56249,56614,56979,57345,57710,58075,
            // 2060-2069
            58440,58806,59171,59536,59901,60267,60632,60997,61362,61728,
            // 2070-2079
            62093,62458,62823,63189,63554,63919,64284,64650,65015,65380,
            // 2080-2089
            65745,66111,66476,66841,67206,67572,67937,68302,68667,69033,
            // 2090-2099
            69398,69763,70128,70494,70859,71224,71589,71955,72320,72685,
            // 2100
            73050
        };
        return YearOffset[y-1900];
    }

    BigInteger Date::minimumSerialNumber() {
        return 367;       // Jan 1st, 1901
    }

    BigInteger Date::maximumSerialNumber() {
        return 73050;    // Dec 31st, 2099
    }


    // weekday formatting

    std::ostream& operator<<(std::ostream& out, const Weekday& w) {
        return out << io::long_weekday(w);
    }

    namespace detail {

        std::ostream& operator<<(std::ostream& out,
                                 const long_weekday_holder& holder) {
            switch (holder.d) {
              case Sunday:
                return out << "Sunday";
              case Monday:
                return out << "Monday";
              case Tuesday:
                return out << "Tuesday";
              case Wednesday:
                return out << "Wednesday";
              case Thursday:
                return out << "Thursday";
              case Friday:
                return out << "Friday";
              case Saturday:
                return out << "Saturday";
              default:
                QL_FAIL("unknown weekday");
            }
        }

        std::ostream& operator<<(std::ostream& out,
                                 const short_weekday_holder& holder) {
            switch (holder.d) {
              case Sunday:
                return out << "Sun";
              case Monday:
                return out << "Mon";
              case Tuesday:
                return out << "Tue";
              case Wednesday:
                return out << "Wed";
              case Thursday:
                return out << "Thu";
              case Friday:
                return out << "Fri";
              case Saturday:
                return out << "Sat";
              default:
                QL_FAIL("unknown weekday");
            }
        }

        std::ostream& operator<<(std::ostream& out,
                                 const shortest_weekday_holder& holder) {
            switch (holder.d) {
              case Sunday:
                return out << "Su";
              case Monday:
                return out << "Mo";
              case Tuesday:
                return out << "Tu";
              case Wednesday:
                return out << "We";
              case Thursday:
                return out << "Th";
              case Friday:
                return out << "Fr";
              case Saturday:
                return out << "Sa";
              default:
                QL_FAIL("unknown weekday");
            }
        }

    }

    namespace io {

00519         detail::long_weekday_holder long_weekday(Weekday d) {
            return detail::long_weekday_holder(d);
        }

00523         detail::short_weekday_holder short_weekday(Weekday d) {
            return detail::short_weekday_holder(d);
        }

00527         detail::shortest_weekday_holder shortest_weekday(Weekday d) {
            return detail::shortest_weekday_holder(d);
        }

    }


    // period formatting

00536     std::ostream& operator<<(std::ostream& out, const Period& p) {
        return out << io::long_period(p);
    }

    namespace detail {

        std::ostream& operator<<(std::ostream& out,
                                 const long_period_holder& holder) {
            Integer n = holder.p.length();
            out << n << " ";
            switch (holder.p.units()) {
              case Days:
                return out << (n == 1 ? "day" : "days");
              case Weeks:
                return out << (n == 1 ? "week" : "weeks");
              case Months:
                return out << (n == 1 ? "month" : "months");
              case Years:
                return out << (n == 1 ? "year" : "years");
              default:
                QL_FAIL("unknown time unit");
            }
        }

        std::ostream& operator<<(std::ostream& out,
                                 const short_period_holder& holder) {
            Integer n = holder.p.length();
            out << n;
            switch (holder.p.units()) {
              case Days:
                return out << "d";
              case Weeks:
                return out << "w";
              case Months:
                return out << "m";
              case Years:
                return out << "y";
              default:
                QL_FAIL("unknown time unit");
            }
        }

    }

    namespace io {

00582         detail::long_period_holder long_period(const Period& p) {
            return detail::long_period_holder(p);
        }

00586         detail::short_period_holder short_period(const Period& p) {
            return detail::short_period_holder(p);
        }

    }


    // month formatting

    std::ostream& operator<<(std::ostream& out, Month m) {
        switch (m) {
          case January:
            return out << "January";
          case February:
            return out << "February";
          case March:
            return out << "March";
          case April:
            return out << "April";
          case May:
            return out << "May";
          case June:
            return out << "June";
          case July:
            return out << "July";
          case August:
            return out << "August";
          case September:
            return out << "September";
          case October:
            return out << "October";
          case November:
            return out << "November";
          case December:
            return out << "December";
          default:
            QL_FAIL("unknown month (" << Integer(m) << ")");
        }
    }


    // frequency formatting

    std::ostream& operator<<(std::ostream& out, Frequency f) {
        switch (f) {
          case NoFrequency:
            return out << "no frequency";
          case Once:
            return out << "once";
          case Annual:
            return out << "annual";
          case Semiannual:
            return out << "semiannual";
          case EveryFourthMonth:
            return out << "every-fourth-month";
          case Quarterly:
            return out << "quarterly";
          case Bimonthly:
            return out << "bimonthly";
          case Monthly:
            return out << "monthly";
          default:
            QL_FAIL("unknown frequency (" << Integer(f) << ")");
        }
    }


    // date formatting

00655     std::ostream& operator<<(std::ostream& out, const Date& d) {
        return out << io::long_date(d);
    }

    namespace detail {

        std::ostream& operator<<(std::ostream& out,
                                 const short_date_holder& holder) {
            const Date& d = holder.d;
            if (d == Date()) {
                out << "null date";
            } else {
                Integer dd = d.dayOfMonth(), mm = Integer(d.month()),
                        yyyy = d.year();
                char filler = out.fill();
                out << std::setw(2) << std::setfill('0') << mm << "/";
                out << std::setw(2) << std::setfill('0') << dd << "/";
                out << "/" << yyyy;
                out.fill(filler);
            }
            return out;
        }

        std::ostream& operator<<(std::ostream& out,
                                 const long_date_holder& holder) {
            const Date& d = holder.d;
            if (d == Date()) {
                out << "null date";
            } else {
                out << d.month() << " ";
                out << io::ordinal(d.dayOfMonth()) << ", ";
                out << d.year();
            }
            return out;
        }

        std::ostream& operator<<(std::ostream& out,
                                 const iso_date_holder& holder) {
            const Date& d = holder.d;
            if (d == Date()) {
                out << "null date";
            } else {
                Integer dd = d.dayOfMonth(), mm = Integer(d.month()),
                        yyyy = d.year();
                char filler = out.fill();
                out << yyyy << "-";
                out << std::setw(2) << std::setfill('0') << mm << "-";
                out << std::setw(2) << std::setfill('0') << dd;
                out.fill(filler);
            }
            return out;
        }
    }

    namespace io {

00711         detail::short_date_holder short_date(const Date& d) {
            return detail::short_date_holder(d);
        }

00715         detail::long_date_holder long_date(const Date& d) {
            return detail::long_date_holder(d);
        }

00719         detail::iso_date_holder iso_date(const Date& d) {
            return detail::iso_date_holder(d);
        }

    }

}

Generated by  Doxygen 1.6.0   Back to index