The PlantDay class serves as a very simple example of an interval server. It is similar to the built-in Weekday time interval, with these exceptions:
The plant day runs from 6:00 AM to 6:00 AM instead of midnight to midnight, so that at 0500 Tuesday, it is still considered Monday.
Monday is the first day of the week regardless of locale. (The built-in Weekday interval begins with Sunday in many locales, including US English.)
Because this is a custom interval server, to be used in one locale only, there is no localization code.
PlantDay implements the ITimeIntervals interface.
Open the code window for the PlantDay class and add these statements:
'PlantDay class Implements PITimeServer.ITimeInterval
This statement brings all the defined properties and methods of the ITimeInterval interface into this class.
'Constants Private Const cName = "PlantDay" Private Const cPluralName = "PlantDays" Private Const cShortName = "pd"
These constants are the name and abbreviated name of the interval, which the SDK will use in parsing time arithmetic expressions.
Private Const cMemberLo = 1 Private Const cMemberHi = 7
These constants are the lowest and highest ordinal numbers supported by this time interval.
Private Const cDayStart = #6:00:00 AM# Private Const cWeekStart = vbMonday
This constant defines the start of the plant day and week.
'Member variables Private mMemnames(cMemberLo To cMemberHi) As String Private mShortnames(cMemberLo To cMemberHi) As String
These variables will be initialized to hold the long and short member names.
In class initialization, you must set up the arrays that hold member names and short names.
On the left-hand drop-down of the code window, select Class. On the right-hand drop-down you will find Initialize and Terminate. Select Initialize, and insert the following statements: (VB-supplied code is shown in gray type).
Private Sub Class_Initialize() Dim i As Integer i = cMemberLo mMemnames(i) = "Monday" mShortnames(i) = "Mon" i = i + 1 mMemnames(i) = "Tuesday" mShortnames(i) = "Tue" i = i + 1 mMemnames(i) = "Wednesday" mShortnames(i) = "Wed" i = i + 1 mMemnames(i) = "Thursday" mShortnames(i) = "Thu" i = i + 1 mMemnames(i) = "Friday" mShortnames(i) = "Fri" i = i + 1 mMemnames(i) = "Saturday" mShortnames(i) = "Sat" i = i + 1 mMemnames(i) = "Sunday" mShortnames(i) = "Sun" End Sub
No class termination routine is necessary.
Insert the following subroutine:
Private Sub CheckDate(dt As Date) If dt < #1/1/1970# Or CDbl(dt) >= 2958466 Then _ Err.Raise tseDATEOUTOFRANGE, cName, _ "Date must be between 1-Jan-1970 and 31-Dec-9999" End Sub
This routine checks a Date to see if it is in the valid range for the PI SDK. If not, it raises an error. It will be used by each property or method that returns a Date, to validate the result before returning it.
As with the Intervals class, Visual Basic supplies the prototype for each of the ITimeInterval properties and methods. Choose ITimeInterval on the code window’s left-hand drop-down. Then, in turn, choose each of the properties and methods on the right-hand drop-down, and insert the following code into them (VB-supplied code in gray):
Private Property Get ITimeInterval_Name( _ Optional ByVal LocaleIndependent As Boolean = False) As String ITimeInterval_Name = cName End Property Private Property Get ITimeInterval_PluralName( _ Optional ByVal LocaleIndependent As Boolean = False) As String ITimeInterval_PluralName = cPluralName End Property Private Property Get ITimeInterval_ShortName( _ Optional ByVal LocaleIndependent As Boolean = False) As String ITimeInterval_ShortName = cShortName End Property
Private Property Get ITimeInterval_MemberHi() As Long ITimeInterval_MemberHi = cMemberHi End Property Private Property Get ITimeInterval_MemberLo() As Long ITimeInterval_MemberLo = cMemberLo End Property
Private Property Get ITimeInterval_MemberName( _ ByVal Index As Long, _ Optional ByVal LocaleIndependent As Boolean = False) As String ITimeInterval_MemberName = mMemnames(Index) End Property Private Property Get ITimeInterval_MemberShortName( _ ByVal Index As Long, _ Optional ByVal LocaleIndependent As Boolean = False) As String ITimeInterval_MemberShortName = mShortnames(Index) End Property
In general, interval arithmetic should be done in wallclock time (PITime.LocalDate) rather than UTC (PITime.UTCSeconds).
Private Function ITimeInterval_AddIntervals( _ ByVal pTime As PITimeServer.PITime, _ ByVal nIntervals As Double) As Date Dim retval As Date retval = pTime.LocalDate + nIntervals CheckDate retval ITimeInterval_AddIntervals = retval End Function
Ordinal uses the VB built-in Weekday function to find the weekday number, where the week begins on Monday. Then, if the time of day is earlier than the start of the plant day, Ordinal subtracts one (modulo 7) from the result.
Private Property Get ITimeInterval_Ordinal( _ ByVal pTime As PITimeServer.PITime, _ Optional ByVal LocaleIndependent As Boolean = False) As Long Dim dt As Date Dim wkday As Integer dt = pTime.LocalDate wkday = Weekday(dt, cWeekStart) If TimeValue(dt) < cDayStart Then If wkday = 1 Then wkday = 7 Else wkday = wkday - 1 End If End If ITimeInterval_Ordinal = wkday End Property
Private Property Get ITimeInterval_StartTime( _ ByVal pTime As PITimeServer.PITime, _ Optional ByVal LocaleIndependent As Boolean = False) As Date Dim dt As Date Dim DayStart As Date dt = pTime.LocalDate DayStart = DateValue(dt) + cDayStart If dt < DayStart Then DayStart = DayStart - 1 End if CheckDate DayStart ITimeInterval_StartTime = DayStart End Property
FindMember needs to do several things:
Private Function ITimeInterval_FindMember( _ ByVal Time As PITimeServer.PITime, _ ByVal Index As Variant, _ Optional ByVal WhatToFind As _ PITimeServer.FindIntervalConstants = 1&, _ Optional ByVal LocaleIndependent As Boolean = False) As Date Dim dt As Date 'start of the base interval Dim ord As Integer 'ordinal of the base interval Dim retval As Date 'return value Dim iix As Integer 'integer index of desired interval Dim uindex As String 'uppercase index string Dim delta As Integer 'number of days difference from base dt = ITimeInterval_StartTime(pTime) ord = ITimeInterval_Ordinal(pTime) If IsNumeric(Index) Then 'index is an ordinal number If Index < cMemberLo Or Index > cMemberHi Then Err.Raise 5, cName, "Index out of range" End If iix = Index Else 'index is a name or short name uindex = UCase(Index) 'case-insensitive compare For iix = cMemberLo To cMemberHi If uindex = UCase(mMemnames(iix)) Then Exit For If uindex = UCase(mShortnames(iix)) Then Exit For If iix = cMemberHi Then _ Err.Raise tseMEMBERNOTFOUND, cName, _ "There is no plant day named """ _ & Index & """" Next End If 'calculate distance to desired day delta = iix - ord Select Case WhatToFind 'adjust delta as required Case fiBefore If delta >= 0 Then delta = delta - 7 'previous week Case fiOnOrBefore If delta > 0 Then delta = delta - 7 'previous week Case fiAfter If delta <= 0 Then delta = delta + 7 'following week Case fiOnOrAfter If delta < 0 Then delta = delta + 7 'following week Case fiCurrent 'current week, do nothing in this case End Select 'calculate start time of target day retval = dt + delta CheckDate retval ITimeInterval_FindMember = retval End Function