(* :Title: Calendar functions *)

(* :Context: QuantLib`Calendar` *)

(* :Author: Niels Elken Sønderby *)

(* :Mathematica Version: 4.0 *)

(* :Copyright: Copyright (C) 2003 Niels Elken Sønderby *)

(* :License:

   This file is part of QuantLib for Mathematica, a Mathematica extension for
   QuantLib, a free-software/open-source financial C++ library
   - http://www.nielses.dk/quantlib/mma/
   - http://quantlib.org/

   QuantLib for Mathematica 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 ferdinando@ametrano.net The license is also available online at 
   http://quantlib.org/html/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.
*)

BeginPackage["QuantLib`Calendar`",
"QuantLib`Common`QuantLibCommon`" (* Needs SymbolToNumber functionality *)
]

(* Usage messages *)

DayOfWeek::usage = 
"DayOfWeek[{y, m, d}] gives the day of the week for year y, month m, 
and day d."

$DaysOfWeek::usage = 
"$DaysOfWeek contains a list of the days of the week."

Sunday::usage = "Sunday is a day of the week."
Monday::usage = "Monday is a day of the week."
Tuesday::usage = "Tuesday is a day of the week."
Wednesday::usage = "Wednesday is a day of the week."
Thursday::usage = "Thursday is a day of the week."
Friday::usage = "Friday is a day of the week."
Saturday::usage = "Saturday is a day of the week."

BusinessDayQ::usage = 
"BusinessDayQ[{y, m, d}, HolidayCalendar -> calendar] returns True if the 
date is a business day in the given calendar."

HolidayQ::usage = 
"HolidayQ[{y, m, d}, HolidayCalendar -> calendar] returns True if the date 
is a holiday in the given calendar."

HolidayCalendar::usage = 
"HolidayCalendar is an option for several date functions specifying the 
dates that are not business days, examples are TokyoCalendar and 
MilanCalendar."

$HolidayCalendars::usage = 
"$HolidayCalendars is a list of available holiday calendars."

FrankfurtCalendar::usage = 
"FrankfurtCalendar is a holiday calendar representing 
the Frankfurt calendar."
HelsinkiCalendar::usage = 
"HelsinkiCalendar is a holiday calendar representing 
the Helsinki calendar."
JohannesburgCalendar::usage =
"JohannesburgCalendar is a holiday calendar representing 
the Johannesburg calendar."
LondonCalendar::usage = 
"LondonCalendar is a holiday calendar representing the London calendar."
MilanCalendar::usage = 
"MilanCalendar is a holiday calendar representing the Milan calendar."
NewYorkCalendar::usage = 
"NewYorkCalendar is a holiday calendar representing the New York calendar."
SydneyCalendar::usage = 
"SydneyCalendar is a holiday calendar representing 
the Sydney (New South Wales, Australia) calendar."
TARGETCalendar::usage = 
"TARGETCalendar is a holiday calendar representing 
the Trans-European Automated Real-time Gross Express-settlement 
Transfer system calendar. http://www.ecb.int/press/00/pr001214_4.htm"
TokyoCalendar::usage = 
"TokyoCalendar is a holiday calendar representing the Tokyo calendar."
TorontoCalendar::usage = 
"TorontoCalendar is a holiday calendar representing the Toronto calendar."
WellingtonCalendar::usage = 
"WellingtonCalendar is a holiday calendar representing 
the Wellington calendar."
ZurichCalendar::usage = 
"ZurichCalendar is a holiday calendar representing the Zurich calendar."

DaysPlus::usage = 
"DaysPlus[{y, m, d}, days, HolidayCalendar -> calendar] 
advances the given date with the given number of business days."

RollDate::usage = 
"RollDate[{y, m, d}, HolidayCalendar -> calendar, RollingConvention -> roll]"

$RollingConventions::usage = 
"$RollingsConventions contains a list of available rolling conventions."
			    
RollingConvention::usage = 
"RollingConvention is an option for date functions which specifies how dates 
are adjusted if they fall on a holiday."

Preceding::usage = 
"Preceding is a rolling convention, which adjust to the first business day 
before the given holiday."
                   
ModifiedPreceding::usage = 
"ModifiedPreceding is a rolling convention, which adjusts to the first 
business day before the given holiday unless it belongs to a different month, 
in which case it adjusts to the first business day after the holiday."

Following::usage = 
"Following is a rolling convention, which adjust to the first business day 
after the given holiday."
                   
ModifiedFollowing::usage = 
"ModifiedFollowing is a rolling convention, which adjusts to the first 
business day after the given holiday unless it belongs to a different month, 
in which case it adjusts to the first business day before the holiday."

Actual365::usage = 
"Actual365 represents the Actual/365 day counting convention."

Actual360::usage = 
"Actual360 represents the Actual/360 day counting convention."

Thirty360American::usage = 
"Thirty360American represents the American (NASD) variant of the 30/360 day
counting convention: if the starting date is the 31st of a month, it becomes
equal to the 30th of the same month. If the ending date is the 31st of a
month and the starting date is earlier than the 30th of a month, the ending
date becomes equal to the 1st of the next month, otherwise the ending date
becomes equal to the 30th of the same month."

Thirty360European::usage = 
"Thirty360European represents the European variant of the 30/360 day counting
convention: starting dates or ending dates that occur on the 31st of a month
become equal to the 30th of the same month."

Thirty360Italian::usage = 
"Thirty360Italian represents the Italian variant of the 30/360 day counting
convention: starting dates or ending dates that occur on February and are
greater than 27 become equal to 30 for computational sake."

ActualActualISMA::usage = 
"ActualActualISMA represents the ISMA Actual/Actual day counting convention.
Also known as Actual/Actual Bond."

ActualActualAFB::usage = 
"ActualActualAFB represents the AFB Actual/Actual day counting convention.
Also known as Actual/Actual Euro."

ActualActualISDA::usage = 
"ActualActualISDA represents the ISDA Actual/Actual day counting convention.
Also known as Actual/Actual Historical."

DayCountConvention::usage = 
"DayCountConvention is an option for date functions that specifies the
length of months and other rules."

$DayCountConventions::usage = 
"$DayCountConventions contains a list of available day count conventions."

DaysBetween::usage = 
"DaysBetween[{y1_,m1_,d1_},{y2_,m2_,d2}, DayCountConvention-> daycount] 
gives the number of days from the first date to the second using the given 
day count convention."

YearsBetween::usage = 
"YearsBetween[{y1_,m1_,d1_},{y2_,m2_,d2}, DayCountConvention-> daycount] 
gives the fraction of years between the two dates using the given day count 
convention."

LeapYearQ::usage = 
"LeapYearQ[{y, m, d}] or LeapYearQ[year] returns true if the date is in a 
leap year."

Begin["`Private`"]

(* Give access to QuantLib`Private` context as LinkPatterns for external 
   program are there. Does a more elegant solution exist? *)
AppendTo[$ContextPath, "QuantLib`Private`"] 

(* Translation table for passing "enum"-values to C++ *)
DayOfWeekNumbers = { {Sunday, 1}, {Monday, 2}, {Tuesday, 3}, {Wednesday, 4},
                     {Thursday, 5}, {Friday, 6}, {Saturday, 7} }

$DaysOfWeek = Transpose[DayOfWeekNumbers][[1]]

DayOfWeek[{y_,m_,d_}] := NumberToSymbol[qlDayOfWeek[y,m,d],DayOfWeekNumbers]

$HolidayCalendar = TARGETCalendar; (* Default value *)

(* Translation table for passing "enum"-values to C++ *)
HolidayCalendarNumbers = { 
{ FrankfurtCalendar, 2 },
{ HelsinkiCalendar,  3 },
{ JohannesburgCalendar, 4 },
{ LondonCalendar, 5 },
{ MilanCalendar, 6 },
{ NewYorkCalendar, 7 },
{ SydneyCalendar, 10 },
{ TARGETCalendar, 11 },
{ TokyoCalendar, 12 },
{ TorontoCalendar, 13 },
{ WellingtonCalendar, 15 },
{ ZurichCalendar, 16 }
}

$HolidayCalendars = Transpose[HolidayCalendarNumbers][[1]]

BusinessDayQ[{y_, m_, d_}, opts___?OptionQ] := 
          Module[{holidaycal},
                 holidaycal = HolidayCalendar /. {opts} 
                              /. Options[BusinessDayQ];
                 If[holidaycal === None, 
                    True, (* To be consistent with ShiftDate *)
                    (* DayOfWeek[{y, m, d}] =!= Saturday =!= Sunday, *)
                    qlBusinessDayQ[y,m,d,
                        SymbolToNumber[holidaycal, HolidayCalendarNumbers]]]
                ]

BusinessDayQ[lst : {{_, _, _} ..}, opts___?OptionQ] := 
             BusinessDayQ[#, opts]& /@ lst

Options[BusinessDayQ] := { HolidayCalendar -> $HolidayCalendar }

HolidayQ[lst : {{_, _, _} ..}, opts___?OptionQ] := 
         (!BusinessDayQ[#, opts])& /@ lst

HolidayQ[args_, opts___?OptionQ] := !BusinessDayQ[args, opts]

DaysPlus[{y_, m_, d_}, days_, opts___?OptionQ] := 
          Module[{holidaycal, calnum},
                 holidaycal = HolidayCalendar /. {opts} 
                              /. Options[DaysPlus];
                 If[holidaycal === None, 
                    calnum=0,
                    calnum=SymbolToNumber[holidaycal, HolidayCalendarNumbers]];
                 qlShiftDate[y,m,d,0,0,days,calnum]
                ]

Options[DaysPlus] := { HolidayCalendar -> None }

$RollingConvention = Following; (* Default value *)

(* Translation table for passing "enum"-values to C++ *)
RollingConventionNumbers = { 
{ Preceding, 1 },
{ ModifiedPreceding, 2 },
{ Following, 3 },
{ ModifiedFollowing, 4 }
}

$RollingConventions = Transpose[RollingConventionNumbers][[1]]

RollDate[{y_, m_, d_}, opts___?OptionQ] :=
    Module[{holidaycal, rollconv, calnum, rollnum},
           holidaycal = HolidayCalendar /. {opts} 
                        /. Options[RollDate];
           rollconv = RollingConvention /. {opts} 
                        /. Options[RollDate];
           calnum = SymbolToNumber[holidaycal, HolidayCalendarNumbers];
           rollnum = SymbolToNumber[rollconv, RollingConventionNumbers];
           qlRollDate[y, m, d, calnum, rollnum]
	  ]
                        
Options[RollDate] := { HolidayCalendar -> $HolidayCalendar, 
                       RollingConvention -> $RollingConvention }

$DayCountConvention = Actual365; (* Default value *)

(* Translation table for passing "enum"-values to C++ *)
DayCountConventionNumbers = { 
{ Actual365, 1 },
{ Actual360, 2 },
{ Thirty360American, 3 }, 
{ Thirty360European, 4 }, 
{ Thirty360Italian, 5 }, 
{ ActualActualISMA, 6 },
{ ActualActualAFB, 7 }, 
{ ActualActualISDA, 8 }
}

$DayCountConventions = Transpose[DayCountConventionNumbers][[1]]

DaysBetween[{y1_,m1_,d1_},{y2_,m2_,d2_}, opts___?OptionQ] :=
    Module[{daycount, daycountnum},
           daycount = DayCountConvention /. {opts} 
                      /. Options[DaysBetween];
           daycountnum = SymbolToNumber[daycount, DayCountConventionNumbers];
           qlDaysBetween[y1,m1,d1,y2,m2,d2,daycountnum]
          ]

Options[DaysBetween] := { DayCountConvention -> $DayCountConvention }

(* Reference period only needed for ActualActualISMA *)
YearsBetween[{y1_, m1_, d1_}, {y2_, m2_, d2_}, 
             {refy1_, refm1_, refd1_}, {refy2_, refm2_, refd2_}, 
             opts___?OptionQ] :=
    Module[{daycount, daycountnum},
           daycount = DayCountConvention /. {opts} 
                      /. Options[DaysBetween];
           daycountnum = SymbolToNumber[daycount, DayCountConventionNumbers];
           qlYearsBetween[y1, m1, d1, y2, m2, d2,
           		  refy1, refm1, refd1, refy2, refm2, refd2, 
           		  daycountnum]
          ]

YearsBetween[{y1_, m1_, d1_}, {y2_, m2_, d2_}, 
             opts___?OptionQ] := YearsBetween[{y1,m1,d1}, {y2,m2,d2},
                                              {0,0,0}, {0,0,0}, opts]

Options[YearsBetween] := { DayCountConvention -> $DayCountConvention }

LeapYearQ[y_Integer]:=qlLeapYearQ[y]

Attributes[LeapYearQ] := {Listable}

End[]

EndPackage[]
