lotrointerface.com
Search Downloads

LoTROInterface SVN Reminders

[/] [trunk/] [Thurallor/] [Reminders/] [Schedule.lua] - Rev 8

Compare with Previous | Blame | View Log

-- Schedule: An object that contains a schedule of one or more times of week and/or times in an n-day rotation.
Schedule = class(nil);

function Schedule:Constructor(scheduleStr)
    self.dailyTimes = {};
    self.weekTimes = {};
    self.rotationTimes = {};
    if (scheduleStr) then
        self:AddStr(scheduleStr);
    end
end

-- A schedule string is a semicolon-separated list of day:time pairs, where
--   'day' can be
--         1...7 => Sunday...Saturday
--         * => every day
--         x/y => the xth day in a y-day rotation
--   'time' can be a fractional number
-- Examples:
--   "2:3;*:6;7:21.5" => Monday at 3:00 AM and any day at 6:00AM and Saturday at 9:30 PM
--   "2/8:3" => The 2nd day in an 8 day rotation (starting from a particular day in 2007) at 3:00 AM server time

-- Adds entries based on the provided schedule string.  The times and days are server times/days.
function Schedule:AddStr(str)
    for dayStr, hourStr in str:gmatch("([0-9*/]+):([0-9.]+)") do
        hour = tonumber(hourStr);
        if (dayStr == "*") then
            self:AddTimeOfDay(hour);
        elseif (dayStr:find("/")) then
            local day, days = dayStr:match("^(.+)/(.+)$");
            self:AddTimeOfRotation(tonumber(days), tonumber(day), hour)
        else
            local dayOfWeek = tonumber(dayStr);
            self:AddTimeOfWeek(dayOfWeek, hour);
        end
    end
end

-- Adds an entry to the schedule on every day of the week at the specified time.
--   'hour' is a decimal number indicating the time of day (e.g. 3.5 => 03:30 am)
function Schedule:AddTimeOfDay(hour)
    local seconds = hour * Time.lengthOfHour;
    if (self.dailyTimes[seconds]) then
        return;
    end

    -- Add entry to self.dailyTimes
    self.dailyTimes[seconds] = true;
    
    -- Eliminate redundancy in self.weekTimes
    for weekTime, _ in pairs(self.weekTimes) do
        if (weekTime % Time.lengthOfDay == seconds) then
            self.weekTimes[weekTime] = nil;
        end
    end
    
    -- Eliminate redundancies in self.rotationTimes[]
    for numDays, rotationTimes in pairs(self.rotationTimes) do
        for rotationTime, _ in pairs(rotationTimes) do
            if (rotationTime % Time.lengthOfDay == seconds) then
                rotationTimes[rotationTime] = nil;
            end
        end
    end
end

-- Adds an entry to the schedule on the specified day(s) of the week at the specified time.
--   'dayOfWeek' is an integer 1...7 indicating Sunday...Saturday
--   'hour' is a decimal number indicating the time of day (e.g. 3.5 => 03:30 am)
function Schedule:AddTimeOfWeek(dayOfWeek, hour)
    local seconds = hour * Time.lengthOfHour;
    if (self.dailyTimes[seconds]) then
        return;
    end

    -- Add entry to self.weekTimes
    local weekTime = (dayOfWeek - 1) * Time.lengthOfDay + seconds;
    self.weekTimes[weekTime] = true;

    -- Check if this hour is now daily
    for dayOfWeek = 1, 7 do
        local weekTime = (dayOfWeek - 1) * Time.lengthOfDay + seconds;
        if (not self.weekTimes[weekTime]) then
            -- There is at least one day in the week on which this hour is not present.
            return;
        end
    end

    -- This hour is now daily.  Eliminate redundancies.
    self:AddTimeOfDay(hour);
end

-- Adds an entry to the schedule on the specified day of an n-day rotation.
--   'days' is n, the number of days in the rotation
--   'day' is the day number 1...n in the rotation
--   'hour' is a decimal number indicating the time of day (e.g. 3.5 => 03:30 am)
function Schedule:AddTimeOfRotation(days, day, hour)
    local seconds = hour * Time.lengthOfHour;
    if (self.dailyTimes[seconds]) then
        return;
    end
    
    -- Add entry to self.rotationTimes
    local rotationTime = (day - 1) * Time.lengthOfDay + seconds;
    self.rotationTimes[days] = self.rotationTimes[days] or {};
    self.rotationTimes[days][rotationTime] = true;

    -- Check if this hour is now daily
    for day = 1, days do
        local rotationTime = (day - 1) * Time.lengthOfDay + seconds;
        if (not self.rotationTimes[days][rotationTime]) then
            -- There is at least one day in the rotation on which this hour is not present.
            return;
        end
    end
   
    -- This hour is now daily.  Eliminate redundancies.
    self:AddTimeOfDay(hour);
end

-- Generates schedule string (or nil).  The times and days are server times/days.
function Schedule:GetStr()
    local substrings = {};
    for hour in self:daily_entries() do
        table.insert(substrings, "*:" .. tostring(hour));
    end
    for dayOfWeek, hour in self:weekly_entries() do
        table.insert(substrings, tostring(dayOfWeek) .. ":" .. tostring(hour));
    end
    for numDays, day, hour in self:rotation_entries() do
        table.insert(substrings, tostring(day) .. "/" .. tostring(numDays) .. ":" .. tostring(hour));
    end
    if (next(substrings)) then
        return table.concat(substrings, ";");
    end
end

-- Returns game-time of the next entry in the schedule.
-- The result will be a Time >= 'prevTime' (optional, default is the current Time).
-- If 'excludePrev' is true, this function will return a Time strictly greater than prevTime.
-- If 'excludePrev' is false, this function will return a Time >= prevTime.
function Schedule:GetNextTime(prevTime, excludePrev)
    prevTime = prevTime or Time();
    if (excludePrev) then
        prevTime = prevTime:JustAfter();
    end
    local nextTime = Time.huge;

    -- Find next time among the time-of-day entries (if any)
    if (next(self.dailyTimes)) then
        local startOfDay = prevTime:GetPrevServerMidnight();
        local prevOffset = prevTime:GetSecondsSince(startOfDay);
        local soonestOffset = math.huge;
        for offset, _ in pairs(self.dailyTimes) do
            if (offset < prevOffset) then
                offset = offset + Time.lengthOfDay;
            end
            soonestOffset = math.min(soonestOffset, offset);
        end
        local nextDailyTime = startOfDay:AddSeconds(soonestOffset);
        nextTime = Time.min(nextTime, nextDailyTime);
    end

    -- Find next time among the time-of-week entries (if any)
    if (next(self.weekTimes)) then
        local startOfWeek = prevTime:GetStartOfWeek();
        local prevOffset = prevTime:GetSecondsSince(startOfWeek);
        local soonestOffset = math.huge;
        for offset, _ in pairs(self.weekTimes) do
            if (offset < prevOffset) then
                offset = offset + Time.lengthOfWeek;
            end
            soonestOffset = math.min(soonestOffset, offset);
        end
        local nextWeekTime = startOfWeek:AddSeconds(soonestOffset);
        nextTime = Time.min(nextTime, nextWeekTime);
    end

    -- Find next time among the time-of-rotation entries (if any)
    if (next(self.rotationTimes)) then
        for numDays, times in pairs(self.rotationTimes) do
            local startOfRotation = prevTime:GetStartOfRotation(numDays, 0.0);
            local prevOffset = prevTime:GetSecondsSince(startOfRotation);
            local lengthOfRotation = numDays * Time.lengthOfDay;
            local soonestOffset = math.huge;
            for offset, _ in pairs(times) do
                if (offset < prevOffset) then
                    offset = offset + lengthOfRotation;
                end
                soonestOffset = math.min(soonestOffset, offset);
            end
            local nextRotationTime = startOfRotation:AddSeconds(soonestOffset);
            nextTime = Time.min(nextTime, nextRotationTime);
        end
    end

    return nextTime;
end

-- Returns 'true' if the specified Time occurs within 24 hours after a rotation time in the schedule.
function Schedule:AvailableInRotation(currentTime)
    for numDays, day, hour in self:rotation_entries() do
        local currentDay = currentTime:GetDayInRotation(numDays, hour);
        if (day == currentDay) then
            return true;
        end
    end
end

-- For iterating over the time-of-day entries ("*:y") in a schedule.  The entries are sorted.
-- Example (where 's' is a Schedule):
--   for hour in s:daily_entries() do
--     ...
--   end
function Schedule:daily_entries()
    local entries = {};
    for offset, _ in pairs(self.dailyTimes) do
        local hour = offset / Time.lengthOfHour;
        entries[hour] = true;
    end
    return sorted_keys(entries);
end

-- For iterating over the unpacked values of 'tableVar', where 'tableVar' is a table
-- with numeric indices.
local function unpacked_values(tableVar)
    local n = 1;
    return function()
        local value = tableVar[n];
        n = n + 1;
        if (value) then
            return unpack(value);
        end
    end
end

-- For iterating over the time-of-week entries ("x:y") in a schedule.  The entries are sorted.
-- Example (where 's' is a Schedule):
--   for dayOfWeek, hour in s:weekly_entries() do
--     ...
--   end
-- 'dayOfWeek' is a number 1..7 indicating Sunday..Saturday.
function Schedule:weekly_entries()
    local entries = {};
    for offset in sorted_keys(self.weekTimes) do
        local dayOfWeek = 1 + math.floor(offset / Time.lengthOfDay);
        local seconds = offset % Time.lengthOfDay;
        local hour = seconds / Time.lengthOfHour;
        table.insert(entries, { dayOfWeek, hour });
    end
    return unpacked_values(entries);
end

-- For iterating over the rotation entries ("x/y:z") in a schedule.  The entries are sorted.
-- Example (where 's' is a Schedule):
--   for numDays, day, hour in s:rotation_entries() do
--     ...
--   end
function Schedule:rotation_entries()
    local entries = {};
        for numDays in sorted_keys(self.rotationTimes) do
                for offset in sorted_keys(self.rotationTimes[numDays]) do
            local day = 1 + math.floor(offset / Time.lengthOfDay);
            local seconds = offset % Time.lengthOfDay;
            local hour = seconds / Time.lengthOfHour;
            table.insert(entries, { numDays, day, hour });
        end
    end
    return unpacked_values(entries);
end

Compare with Previous | Blame


All times are GMT -5. The time now is 07:23 AM.


Our Network
EQInterface | EQ2Interface | Minion | WoWInterface | ESOUI | LoTROInterface | MMOUI | Swtorui