lotrointerface.com
Search Downloads

LoTROInterface SVN Reminders

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

Compare with Previous | Blame | View Log

-- Time: An object representing a particular time of day, independent of time zone
Time = class(nil);

-- Constants
Time.lengthOfHour = 60 * 60;
Time.lengthOfDay = 24 * Time.lengthOfHour;
Time.lengthOfWeek = 7 * Time.lengthOfDay;

-- Class variables -- shared by all instances; updated by Time.SetLocalTimeOffset()
Time.localTimeOffset = 0;          -- number that must be added to the server time to get the local time
Time.gameTimeOffset = 0            -- number that must be added to the game-time to get the local-time
Time.gameDayZeroLocalTime = 0;     -- Local-time at which Game Day 0 began (which was at 3 am server time on that day)
Time.serverResetLocalTime = 0;     -- local-time of a random occurrence of 3 am server time (i.e. server reset time)
Time.serverMidnightLocalTime = 0;  -- local-time of a random occurrence of 12 am server time (i.e. midnight on the server)
Time.serverWeekStartLocalTime = 0; -- local-time of a random occurrence of 12 am Sunday morning server time (i.e. the start of a week)

-- Class functions -- do not require an instance

-- Sets the local time zone.  Must be called before working with Time objects.
--    'offsetHours' is the number of hours (possible noninteger) that must be added to the server time
--       to get the time in the local timezone.
function Time.SetLocalTimeOffset(offsetHours)
    Time.localTimeOffset = offsetHours * Time.lengthOfHour;

    local currentLocalTime = Turbine.Engine.GetLocalTime();
    Time.gameTimeOffset = currentLocalTime - Turbine.Engine.GetGameTime();
    Time.serverResetLocalTime = Time():GetNextServerTime(3, 0, 0, nil, false):GetLocalTime();
    Time.serverMidnightLocalTime = Time.serverResetLocalTime - 3 * Time.lengthOfHour;
    Time.serverWeekStartLocalTime = Time():GetNextServerTime(0, 0, 0, 1, false):GetLocalTime();

    -- Calculate amount to be subtracted from local-time values such that server reset times occur at integer numbers of game days.
    local approxLocalTimeLotroCreated = 1179817200;
    local timeSinceBeginning = Time.serverResetLocalTime - approxLocalTimeLotroCreated;
    local nextGameDay = round(timeSinceBeginning / Time.lengthOfDay);
    Time.gameDayZeroLocalTime = Time.serverResetLocalTime - (nextGameDay * Time.lengthOfDay);
end

-- Converts a number of seconds to a display string such as "1d 2h 30m 45s"
-- If 'canonical' is true, then all components will be returned; if false, 
-- approximations for long delays will be returned, such as "145 days".
-- If the argument is <= 0, returns "Now" (unless 'showNegativeTimes' is true).
function Time.GetDelayStr(delay, canonical, showNegativeTimes)
    if (type(delay) ~= "number") then
        if (IsA(delay, Time)) then
            error("Time.GetDelayStr(): static function called with ':'", 2);
        else
            error("Time.GetDelayStr(): first argument must be a number", 2);
        end
    end
    
    -- Format the time display string
    local text;
    delay = math.ceil(delay);
    local s = math.floor(delay % 60);
    local m = math.floor((delay % 3600) / 60);
    local h = math.floor((delay % 86400) / 3600);
    local d = math.floor(delay / 86400);
    if (canonical) then
        text = L:GetText("/Time/TimeDisplay/dhms");
        text = string.gsub(string.gsub(string.gsub(string.gsub(text, "<d>", d), "<h>", h), "<m>", m), "<s>", s);
    else
        if (delay <= 0.5) then
            if (showNegativeTimes and (delay <= -0.5)) then
                local delayStr = Time.GetDelayStr(-delay, canonical, false);
                text = L:GetText("/Time/TimeDisplay/NegativeTime");
                text = string.gsub(text, "<delay>", delayStr);
            else
                text = L:GetText("/Time/TimeDisplay/Now");
            end
        elseif (delay < 60) then
            text = L:GetText("/Time/TimeDisplay/s");
            text = string.gsub(text, "<s>", s);
        elseif (delay < 10 * 60) then
            text = L:GetText("/Time/TimeDisplay/ms");
            text = string.gsub(string.gsub(text, "<m>", m), "<s>", s);
        elseif (delay < 60 * 60) then
            text = L:GetText("/Time/TimeDisplay/m");
            text = string.gsub(text, "<m>", m);
        elseif (delay < 24 * 60 * 60) then
            text = L:GetText("/Time/TimeDisplay/hm");
            text = string.gsub(string.gsub(text, "<h>", h), "<m>", m);
        elseif (delay < 36 * 60 * 60) then
            text = L:GetText("/Time/TimeDisplay/dhm");
            text = string.gsub(string.gsub(string.gsub(text, "<d>", d), "<h>", h), "<m>", m);
        elseif (delay < 3 * 24 * 60 * 60) then
            text = L:GetText("/Time/TimeDisplay/dh");
            text = string.gsub(string.gsub(text, "<d>", d), "<h>", h);
        else
            if (h >= 12) then
                return Time.GetDelayStr(delay + 12 * 60 * 60);
            else
                text = L:GetText("/Time/TimeDisplay/d");
                text = string.gsub(text, "<d>", d);
            end
        end
    end
    return text;
end

-- Converts a delay string such as "1d 2h 30m 45s" into a number of seconds
function Time.GetNumericDelay(str)
    local delay = 0;
    local regexp = L:GetText("/Time/TimeInput/Regexp");
    local size = L:GetText("/Time/TimeInput/Components");
    for a, b in string.gmatch(str, regexp) do
        delay = delay + a * size[b];
    end
    return delay;
end

-- Converts hours, minutes into "12:34" or "12h34"
function Time.GetTimeOfDayStr(hour, minute)
    hour = string.format("%02d", tonumber(hour));
    minute = string.format("%02d", tonumber(minute));
    local str = L:GetText("/Time/TimeOfDay");
    str = string.gsub(str, "<h>", hour);
    str = string.gsub(str, "<m>", minute);
    return str;
end

-- Extracts hours, minutes from "12:34" or "12h34"
function Time.GetNumericTimeOfDay(str)
    local regexp = L:GetText("/Time/TimeOfDayRegexp");
    hour, minute = string.match(str, regexp);
    return hour, minute;
end

-- Returns a clone of the earlier of two Times
function Time.min(a, b)
    if (a:IsBefore(b)) then
        return a:Clone();
    else
        return b:Clone();
    end
end

-- Returns a clone of the later of two Times
function Time.max(a, b)
    if (a:IsAfter(b)) then
        return a:Clone();
    else
        return b:Clone();
    end
end

-- Object functions -- called on a specific Time instance

-- Constructor: If 'constant' is true, then attempting to modify the object later will generate errors.
function Time:Constructor(localTime, constant)
    self.localTime = localTime or Turbine.Engine.GetLocalTime();
    self.constant = constant;
end

function Time:_CheckConstant()
    if (self.constant) then
        error("Attempted to modify constant Time object.  Clone it first.", 3);
    end
end

function Time:SetLocalTime(localTime)
    self:_CheckConstant();
    self.localTime = localTime;
    return self;
end

function Time:GetLocalTime()
    return self.localTime;
end

function Time:GetGameDay()
    local gameDay = (self.localTime - Time.gameDayZeroLocalTime) / Time.lengthOfDay;
    return math.floor(gameDay);
end

function Time:GetGameTime()
    return self.localTime - Time.gameTimeOffset;
end

function Time:SetGameTime(gameTime)
    self:_CheckConstant();
    self.localTime = gameTime + Time.gameTimeOffset;
    return self;
end

-- Accepts a local time of day (specified as hours, minutes, seconds) and finds the next
-- Time at which that time of day will occur locally.
-- If 'dayOfWeek' is specified, then it is an integer (1...7 => Sunday...Saturday) and the
--   result will also be restricted to that particular weekday.
-- If 'excludeSelf' is true, this function will return a Time strictly greater than self.
-- If 'excludeSelf' is false, this function will return a Time >= self.
function Time:GetNextLocalTime(hour, minute, second, dayOfWeek, excludeSelf)
    local currentLocalTime = Turbine.Engine.GetLocalTime();
    local localDateInfo = Turbine.Engine.GetDate();
    local nextLocalTime = currentLocalTime + (hour - localDateInfo.Hour) * Time.lengthOfHour;
    nextLocalTime = nextLocalTime + (minute - localDateInfo.Minute) * 60;
    nextLocalTime = nextLocalTime + (second - localDateInfo.Second);
    local windowSize = Time.lengthOfDay;
    if (dayOfWeek) then
        nextLocalTime = nextLocalTime + (dayOfWeek - localDateInfo.DayOfWeek) * Time.lengthOfDay;
        windowSize = Time.lengthOfWeek;
    end
    local nextTime = Time(nextLocalTime);
    if (excludeSelf) then
        return nextTime:RestrictToWindow(self:JustAfter(), windowSize);
    else
        return nextTime:RestrictToWindow(self, windowSize);
    end
end

-- Accepts a server time of day (specified as hours, minutes, seconds) and finds the next
-- Time at which that time of day will occur on the server.
-- If 'dayOfWeek' is specified, then it is an integer (1...7 => Sunday...Saturday) and the
--   result will also be restricted to that particular weekday.
-- If 'excludeSelf' is true, this function will return a Time strictly greater than self.
-- If 'excludeSelf' is false, this function will return a Time >= self.
function Time:GetNextServerTime(hour, minute, second, dayOfWeek, excludeSelf)
    local nextTime = self:GetNextLocalTime(hour, minute, second, dayOfWeek, false):AddSeconds(Time.localTimeOffset);
    local windowSize = Time.lengthOfDay;
    if (dayOfWeek) then
        windowSize = Time.lengthOfWeek;
    end
    if (excludeSelf) then
        return nextTime:RestrictToWindow(self:JustAfter(), windowSize);
    else
        return nextTime:RestrictToWindow(self, windowSize);
    end
end

-- Returns a new Time corresponding to the next time it will be 03:00 server time.
-- If 'excludeSelf' is true, this function will return a Time strictly greater than self.
-- If 'excludeSelf' is false, this function will return a Time >= self.
function Time:GetNextServerResetTime(excludeSelf)
    local serverResetTime = Time(Time.serverResetLocalTime);
    if (excludeSelf) then
        return serverResetTime:RestrictToWindow(self:JustAfter(), Time.lengthOfDay);
    else
        return serverResetTime:RestrictToWindow(self, Time.lengthOfDay);
    end
end

-- Returns a new Time corresponding to the last time it was 03:00 server time.
-- If 'excludeSelf' is true, this function will return a Time strictly earlier than self.
-- If 'excludeSelf' is false, this function will return a Time <= self.
function Time:GetPrevServerResetTime(excludeSelf)
    local serverResetTime = Time(Time.serverResetLocalTime);
    local oneDayAgo = self:Clone():SubtractDays(1);
    if (excludeSelf) then
        return serverResetTime:RestrictToWindow(oneDayAgo, Time.lengthOfDay);
    else
        return serverResetTime:RestrictToWindow(oneDayAgo:JustAfter(), Time.lengthOfDay);
    end
end

-- Returns a new Time corresponding to the next time it will be midnight server time.
-- If 'excludeSelf' is true, this function will return a Time strictly greater than self.
-- If 'excludeSelf' is false, this function will return a Time >= self.
function Time:GetNextServerMidnight(excludeSelf)
    local serverMidnight = Time(Time.serverMidnightLocalTime);
    if (excludeSelf) then
        return serverMidnight:RestrictToWindow(self:JustAfter(), Time.lengthOfDay);
    else
        return serverMidnight:RestrictToWindow(self, Time.lengthOfDay);
    end
end

-- Returns a new Time corresponding to the last time it was midnight server time.
-- If the Time is midnight server time, this function returns self:Clone():SubtractDays(1).
function Time:GetPrevServerMidnight()
    local serverMidnight = Time(Time.serverMidnightLocalTime);
    local oneDayAgo = self:Clone():SubtractDays(1);
    if (excludeSelf) then
        return serverMidnight:RestrictToWindow(oneDayAgo, Time.lengthOfDay);
    else
        return serverMidnight:RestrictToWindow(oneDayAgo:JustAfter(), Time.lengthOfDay);
    end
end

-- Modifies the Time object by subtracting the indicated number of seconds.  Returns self.
function Time:SubtractSeconds(sec)
    self:_CheckConstant();
    self.localTime = self.localTime - sec;
    return self;
end

-- Modifies the Time object by subtracting the indicated number of hours.  Returns self.
function Time:SubtractHours(hours)
    self:_CheckConstant();
    self.localTime = self.localTime - hours * Time.lengthOfHour;
    return self;
end

-- Modifies the Time object by subtracting the indicated number of days.  Returns self.
function Time:SubtractDays(days)
    self:_CheckConstant();
    self.localTime = self.localTime - days * Time.lengthOfDay;
    return self;
end

-- Modifies the Time object by adding the indicated number of seconds.  Returns self.
function Time:AddSeconds(sec)
    self:_CheckConstant();
    self.localTime = self.localTime + sec;
    return self;
end

-- Modifies the Time object by adding the indicated number of hours.  Returns self.
function Time:AddHours(hours)
    self:_CheckConstant();
    self.localTime = self.localTime + hours * Time.lengthOfHour;
    return self;
end

-- Modifies the Time object by adding the indicated number of days.  Returns self.
function Time:AddDays(days)
    self:_CheckConstant();
    self.localTime = self.localTime + days * Time.lengthOfDay;
    return self;
end

-- Modifies the Time object by adding or subtracting an integer multiple of the specified increment,
-- such that the resulting time is >= 'startTime' and < one increment later.
--   'incrementSize' -- the increment, in seconds
function Time:RestrictToWindow(startTime, incrementSize)
    self:_CheckConstant();
    startDifference = self:GetSecondsSince(startTime);
    if (startDifference < 0) then
        local incrementsNeeded = math.ceil(-startDifference / incrementSize);
        self:AddSeconds(incrementsNeeded * incrementSize);
    end
    local endTime = startTime:Clone():AddSeconds(incrementSize);
    endDifference = endTime:GetSecondsSince(self);
    if (endDifference <= 0) then
        local excessIncrements = 1 + math.floor(-endDifference / incrementSize);
        self:SubtractSeconds(excessIncrements * incrementSize);
    end
    return self;
end

-- Subtracts another Time and returns the result in seconds.
function Time:GetSecondsSince(otherTime)
    return (self.localTime - otherTime.localTime);
end

-- Returns a Time representing the time at which the current server week started,
-- i.e., the last time that server time was 0:00 on a Sunday morning (which could be now,
-- if the Time is 0:00 on a Sunday morning).
function Time:GetStartOfWeek()
    return Time(Time.serverWeekStartLocalTime):RestrictToWindow(self:JustAfter(), Time.lengthOfWeek):SubtractDays(7);
end

-- Returns an integer, 1-7, representing Sunday..Saturday, indicating the server's current day of the week.
function Time:GetCurrentDayInWeek()
    local startOfWeek = self:GetStartOfWeek();
    local elapsedTime = self:GetSecondsSince(startOfWeek);
    local day = 1 + math.floor(elapsedTime / Time.lengthOfDay);
    return day;
end

-- Returns a Time representing the time at which the current n-day rotation started.
-- This will correspond to a Time in the recent past (or could be the current Time).
-- 'numDays' is n
-- 'changeHour' (optional) indicates the time of day on the server at which the rotation day changes (default = 3.0)
function Time:GetStartOfRotation(numDays, changeHour)
    local changeHour = changeHour or 3.0;
    local firstRotationStart = Time(Time.gameDayZeroLocalTime):AddHours(changeHour - 3.0);
    local rotationLength = numDays * Time.lengthOfDay;
    return firstRotationStart:RestrictToWindow(self:JustAfter(), rotationLength):SubtractDays(numDays);
end

-- Returns an integer 1...n, representing the server's current day in an n-day rotation.
-- 'numDays' is n
-- 'changeHour' (optional) indicates the time of day on the server at which the rotation day changes (default = 3.0)
function Time:GetDayInRotation(numDays, changeHour)
    local startOfRotation = self:GetStartOfRotation(numDays, changeHour);
    local elapsedTime = self:GetSecondsSince(startOfRotation);
    local day = 1 + math.floor(elapsedTime / Time.lengthOfDay);
    return day;
end

-- Modifies the Time object, advancing it to the the next occurrence of 'day' in an n-day rotation.
-- 'numDays' is n
-- 'day' is an integer 1...n
-- 'changeHour' (optional) indicates the time of day on the server at which the rotation day changes (default = 3.0)
function Time:SetDayInRotation(numDays, day, changeHour)
    self:_CheckConstant();
    local currentDay = self:GetDayInRotation(numDays, changeHour);
    local daysNeeded = (numDays + day - currentDay) % numDays;
    return self:AddDays(daysNeeded);
end

-- Time object comparison function
function Time:IsBefore(otherTime)
    return (self.localTime < otherTime.localTime);
end

-- Time object comparison function
function Time:IsAfter(otherTime)
    return (self.localTime > otherTime.localTime);
end

-- Time object comparison function
function Time:Equals(otherTime)
    return (self.localTime == otherTime.localTime);
end

-- True if and only if the Time is 3 am server time.
function Time:IsServerResetTime()
    local remainder = (self.localTime - Time.serverResetLocalTime) % Time.lengthOfDay;
    return (remainder == 0);
end

function Time:Clone()
    return Time(self.localTime);
end

-- Without modifying the Time object, returns a new Time that is just after (1 second after) it.
function Time:JustAfter()
    return self:Clone():AddSeconds(1);
end

-- A special instance indicating the latest Time possible.
Time.huge = Time(math.huge, true);

Compare with Previous | Blame


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


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