Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox Programming Articles  |  Beyond Help Articles  |  Tips & Tricks Articles  


Paradox® Date, Time, and DateTime Data Types
Calculations Using Date Types
© 2001 Rick Kelly
www.crooit.com

Download the library containing the routines used in this series.

Preface

A library of all OPAL methods presented is available here.

In the previous section of exploring Paradox Date and Time values we discovered that these values are simply stored as numbers and an approach was presented to address Paradox errors in dealing with B.C. Era dates. Using that as our reference base about Paradox and Date Types, let's look at some common arithmetical tasks involving Date types.


Days Between Two Dates

Knowing that Date types are numbers, it is a simple subtraction to determine the number of days between two dates. Using emFixedFromGregorian ensures that B.C. Era dates are handled correctly.
method emElaspedDays(daStart Date,daEnd Date) LongInt
;
; Calculate the number of days between two dates
;
return emFixedFromGregorian(daEnd) - emFixedFromGregorian(daStart)
endMethod

Ordinal Day of Year

The OPAL method doy() returns an integer from 1 to 366 representing a specified day's position in the year and is not accurate for B.C. Era dates. We can use the elapsed days from 12/31 of the previous year as a replacement method.
method emDayOfYear(daAny Date) SmallInt
;
; Return the ordinal day of the year (1-366)
;
return smallInt(emElaspedDays(date(12,31,year(daAny) - 1),daAny))
endMethod

Days Remaining in Year

Days remaining in a year is the elapsed days between any date and December 31st of the same year.
method emDaysRemainingInYear(daAny Date) SmallInt
;
; Return the number of days remaining in year
;
return smallInt(emElaspedDays(daAny,date(12,31,year(daAny))))
endMethod

Days In a Year

Using emDayOfYear and emDaysRemainingInYear, we can calculate the total length of a given year, in days, by adding the two together.
method emDaysInYear(daDate Date) SmallInt
;
; Return the number of days (365 or 366) in the year
;
return emDayOfYear(daDate) + emDaysRemainingInYear(daDate)
endMethod

Age In Years

An age, in years, is calculated by subtracting the origin year from the as-of year and subtracting 1 if the origin month and day has not been observed in the as-of year.
method emAge(daOriginDate Date,daAsOfDate Date) LongInt
var
liYears LongInt
endVar
;
; Given an origin date and as of date
; return the age in years
;
liYears = year(daAsOfDate) - year(daOriginDate)
;
; Adjust if origin date has not been
; observed in daAsOfDate year
;
switch
case liYears = 0 :
case month(daOriginDate) < month(daAsOfDate) :
case emDayOfYear(daOriginDate) <= emDayOfYear(daAsOfDate) :
otherwise :
  liYears = liYears - 1
endSwitch
return liYears
endMethod

Holidays

Many holidays or events on various calendars are either on a fixed day (i.e. Christmas on December 25) in any given year or a designated day of the week relative to a week of a month. Frequent exceptions are found for religious observances.

Our major rule for moveable holidays/events to remember is that given any date, a particular weekday can always be found six days before, on the given date, or six days after. Using emFixedFromGregorian in our methods ensures that B.C. Era dates are handled correctly.


Daylight Savings

Many locales world-wide have implemented daylight savings. Local time is advanced one hour at 2:00 AM on the first Sunday in April and moved back one hour at 2:00 AM on the last Sunday in October. For our purposes, the end of daylight savings in October will be Sunday even though only two hours of the day have elapsed.

For clarification, the following constants are referenced:
Const
cnSunday    = 0
cnMonday    = 1
cnTuesday   = 2
cnWednesday = 3
cnThursday  = 4
cnFriday    = 5
cnSaturday  = 6
cnFirst     = 1
cnLast      = -1
endConst

method emDaylightSavingsStart(siYear SmallInt) Date
;
; Daylight Savings Starts - First Sunday in April
;
return emGregorianFromFixed(cmNthWeekDay(cnFirst,
                                         cnSunday,
                                         emFixedFromGregorian(date(4,1,siYear))))
endMethod

method emDaylightSavingsEnd(siYear SmallInt) Date
;
; Daylight Savings Ends - Last Sunday in October
;
return emGregorianFromFixed(cmNthWeekDay(cnLast,
                                         cnSunday,
                                         emFixedFromGregorian(date(10,31,siYear))))
endMethod

Religious Holidays

Let's take a look at how we can use our fixed date format to calculate Easter as observed by Catholic and Protestant churches. Orthodox Christians continue to base their calculation of Easter on the Julian calendar using different calculations.

Many different approaches to calculating Easter have been published and the web has numerous articles discussing pros and cons of various algorithms. The date of Easter was originally bound to the Julian calendar and was defined by the Council of Nicaea in 325 AD as:
The first Sunday after the first full moon occurring on or after the vernal equinox.
The vernal equinox is one of the two points where the apparent path of the sun crosses the equator. At the vernal equinox the sun appears to be moving across the equator from the southern hemisphere to the northern hemisphere. The arrival of the sun at the vernal equinox on or about March 21st marks the beginning of Spring.

Because the number of days in the year is not a whole number, the sun does not arrive at the vernal equinox at the same date every year. The date of the equinox may vary by more than two days, occurring as early as March 19th or as late as March 21st.

Our calculation of Easter is based on the assumption that the vernal equinox is always March 21st and on approximations of lunar phases called epacts.

Epacts are based on the fact that new moons occur on about the same day in a 19 year cycle of 235 lunations or synodic months called the Metonic Cycle. Computation from modern data shows that 235 lunations are 6,939 days, 16.5 hours; and 19 solar years, 6,939 days, 14.5 hours.
method emEaster(siYear SmallInt) Date1 (Pages 53-54)
;
; Calculate Easter in siYear for
; Catholic and Protestant churches
;
var
;Day after full moon on or after March 21
liPaschalMoon  LongInt
;Number of centuries in siYear
nuCentury      Number
;Age of Moon (in days) for April 5
siShiftedEpact SmallInt
endVar
nuCentury = cmFloor(number(siYear) / 100) + 1
;
; (14 + 11 * cmMod(19,siYear))
;    By Nicaean Rule (Orthodox Christian)
; cmFloor((3 * nuCentury) / 4)
;    Correction for Gregorian Centuries
; cmFloor((5 + 8 * nuCentury) / 25)
;    Correction for Metonic Cycle Inaccuracy
;
siShiftedEpact = smallInt(cmMod(30,
                                (14 + 11 * cmMod(19,siYear)) -
                                cmFloor((3 * nuCentury) / 4) +
                                cmFloor((5 + 8 * nuCentury) / 25)))
;
; Adjustment for 29.5 day lunar cycle
;
switch
case siShiftedEpact = 0 or (siShiftedEpact = 1 and 10 < cmMod(19,siYear)) :
  siShiftedEpact = siShiftedEpact + 1
endSwitch
;
; liPaschalMoon = Day after full moon on or after March 21
;
liPaschalMoon = emFixedFromGregorian(date(4,19,siYear)) - siShiftedEpact
;
; Return the Sunday following liPaschalMoon
;
return emGregorianFromFixed(cmWeekDayAfter(liPaschalMoon,cnSunday))

endMethod

Converting Dates to other Calendar Formats

There are many calendaring systems in use throughout the world today. Using our fixed date format, we can easily convert from one calendar to another. For an example let's use the International Organization for Standardization (ISO) calendar, popular in some European countries. ISO calendar dates are organized by an ordinal day of the week and a week number in any given Gregorian year. The ISO standard defines a week as:
"A seven day period within a calendar year, starting on a Monday and identified by its ordinal number with the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes 4 January." 1 (Page 43)
An ISO year contains 52 or 53 weeks with Sunday as the seventh day of week.

Two methods are needed: 1) emFixedFromISO, and 2) emISOFromFixed.
method emFixedFromISO(siISOWeek SmallInt, siISODay SmallInt, siISOYear SmallInt) LongInt
;
; Given an ISO date, convert to fixed format
;
return cmNthWeekDay(siISOWeek,
                    cnSunday,
                    emFixedFromGregorian(date(12,28,siISOYear - 1))) + siISODay
endMethod

method emISOFromFixed(liFixedDate LongInt,
                      var siISOWeek SmallInt,
                      var siISODay SmallInt,
                      var siISOYear SmallInt)
;
;Given a fixed date, calculate the ISO date
;
siISOYear = year(date(liFixedDate - 3))
switch
case liFixedDate >= emFixedFromISO(1,1,siISOYear + 1) :
  siISOYear = siISOYear + 1
endSwitch
siISOWeek = smallInt(cmFloor((liFixedDate - emFixedFromISO(1,1,siISOYear)) / 7)) + 1
siISODay = smallInt(cmAMod(7,liFixedDate))
endMethod

Conclusion

We now have methods that support some common arithmetical date calculations, basic calendar holidays and, using a common fixed date format, converting between calendaring systems.

Using our library of methods and procedures, how would you calculate Advent (Sunday nearest November 30)? Post replies (or pleas for help) on the forum discussion for this article (link below). The solution will be posted after a reasonable time (or amount of begging).


Next: Basic Astronomy and Time

References
Common / Shared ObjectPALŽ Routines


Discussion of this article


 Feedback |  Paradox Day |  Who Uses Paradox |  I Use Paradox |  Downloads 


 The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation.
 Paradox is a registered trademark of Corel Corporation.


 Modified: 15 May 2003
 Terms of Use / Legal Disclaimer


 Copyright © 2001- 2003 Paradox Community. All rights reserved. 
 Company and product names are trademarks or registered trademarks of their respective companies. 
 Authors hold the copyrights to their own works. Please contact the author of any article for details.