diff --git a/README.md b/README.md index bb60c46..32eb9c0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,12 @@ -date -==== +LuaDate v2 -date/time library for Lua +Lua Date and Time module for Lua 5.x. + +Features: + * Date and Time string parsing. + * Time addition and subtraction. + * Time span calculation. + * Support ISO 8601 Dates. + * Local time support. + * Lua module (not binary). + * Formats Date and Time like strftime. \ No newline at end of file diff --git a/date-1.0-1.rockspec b/date-1.0-1.rockspec new file mode 100644 index 0000000..65f267f --- /dev/null +++ b/date-1.0-1.rockspec @@ -0,0 +1,27 @@ +package = "date" +version = "1.0-1" +source = { + url = "http://luaforunix.luaforge.net/packages/date-1.0-1.tar.gz", +} +description = { + summary = "Lua Date and Time module for Lua 5.1", + detailed = [[ + Date and Time string parsing; Time addition and subtraction; + Time span calculation; Supports ISO 8601 Dates. + ]], + license = "MIT/X11", + homepage = "http://luaforge.net/projects/date/", + maintainer = "Steve Donovan ", +} +dependencies = { +} +build = { + type = "none", + copy_directories = {'doc','samples'}, + install = { + lua = { + date = "date.lua" + } + } +} + diff --git a/date.lua b/date.lua new file mode 100644 index 0000000..e2c6838 --- /dev/null +++ b/date.lua @@ -0,0 +1,747 @@ +--[[LuaDate version:2.0.1]]------------------------------- +--[[------------------------------------------------------ + Copyright (C) 2006, by Jas Latrix (jastejada@yahoo.com) + All Rights Deserved. + use this code at your own risk!. + keep out of reach of children. +--]]------------------------------------------------------ +--[[ CONSTANTS ]]-- + local HOURPERDAY = 24 + local MINPERHOUR = 60 + local MINPERDAY = 1440 -- 24*60 + local SECPERMIN = 60 + local SECPERHOUR = 3600 -- 60*60 + local SECPERDAY = 86400 -- 24*60*60 + local TICKSPERSEC = 1000000 + local TICKSPERDAY = 86400000000 + local TICKSPERHOUR = 3600000000 + local TICKSPERMIN = 60000000 + local DAYNUM_MAX = 365242500 -- Sat Jan 01 1000000 00:00:00 + local DAYNUM_MIN = -365242500 -- Mon Jan 01 1000000 BCE 00:00:00 + local DAYNUM_DEF = 0 -- Mon Jan 01 0001 00:00:00 + local _; +--[[ LOCAL ARE FASTER ]]-- + local type = type + local pairs = pairs + local error = error + local assert = assert + local tonumber = tonumber + local tostring = tostring + local string = string + local math = math + local os = os + local unpack = unpack + local setmetatable = setmetatable + local getmetatable = getmetatable +--[[ EXTRA FUNCTIONS ]]-- + local fmt = string.format + local lwr = string.lower + local upr = string.upper + local rep = string.rep + local len = string.len + local sub = string.sub + local gsub = string.gsub + local gmatch = string.gmatch or string.gfind + local find = string.find + local ostime = os.time + local osdate = os.date + local floor = math.floor + local ceil = math.ceil + local abs = math.abs + -- removes the decimal part of a number + local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end + -- returns the modulo n % d; + local function mod (n,d) return n - d*floor(n/d) end + -- rounds a number; + local function round(n, d) d=d^10 return floor((n*d)+.5)/d end + -- rounds a number to whole; + local function whole(n)return floor(n+.5)end + -- is `str` in string list `tbl`, `ml` is the minimun len + local function inlist(str, tbl, ml, tn) + local sl = len(str) + if sl < (ml or 0) then return nil end + str = lwr(str) + for k, v in pairs(tbl) do + if str == lwr(sub(v, 1, sl)) then + if tn then tn[0] = k end + return k + end + end + end + local function fnil() end + local function fret(x)return x;end +--[[ DATE FUNCTIONS ]]-- + local DATE_EPOCH -- to be set later + local sl_weekdays = { + [0]="Sunday",[1]="Monday",[2]="Tuesday",[3]="Wednesday",[4]="Thursday",[5]="Friday",[6]="Saturday", + [7]="Sun",[8]="Mon",[9]="Tue",[10]="Wed",[11]="Thu",[12]="Fri",[13]="Sat", + } + local sl_meridian = {[-1]="AM", [1]="PM"} + local sl_months = { + [00]="January", [01]="February", [02]="March", + [03]="April", [04]="May", [05]="June", + [06]="July", [07]="August", [08]="September", + [09]="October", [10]="November", [11]="December", + [12]="Jan", [13]="Feb", [14]="Mar", + [15]="Apr", [16]="May", [17]="Jun", + [18]="Jul", [19]="Aug", [20]="Sep", + [21]="Oct", [22]="Nov", [23]="Dec", + } + -- added the '.2' to avoid collision, use `fix` to remove + local sl_timezone = { + [000]="utc", [0.2]="gmt", + [300]="est", [240]="edt", + [360]="cst", [300.2]="cdt", + [420]="mst", [360.2]="mdt", + [480]="pst", [420.2]="pdt", + } + -- set the day fraction resolution + local function setticks(t) + TICKSPERSEC = t; + TICKSPERDAY = SECPERDAY*TICKSPERSEC + TICKSPERHOUR= SECPERHOUR*TICKSPERSEC + TICKSPERMIN = SECPERMIN*TICKSPERSEC + end + -- is year y leap year? + local function isleapyear(y) -- y must be int! + return (mod(y, 4) == 0 and (mod(y, 100) ~= 0 or mod(y, 400) == 0)) + end + -- day since year 0 + local function dayfromyear(y) -- y must be int! + return 365*y + floor(y/4) - floor(y/100) + floor(y/400) + end + -- day number from date, month is zero base + local function makedaynum(y, m, d) + local mm = mod(mod(m,12) + 10, 12) + return dayfromyear(y + floor(m/12) - floor(mm/10)) + floor((mm*306 + 5)/10) + d - 307 + --local yy = y + floor(m/12) - floor(mm/10) + --return dayfromyear(yy) + floor((mm*306 + 5)/10) + (d - 1) + end + -- date from day number, month is zero base + local function breakdaynum(g) + local g = g + 306 + local y = floor((10000*g + 14780)/3652425) + local d = g - dayfromyear(y) + if d < 0 then y = y - 1; d = g - dayfromyear(y) end + local mi = floor((100*d + 52)/3060) + return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1) + end + --[[ for floats or int32 Lua Number data type + local function breakdaynum2(g) + local g, n = g + 306; + local n400 = floor(g/DI400Y);n = mod(g,DI400Y); + local n100 = floor(n/DI100Y);n = mod(n,DI100Y); + local n004 = floor(n/DI4Y); n = mod(n,DI4Y); + local n001 = floor(n/365); n = mod(n,365); + local y = (n400*400) + (n100*100) + (n004*4) + n001 - ((n001 == 4 or n100 == 4) and 1 or 0) + local d = g - dayfromyear(y) + local mi = floor((100*d + 52)/3060) + return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1) + end + ]] + -- day fraction from time + local function makedayfrc(h,r,s,t) + return ((h*60 + r)*60 + s)*TICKSPERSEC + t + end + -- time from day fraction + local function breakdayfrc(df) + return + mod(floor(df/TICKSPERHOUR),HOURPERDAY), + mod(floor(df/TICKSPERMIN ),MINPERHOUR), + mod(floor(df/TICKSPERSEC ),SECPERMIN), + mod(df,TICKSPERSEC) + end + -- weekday sunday = 0, monday = 1 ... + local function weekday(dn) return mod(dn + 1, 7) end + -- yearday 0 based ... + local function yearday(dn) + return dn - dayfromyear((breakdaynum(dn))-1) + end + -- parse v as a month + local function getmontharg(v) + local m = tonumber(v); + return (m and fix(m - 1)) or inlist(tostring(v) or "", sl_months, 2) + end + -- get daynum of isoweek one of year y + local function isow1(y) + local f = makedaynum(y, 0, 4) -- get the date for the 4-Jan of year `y` + local d = weekday(f) + d = d == 0 and 7 or d -- get the ISO day number, 1 == Monday, 7 == Sunday + return f + (1 - d) + end + local function isowy(dn) + local w1; + local y = (breakdaynum(dn)) + if dn >= makedaynum(y, 11, 29) then + w1 = isow1(y + 1); + if dn < w1 then + w1 = isow1(y); + else + y = y + 1; + end + else + w1 = isow1(y); + if dn < w1 then + w1 = isow1(y-1) + y = y - 1 + end + end + return floor((dn-w1)/7)+1, y + end + local function isoy(dn) + local y = (breakdaynum(dn)) + return y + (((dn >= makedaynum(y, 11, 29)) and (dn >= isow1(y + 1))) and 1 or (dn < isow1(y) and -1 or 0)) + end + local function makedaynum_isoywd(y,w,d) + return isow1(y) + 7*w + d - 8 -- simplified: isow1(y) + ((w-1)*7) + (d-1) + end +--[[ THE DATE MOUDLE ]]-- + local fmtstr = "%x %X"; +--#if not DATE_OBJECT_AFX then + date = {} + local date = date + setmetatable(date, date) +-- Version: VMMMRRRR; V-Major, M-Minor, R-Revision; e.g. 5.45.321 == 50450321 + date.version = 20000000 -- 2.0.0 +--#end -- not DATE_OBJECT_AFX +--[[ THE DATE OBJECT ]]-- + local dobj = {} + dobj.__index = dobj + dobj.__metatable = dobj + -- shout invalid arg + local function date_error_arg() return error("invalid argument(s)",0) end + -- create new date object + local function date_new(dn, df) + return setmetatable({daynum=dn, dayfrc=df}, dobj) + end + -- is `v` a date object? + local function date_isdobj(v) + return (type(v) == 'table' and getmetatable(v) == dobj) and v + end + +--#if not NO_LOCAL_TIME_SUPPORT then + -- magic year table + local date_epoch, yt; + local function getequivyear(y) + assert(not yt) + yt = {} + local de, dw, dy = date_epoch:copy() + for i = 0, 3000 do + de:setyear(de:getyear() + 1, 1, 1) + dy = de:getyear() + dw = de:getweekday() * (isleapyear(dy) and -1 or 1) + if not yt[dw] then yt[dw] = dy end --print(de) + if yt[1] and yt[2] and yt[3] and yt[4] and yt[5] and yt[6] and yt[7] and yt[-1] and yt[-2] and yt[-3] and yt[-4] and yt[-5] and yt[-6] and yt[-7] then + getequivyear = function(y) return yt[ (weekday(makedaynum(y, 0, 1)) + 1) * (isleapyear(y) and -1 or 1) ] end + return getequivyear(y) + end + end + end + -- TimeValue from daynum and dayfrc + local function dvtotv(dn, df) + return fix(dn - DATE_EPOCH) * SECPERDAY + (df/1000) + end + -- TimeValue from date and time + local function totv(y,m,d,h,r,s) + return (makedaynum(y, m, d) - DATE_EPOCH) * SECPERDAY + ((h*60 + r)*60 + s) + end + -- TimeValue from TimeTable + local function tmtotv(tm) + return tm and totv(tm.year, tm.month - 1, tm.day, tm.hour, tm.min, tm.sec) + end + -- Returns the bias in seconds of utc time daynum and dayfrc + local function getbiasutc2(self) + local y,m,d = breakdaynum(self.daynum) + local h,r,s = breakdayfrc(self.dayfrc) + local tvu = totv(y,m,d,h,r,s) -- get the utc TimeValue of date and time + local tml = osdate("*t", tvu) -- get the local TimeTable of tvu + if (not tml) or (tml.year > (y+1) or tml.year < (y-1)) then -- failed try the magic + y = getequivyear(y) + tvu = totv(y,m,d,h,r,s) + tml = osdate("*t", tvu) + end + local tvl = tmtotv(tml) + if tvu and tvl then + return tvu - tvl, tvu, tvl + else + return error("failed to get bias from utc time") + end + end + -- Returns the bias in seconds of local time daynum and dayfrc + local function getbiasloc2(daynum, dayfrc) + local tvu + -- extract date and time + local y,m,d = breakdaynum(daynum) + local h,r,s = breakdayfrc(dayfrc) + -- get equivalent TimeTable + local tml = {year=y, month=m+1, day=d, hour=h, min=r, sec=s} + -- get equivalent TimeValue + local tvl = tmtotv(tml) + + local function chkutc() + tml.isdst = nil; local tvug = ostime(tml) if tvug and (tvl == tmtotv(osdate("*t", tvug))) then tvu = tvug return end + tml.isdst = true; local tvud = ostime(tml) if tvud and (tvl == tmtotv(osdate("*t", tvud))) then tvu = tvud return end + tvu = tvud or tvug + end + chkutc() + if not tvu then + tml.year = getequivyear(y) + tvl = tmtotv(tml) + chkutc() + end + return ((tvu and tvl) and (tvu - tvl)) or error("failed to get bias from local time"), tvu, tvl + end +--#end -- not NO_LOCAL_TIME_SUPPORT + +--#if not DATE_OBJECT_AFX then + -- the date parser + local strwalker = {} -- ^Lua regular expression is not as powerful as Perl$ + strwalker.__index = strwalker + local function newstrwalker(s)return setmetatable({s=s, i=1, e=1, c=len(s)}, strwalker) end + function strwalker:aimchr() return "\n" .. self.s .. "\n" .. rep(".",self.e-1) .. "^" end + function strwalker:finish() return self.i > self.c end + function strwalker:back() self.i = self.e return self end + function strwalker:restart() self.i, self.e = 1, 1 return self end + function strwalker:match(s) return (find(self.s, s, self.i)) end + function strwalker:__call(s, f)-- print("strwalker:__call "..s..self:aimchr()) + local is, ie; is, ie, self[1], self[2], self[3], self[4], self[5] = find(self.s, s, self.i) + if is then self.e, self.i = self.i, 1+ie; if f then f(unpack(self)) end return self end + end + local function date_parse(str) + local y,m,d, h,r,s, z, w,u, j, e, k, x,v,c, chkfin, dn,df; + local sw = newstrwalker(gsub(gsub(str, "(%b())", ""),"^(%s*)","")) -- remove comment, trim leading space + --local function error_out() print(y,m,d,h,r,s) end + local function error_dup(q) --[[error_out()]] error("duplicate value: " .. (q or "") .. sw:aimchr()) end + local function error_syn(q) --[[error_out()]] error("syntax error: " .. (q or "") .. sw:aimchr()) end + local function error_inv(q) --[[error_out()]] error("invalid date: " .. (q or "") .. sw:aimchr()) end + local function sety(q) y = y and error_dup() or tonumber(q); end + local function setm(q) m = (m or w or j) and error_dup(m or w or j) or tonumber(q) end + local function setd(q) d = d and error_dup() or tonumber(q) end + local function seth(q) h = h and error_dup() or tonumber(q) end + local function setr(q) r = r and error_dup() or tonumber(q) end + local function sets(q) s = s and error_dup() or tonumber(q) end + local function adds(q) s = s + tonumber(q) end + local function setj(q) j = (m or w or j) and error_dup() or tonumber(q); end + local function setz(q) z = (z ~= 0 and z) and error_dup() or q end + local function setzn(zs,zn) zn = tonumber(zn); setz( ((zn<24) and (zn*60) or (mod(zn,100) + floor(zn/100) * 60))*( zs=='+' and -1 or 1) ) end + local function setzc(zs,zh,zm) setz( ((tonumber(zh)*60) + tonumber(zm))*( zs=='+' and -1 or 1) ) end + + if not (sw("^(%d%d%d%d)",sety) and (sw("^(%-?)(%d%d)%1(%d%d)",function(_,a,b) setm(tonumber(a)); setd(tonumber(b)) end) or sw("^(%-?)[Ww](%d%d)%1(%d?)",function(_,a,b) w, u = tonumber(a), tonumber(b or 1) end) or sw("^%-?(%d%d%d)",setj) or sw("^%-?(%d%d)",function(a) setm(a);setd(1) end)) + and ((sw("^%s*[Tt]?(%d%d):?",seth) and sw("^(%d%d):?",setr) and sw("^(%d%d)",sets) and sw("^(%.%d+)",adds)) + or sw:finish() or (sw"^%s*$" or sw"^%s*[Zz]%s*$" or sw("^%s-([%+%-])(%d%d):?(%d%d)%s*$",setzc) or sw("^%s*([%+%-])(%d%d)%s*$",setzn)) + ) ) + then --print(y,m,d,h,r,s,z,w,u,j) + sw:restart(); y,m,d,h,r,s,z,w,u,j = nil; + repeat -- print(sw:aimchr()) + if sw("^[tT:]?%s*(%d%d?):",seth) then --print("$Time") + _ = sw("^%s*(%d%d?)",setr) and sw("^%s*:%s*(%d%d?)",sets) and sw("^(%.%d+)",adds) + elseif sw("^(%d+)[/\%s,-]?%s*") then --print("$Digits") + x, c = tonumber(sw[1]), len(sw[1]) + if (x >= 70) or (m and d and (not y)) or (c > 3) then + sety( x + ((x >= 100 or c>3)and 0 or 1900) ) + else + if m then setd(x) else m = x end + end + elseif sw("^(%a+)[/\%s,-]?%s*") then --print("$Words") + x = sw[1] + if inlist(x, sl_months, 2, sw) then + if m and (not d) and (not y) then d, m = m, false end + setm(mod(sw[0],12)+1) + elseif inlist(x, sl_timezone, 2, sw) then + c = fix(sw[0]) -- ignore gmt and utc + if c ~= 0 then setz(c, x) end + elseif inlist(x, sl_weekdays, 2, sw) then + k = sw[0] + else + sw:back() + -- am pm bce ad ce bc + if sw ("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*[Ee]%s*(%2)%s*") or sw ("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*") then + e = e and error_dup() or -1 + elseif sw ("^([aA])%s*(%.?)%s*[Dd]%s*(%2)%s*") or sw ("^([cC])%s*(%.?)%s*[Ee]%s*(%2)%s*") then + e = e and error_dup() or 1 + elseif sw("^([PApa])%s*(%.?)%s*[Mm]?%s*(%2)%s*") then + x = lwr(sw[1]) -- there should be hour and it must be correct + if (not h) or (h > 12) or (h < 0) then return error_inv() end + if x == 'a' and h == 12 then h = 0 end -- am + if x == 'p' and h ~= 12 then h = h + 12 end -- pm + else error_syn() end + end + elseif not(sw("^([+-])(%d%d?):(%d%d)",setzc) or sw("^([+-])(%d+)",setzn) or sw("^[Zz]%s*$")) then -- sw{"([+-])",{"(%d%d?):(%d%d)","(%d+)"}} + error_syn("?") + end + sw("^%s*") until sw:finish() + --else print("$Iso(Date|Time|Zone)") + end + -- if date is given, it must be complete year, month & day + if (not y and not h) or ((m and not d) or (d and not m)) or ((m and w) or (m and j) or (j and w)) then return error_inv("!") end + -- fix month + if m then m = m - 1 end + -- fix year if we are on BCE + if e and e < 0 and y > 0 then y = 1 - y end + -- create date object + dn = (y and ((w and makedaynum_isoywd(y,w,u)) or (j and makedaynum(y, 0, j)) or makedaynum(y, m, d))) or DAYNUM_DEF + df = makedayfrc(h or 0, r or 0, s or 0, 0) + ((z or 0)*TICKSPERMIN) + --print("Zone",h,r,s,z,m,d,y,df) + return date_new(dn, df) -- no need to :normalize(); + end + local function date_fromtable(v) + local y, m, d = fix(v.year), getmontharg(v.month), fix(v.day) + local h, r, s, t = tonumber(v.hour), tonumber(v.min), tonumber(v.sec), tonumber(v.ticks) + -- atleast there is time or complete date + if (y or m or d) and (not(y and m and d)) then return error("incomplete table") end + return (y or h or r or s or t) and date_new(y and makedaynum(y, m, d) or DAYNUM_DEF, makedayfrc(h or 0, r or 0, s or 0, t or 0)) + end + local tmap = { + ['number'] = function(v) return date_epoch:copy():addseconds(v) end, + ['string'] = function(v) return date_parse(v) end, + ['boolean']= function(v) return date_fromtable(osdate(v and "!*t" or "*t")) end, + ['table'] = function(v) local ref = getmetatable(v) == dobj; return ref and v or date_fromtable(v), ref end + } + local function date_getdobj(v) + local o, r = (tmap[type(v)] or fnil)(v); + return (o and o:normalize() or error"invalid date time value"), r -- if r is true then o is a reference to a date obj + end +--#end -- not DATE_OBJECT_AFX + local function date_from(...) + local arg = {...} + local y, m, d = fix(arg[1]), getmontharg(arg[2]), fix(arg[3]) + local h, r, s, t = tonumber(arg[4] or 0), tonumber(arg[5] or 0), tonumber(arg[6] or 0), tonumber(arg[7] or 0) + if y and m and d and h and r and s and t then + return date_new(makedaynum(y, m, d), makedayfrc(h, r, s, t)):normalize() + else + return date_error_arg() + end + end + + --[[ THE DATE OBJECT METHODS ]]-- + function dobj:normalize() + local dn, df = fix(self.daynum), self.dayfrc + self.daynum, self.dayfrc = dn + floor(df/TICKSPERDAY), mod(df, TICKSPERDAY) + return (dn >= DAYNUM_MIN and dn <= DAYNUM_MAX) and self or error("date beyond imposed limits:"..self) + end + + function dobj:getdate() local y, m, d = breakdaynum(self.daynum) return y, m+1, d end + function dobj:gettime() return breakdayfrc(self.dayfrc) end + + function dobj:getclockhour() local h = self:gethours() return h>12 and mod(h,12) or (h==0 and 12 or h) end + + function dobj:getyearday() return yearday(self.daynum) + 1 end + function dobj:getweekday() return weekday(self.daynum) + 1 end -- in lua weekday is sunday = 1, monday = 2 ... + + function dobj:getyear() local r,_,_ = breakdaynum(self.daynum) return r end + function dobj:getmonth() local _,r,_ = breakdaynum(self.daynum) return r+1 end-- in lua month is 1 base + function dobj:getday() local _,_,r = breakdaynum(self.daynum) return r end + function dobj:gethours() return mod(floor(self.dayfrc/TICKSPERHOUR),HOURPERDAY) end + function dobj:getminutes() return mod(floor(self.dayfrc/TICKSPERMIN), MINPERHOUR) end + function dobj:getseconds() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN) end + function dobj:getfracsec() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)+(mod(self.dayfrc,TICKSPERSEC)/TICKSPERSEC) end + function dobj:getticks(u) local x = mod(self.dayfrc,TICKSPERSEC) return u and ((x*u)/TICKSPERSEC) or x end + + function dobj:getweeknumber(wdb) + local wd, yd = weekday(self.daynum), yearday(self.daynum) + if wdb then + wdb = tonumber(wdb) + if wdb then + wd = mod(wd-(wdb-1),7)-- shift the week day base + else + return date_error_arg() + end + end + return (yd < wd and 0) or (floor(yd/7) + ((mod(yd, 7)>=wd) and 1 or 0)) + end + + function dobj:getisoweekday() return mod(weekday(self.daynum)-1,7)+1 end -- sunday = 7, monday = 1 ... + function dobj:getisoweeknumber() return (isowy(self.daynum)) end + function dobj:getisoyear() return isoy(self.daynum) end + function dobj:getisodate() + local w, y = isowy(self.daynum) + return y, w, self:getisoweekday() + end + function dobj:setisoyear(y, w, d) + local cy, cw, cd = self:getisodate() + if y then cy = fix(tonumber(y))end + if w then cw = fix(tonumber(w))end + if d then cd = fix(tonumber(d))end + if cy and cw and cd then + self.daynum = makedaynum_isoywd(cy, cw, cd) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:setisoweekday(d) return self:setisoyear(nil, nil, d) end + function dobj:setisoweeknumber(w,d) return self:setisoyear(nil, w, d) end + + function dobj:setyear(y, m, d) + local cy, cm, cd = breakdaynum(self.daynum) + if y then cy = fix(tonumber(y))end + if m then cm = getmontharg(m) end + if d then cd = fix(tonumber(d))end + if cy and cm and cd then + self.daynum = makedaynum(cy, cm, cd) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:setmonth(m, d)return self:setyear(nil, m, d) end + function dobj:setday(d) return self:setyear(nil, nil, d) end + + function dobj:sethours(h, m, s, t) + local ch,cm,cs,ck = breakdayfrc(self.dayfrc) + ch, cm, cs, ck = tonumber(h or ch), tonumber(m or cm), tonumber(s or cs), tonumber(t or ck) + if ch and cm and cs and ck then + self.dayfrc = makedayfrc(ch, cm, cs, ck) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:setminutes(m,s,t) return self:sethours(nil, m, s, t) end + function dobj:setseconds(s, t) return self:sethours(nil, nil, s, t) end + function dobj:setticks(t) return self:sethours(nil, nil, nil, t) end + + function dobj:spanticks() return (self.daynum*TICKSPERDAY + self.dayfrc) end + function dobj:spanseconds() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERSEC end + function dobj:spanminutes() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERMIN end + function dobj:spanhours() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERHOUR end + function dobj:spandays() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERDAY end + + function dobj:addyears(y, m, d) + local cy, cm, cd = breakdaynum(self.daynum) + if y then y = fix(tonumber(y))else y = 0 end + if m then m = fix(tonumber(m))else m = 0 end + if d then d = fix(tonumber(d))else d = 0 end + if y and m and d then + self.daynum = makedaynum(cy+y, cm+m, cd+d) + return self:normalize() + else + return date_error_arg() + end + end + + function dobj:addmonths(m, d) + return self:addyears(nil, m, d) + end + + local function dobj_adddayfrc(self,n,pt,pd) + n = tonumber(n) + if n then + local x = floor(n/pd); + self.daynum = self.daynum + x; + self.dayfrc = self.dayfrc + (n-x*pd)*pt; + return self:normalize() + else + return date_error_arg() + end + end + function dobj:adddays(n) return dobj_adddayfrc(self,n,TICKSPERDAY,1) end + function dobj:addhours(n) return dobj_adddayfrc(self,n,TICKSPERHOUR,HOURPERDAY) end + function dobj:addminutes(n) return dobj_adddayfrc(self,n,TICKSPERMIN,MINPERDAY) end + function dobj:addseconds(n) return dobj_adddayfrc(self,n,TICKSPERSEC,SECPERDAY) end + function dobj:addticks(n) return dobj_adddayfrc(self,n,1,TICKSPERDAY) end + local tvspec = { + -- Abbreviated weekday name (Sun) + ['%a']=function(self) return sl_weekdays[weekday(self.daynum) + 7] end, + -- Full weekday name (Sunday) + ['%A']=function(self) return sl_weekdays[weekday(self.daynum)] end, + -- Abbreviated month name (Dec) + ['%b']=function(self) return sl_months[self:getmonth() - 1 + 12] end, + -- Full month name (December) + ['%B']=function(self) return sl_months[self:getmonth() - 1] end, + -- Year/100 (19, 20, 30) + ['%C']=function(self) return fmt("%.2d", fix(self:getyear()/100)) end, + -- The day of the month as a number (range 1 - 31) + ['%d']=function(self) return fmt("%.2d", self:getday()) end, + -- year for ISO 8601 week, from 00 (79) + ['%g']=function(self) return fmt("%.2d", mod(self:getisoyear() ,100)) end, + -- year for ISO 8601 week, from 0000 (1979) + ['%G']=function(self) return fmt("%.4d", self:getisoyear()) end, + -- same as %b + ['%h']=function(self) return self:fmt0("%b") end, + -- hour of the 24-hour day, from 00 (06) + ['%H']=function(self) return fmt("%.2d", self:gethours()) end, + -- The hour as a number using a 12-hour clock (01 - 12) + ['%I']=function(self) return fmt("%.2d", self:getclockhour()) end, + -- The day of the year as a number (001 - 366) + ['%j']=function(self) return fmt("%.3d", self:getyearday()) end, + -- Month of the year, from 01 to 12 + ['%m']=function(self) return fmt("%.2d", self:getmonth()) end, + -- Minutes after the hour 55 + ['%M']=function(self) return fmt("%.2d", self:getminutes())end, + -- AM/PM indicator (AM) + ['%p']=function(self) return sl_meridian[self:gethours() > 11 and 1 or -1] end, --AM/PM indicator (AM) + -- The second as a number (59, 20 , 01) + ['%S']=function(self) return fmt("%.2d", self:getseconds()) end, + -- ISO 8601 day of the week, to 7 for Sunday (7, 1) + ['%u']=function(self) return self:getisoweekday() end, + -- Sunday week of the year, from 00 (48) + ['%U']=function(self) return fmt("%.2d", self:getweeknumber()) end, + -- ISO 8601 week of the year, from 01 (48) + ['%V']=function(self) return fmt("%.2d", self:getisoweeknumber()) end, + -- The day of the week as a decimal, Sunday being 0 + ['%w']=function(self) return self:getweekday() - 1 end, + -- Monday week of the year, from 00 (48) + ['%W']=function(self) return fmt("%.2d", self:getweeknumber(2)) end, + -- The year as a number without a century (range 00 to 99) + ['%y']=function(self) return fmt("%.2d", mod(self:getyear() ,100)) end, + -- Year with century (2000, 1914, 0325, 0001) + ['%Y']=function(self) return fmt("%.4d", self:getyear()) end, + -- Time zone offset, the date object is assumed local time (+1000, -0230) + ['%z']=function(self) local b = -self:getbias(); local x = abs(b); return fmt("%s%.4d", b < 0 and "-" or "+", fix(x/60)*100 + floor(mod(x,60))) end, + -- Time zone name, the date object is assumed local time + ['%Z']=function(self) return self:gettzname() end, + -- Misc -- + -- Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to "%Y" (1 BCE, 40 BCE) + ['%\b']=function(self) local x = self:getyear() return fmt("%.4d%s", x>0 and x or (-x+1), x>0 and "" or " BCE") end, + -- Seconds including fraction (59.998, 01.123) + ['%\f']=function(self) local x = self:getfracsec() return fmt("%s%.9g",x >= 10 and "" or "0", x) end, + -- percent character % + ['%%']=function(self) return "%" end, + -- Group Spec -- + -- 12-hour time, from 01:00:00 AM (06:55:15 AM); same as "%I:%M:%S %p" + ['%r']=function(self) return self:fmt0("%I:%M:%S %p") end, + -- hour:minute, from 01:00 (06:55); same as "%I:%M" + ['%R']=function(self) return self:fmt0("%I:%M") end, + -- 24-hour time, from 00:00:00 (06:55:15); same as "%H:%M:%S" + ['%T']=function(self) return self:fmt0("%H:%M:%S") end, + -- month/day/year from 01/01/00 (12/02/79); same as "%m/%d/%y" + ['%D']=function(self) return self:fmt0("%m/%d/%y") end, + -- year-month-day (1979-12-02); same as "%Y-%m-%d" + ['%F']=function(self) return self:fmt0("%Y-%m-%d") end, + -- The preferred date and time representation; same as "%x %X" + ['%c']=function(self) return self:fmt0("%x %X") end, + -- The preferred date representation, same as "%a %b %d %\b" + ['%x']=function(self) return self:fmt0("%a %b %d %\b") end, + -- The preferred time representation, same as "%H:%M:%\f" + ['%X']=function(self) return self:fmt0("%H:%M:%\f") end, + -- GroupSpec -- + -- Iso format, same as "%Y-%m-%dT%T" + ['${iso}'] = function(self) return self:fmt0("%Y-%m-%dT%T") end, + -- http format, same as "%a, %d %b %Y %T GMT" + ['${http}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end, + -- ctime format, same as "%a %b %d %T GMT %Y" + ['${ctime}'] = function(self) return self:fmt0("%a %b %d %T GMT %Y") end, + -- RFC850 format, same as "%A, %d-%b-%y %T GMT" + ['${rfc850}'] = function(self) return self:fmt0("%A, %d-%b-%y %T GMT") end, + -- RFC1123 format, same as "%a, %d %b %Y %T GMT" + ['${rfc1123}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end, + -- asctime format, same as "%a %b %d %T %Y" + ['${asctime}'] = function(self) return self:fmt0("%a %b %d %T %Y") end, + } + function dobj:fmt0(str) return (gsub(str, "%%[%a%%\b\f]", function(x) local f = tvspec[x];return (f and f(self)) or x end)) end + function dobj:fmt(str) + str = str or self.fmtstr or fmtstr + return self:fmt0((gmatch(str, "${%w+}")) and (gsub(str, "${%w+}", function(x)local f=tvspec[x];return (f and f(self)) or x end)) or str) + end + + function dobj.__lt(a,b) return (a.daynum == b.daynum) and (a.dayfrc < b.dayfrc) or (a.daynum < b.daynum) end + function dobj.__le(a, b)return (a.daynum == b.daynum) and (a.dayfrc <= b.dayfrc) or (a.daynum <= b.daynum) end + function dobj.__eq(a, b)return (a.daynum == b.daynum) and (a.dayfrc == b.dayfrc) end + function dobj.__sub(a,b) + local d1, d2 = date_getdobj(a), date_getdobj(b) + local d0 = d1 and d2 and date_new(d1.daynum - d2.daynum, d1.dayfrc - d2.dayfrc) + return d0 and d0:normalize() + end + function dobj.__add(a,b) + local d1, d2 = date_getdobj(a), date_getdobj(b) + local d0 = d1 and d2 and date_new(d1.daynum + d2.daynum, d1.dayfrc + d2.dayfrc) + return d0 and d0:normalize() + end + function dobj.__concat(a, b) return tostring(a) .. tostring(b) end + function dobj:__tostring() return self:fmt() end + + function dobj:copy() return date_new(self.daynum, self.dayfrc) end + + --[[ THE LOCAL DATE OBJECT METHODS ]]-- + function dobj:tolocal() + local dn,df = self.daynum, self.dayfrc + local bias = getbiasutc2(self) + if bias then + -- utc = local + bias; local = utc - bias + self.daynum = dn + self.dayfrc = df - bias*TICKSPERSEC + return self:normalize() + else + return nil + end + end + + function dobj:toutc() + local dn,df = self.daynum, self.dayfrc + local bias = getbiasloc2(dn, df) + if bias then + -- utc = local + bias; + self.daynum = dn + self.dayfrc = df + bias*TICKSPERSEC + return self:normalize() + else + return nil + end + end + + function dobj:getbias() return (getbiasloc2(self.daynum, self.dayfrc))/SECPERMIN end + + function dobj:gettzname() + local _, tvu, _ = getbiasloc2(self.daynum, self.dayfrc) + return tvu and osdate("%Z",tvu) or "" + end + +--#if not DATE_OBJECT_AFX then + function date.time(h, r, s, t) + h, r, s, t = tonumber(h or 0), tonumber(r or 0), tonumber(s or 0), tonumber(t or 0) + if h and r and s and t then + return date_new(DAYNUM_DEF, makedayfrc(h, r, s, t)) + else + return date_error_arg() + end + end + + function date:__call(...) + local arg = {...} + local n = #arg + if n > 1 then return (date_from(...)) + elseif n == 0 then return (date_getdobj(false)) + else local o, r = date_getdobj(arg[1]); return r and o:copy() or o end + end + + date.diff = dobj.__sub + + function date.isleapyear(v) + local y = fix(v); + if not y then + y = date_getdobj(v) + y = y and y:getyear() + end + return isleapyear(y+0) + end + + function date.epoch() return date_epoch:copy() end + + function date.isodate(y,w,d) return date_new(makedaynum_isoywd(y + 0, w and (w+0) or 1, d and (d+0) or 1), 0) end + +-- Internal functions + function date.fmt(str) if str then fmtstr = str end; return fmtstr end + function date.daynummin(n) DAYNUM_MIN = (n and n < DAYNUM_MAX) and n or DAYNUM_MIN return n and DAYNUM_MIN or date_new(DAYNUM_MIN, 0):normalize()end + function date.daynummax(n) DAYNUM_MAX = (n and n > DAYNUM_MIN) and n or DAYNUM_MAX return n and DAYNUM_MAX or date_new(DAYNUM_MAX, 0):normalize()end + function date.ticks(t) if t then setticks(t) end return TICKSPERSEC end +--#end -- not DATE_OBJECT_AFX + + local tm = osdate("!*t", 0); + if tm then + date_epoch = date_new(makedaynum(tm.year, tm.month - 1, tm.day), makedayfrc(tm.hour, tm.min, tm.sec, 0)) + -- the distance from our epoch to os epoch in daynum + DATE_EPOCH = date_epoch and date_epoch:spandays() + else -- error will be raise only if called! + date_epoch = setmetatable({},{__index = function() error("failed to get the epoch date") end}) + end + +--#if not DATE_OBJECT_AFX then +return date +--#else +--$return date_from +--#end + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..f4a90f1 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,525 @@ +LuaDate v2

LuaDate v2

Lua Date and Time module for Lua 5.x.

Jas Latrix

All Rights Deserved. Use at your own risk!. Shake well before using.

1. Introduction

LuaDate is a Lua module for date and time calculation and retrieval using the Gregorian Date system. +

+ To Load the module call require"date" in your script. + Make sure Lua can find the source file date.lua. + A global table called date will be created. + Use the metamethod __call to construct a dateObject see example below: +

+require "date"
+-- prints all FRIDAY the 13TH dates between year 2000 and 2010
+for i = 2000, 2010 do
+	-- year jan 1
+	x = date(i, 1, 1)
+	-- from january to december
+	for j = 1, 12 do
+		-- set date to 13, check if friday
+		if x:setmonth(j, 13):getweekday() == 6 then
+			print(x:fmt("%A, %B %d %Y"))
+		end
+	end
+end
+
+--- OUTPUT ---
+--> Friday, October 13 2000
+--> Friday, April 13 2001
+--> Friday, July 13 2001
+--> Friday, September 13 2002
+--> Friday, December 13 2002
+--> Friday, June 13 2003
+--> Friday, February 13 2004
+--> Friday, August 13 2004
+--> Friday, May 13 2005
+--> Friday, January 13 2006
+--> Friday, October 13 2006
+--> Friday, April 13 2007
+--> Friday, July 13 2007
+--> Friday, June 13 2008
+--> Friday, February 13 2009
+--> Friday, March 13 2009
+--> Friday, November 13 2009
+--> Friday, August 13 2010
+

+

2. Limits

  • This module does not recognized leap seconds.
  • It assume that a day has exactly 24*60*60 seconds.
  • The Lua number must be a double C data type
  • This module supports dates that are greater than + Mon Jan 01 1000000 BCE 00:00:00 and less than + Mon Jan 01 1000001 00:00:00.
  • see Local time support for local time limts

3. Local time support

+ This module also support local time. Local Time is. +
Local = UTC + bias
+ The bias is time zone offset plus the daylight savings if in effect. + The bias is retrieve using the Lua function os.date and os.time. + It assumes that the Lua function os.time returns the number of seconds since the start time (called "epoch"). + If the time value is outside the allowable range of times, usually + Jan 01 1970 00:00:00 to + Jan 19 2038 03:14:07 the bias will be retrieve + using the equivalent year inside the allowable range. Two years are considered to + equivalent if they have the same leap year ness and starting weekday. +

The function that needs local time support are + date(true) (get the current UTC time), + date(false) (get the current local time), + date(num_time), + dateObj:getbias(), + dateObject:fmt(str) (when str has a '%Z' or '%z'), +

4. Parsable date value

+ Parsable date value is a lua value that can be converted to a dateObject. + This value must be num_time or tbl_date or str_date or bool_now argument + describe in the date library __call method. +

5. Parsable month value

+ If a function needs a month value it must be a string or a number. + If the value is a string, it must be the name of the month full or abrrieviated. + If the value is a number, that number must be 1-12 (January-December). see table below +

Table 1. 

IndexAbbriviationFull Name
1JanJanuary
2FebFebruary
3MarMarch
4AprApril
5MayMay
6JunJune
7JulJuly
8AugAugust
9SepSeptember
10OctOctober
11NovNovember
12DecDecember

+ If the value does not represent month, that is equivalent to passing a nil value. +

6. date

The date module.

6.1. Function(s) of date

6.1.1. diff

Subtract the date and time value of two dateObject and returns the resulting dateObject.
Syntax
date.diff(var_date1, var_date2)
Arguments
var_date1
Required. a dateObject or parsable date value
var_date2
Required. a dateObject or parsable date value
Returns
SuccessThe resulting dateObject
Failurenil.
Example
+-- get the days between two dates
+d = date.diff("Jan 7 1563", date(1563, 1, 2))
+assert(d:spandays()==5)
+

6.1.2. epoch

Returns dateObject whose date and time value is the Operating System epoch.
Syntax
date.epoch()
Returns
SuccessThe resulting dateObject
Failurenil.
Example
+assert(date.epoch()==date("jan 1 1970"))
+

6.1.3. isleapyear

Check if a number or dateObject is a leapyear.
Syntax
date.isleapyear(var_year)
Arguments
var_year
a number or dateObject or parsable date value
Returns
true if var_year leap year. + false if var_year not leap year. +
Remarks
+ A leap year in the Gregorian calendar is defined as a year that is evenly + divisible by four, except if it is divisible by 100; however, years that are + divisible by 400 are leap years. +
Example
+d = date(1776, 1, 1)
+assert(date.isleapyear(d))
+assert(date.isleapyear(d:getyear()))
+assert(date.isleapyear(1776))
+

6.2. Method(s) of date

6.2.1. __call

+ Construct a dateObject. It has 3 method of constructing a date object. +
  • 3 or more arguments - int_year, var_month, int_day, num_hour, num_min, num_sec, int_ticks
  • 1 argument - num_time or tbl_date or str_date or bool_now
  • no argument - same as date(false)
+ This is a metamethod of date. + Remember date:__call(...) is the same as date(...). +
Syntax
date(num_time)
date(tbl_date)
date(str_date)
date(bool_now)
date(int_year, var_month, int_day, [num_hour], [num_min], [num_sec], [int_ticks])
Arguments
num_time
Required number value. Represents the number of seconds in Universal Coordinated Time between the specified value and the System epoch.
tbl_date
Required table value. The constructor will look for the value of this key: +
  • year - an integer, the full year, for example, 1969. Required if month and day is given
  • month - a parsable month value. Required if year and day is given
  • day - an integer, the day of month from 1 to 31. Required if year and month is given
  • hour - a number, hours value, from 0 to 23, indicating the number of hours since midnight. (default = 0)
  • min - a number, minutes value, from 0 to 59. (default = 0)
  • sec - a number, seconds value, from 0 to 59. (default = 0)
+ Time (hour or min or sec or msec) must be supplied if + date (year and month and day) is not given, vice versa. +
str_date
Required string value. It must have number/words representing date and/or time. + Use commas and spaces as delimiters. + Strings enclosed by parenthesis is treated as a comment and is ignored, these parentheses may be nested. + The stated day of the week is ignored whether its correct or not. + A string containing an invalid date is an error. + For example, a string containing two years or two months is an error. + Time must be supplied if date is not given, vice versa. +

Time Format.  + Hours, minutes, and seconds are separated by colons, although all need not be specified. "10:", "10:11", and "10:11:12" are all valid. + If the 24-hour clock is used, it is an error to specify "PM" for times later than 12 noon. For example, "23:15 PM" is an error. +

Time Zone Format.  + First character is a sign "+" (east of UTC) or "-" (west of UTC). + Hours and minutes offset are separated by colons: +

+assert( date("Jul 27 2006 03:56:28 +2:00") == date(2006,07,27,1,56,28))
+

+ Another format is [sign][number] + If [number] is less than 24, it is the offset in hours e.g. "-10" = -10 hours. + Otherwise it is the offset in houndred hours e.g. "+75" = "+115" = +1.25 hours. +

+assert(date("Jul 27 2006 -75 ") == date(2006,07,27,1,15,0))
+assert(date("Jul 27 2006 -115") == date(2006,07,27,1,15,0))
+assert(date("Jul 27 2006 +10 ") == date(2006,07,26,14,0,0))
+assert(date("Jul 27 2006 +2  ") == date(2006,07,26,22,0,0))
+

+Standard timezone GMT, UTC, EST, EDT, CST, CDT, MST, MDT, PST, PDT are supported. +

+assert(date("Jul 27 2006 GMT") == date(2006,07,27,0,0,0))
+assert(date("Jul 27 2006 UTC") == date(2006,07,27,0,0,0))
+assert(date("Jul 27 2006 EST") == date(2006,07,27,5,0,0))
+assert(date("Jul 27 2006 EDT") == date(2006,07,27,4,0,0))
+assert(date("Jul 27 2006 CST") == date(2006,07,27,6,0,0))
+assert(date("Jul 27 2006 CDT") == date(2006,07,27,5,0,0))
+assert(date("Jul 27 2006 MST") == date(2006,07,27,7,0,0))
+assert(date("Jul 27 2006 MDT") == date(2006,07,27,6,0,0))
+assert(date("Jul 27 2006 PST") == date(2006,07,27,8,0,0))
+assert(date("Jul 27 2006 PDT") == date(2006,07,27,7,0,0))
+

Date Format.  + Short dates can use either a "/" or "-" date separator, but must follow the month/day/year format +

+assert(date("02-03-04")==date(1904,02,03))
+assert(date("12/25/98")==date(1998,12,25))
+

+ Long dates of the form "July 10 1995" can be given with the year, month, and day in any order, and the year in 2-digit or 4-digit form. + If you use the 2-digit form, the year must be greater than or equal to 70. +

+assert(date("Feb-03-04")==date(1904,02,03))
+assert(date("December 25 1998")==date(1998,12,25))
+

+ Follow the year with BC or BCE to indicate that the year is before common era. +

+assert(date("Feb 3 0003 BC")==date(-2,02,03))
+assert(date("December 25 0001 BC")==date(0,12,25))
+

Supported ISO 8601 Formats. 

+ YYYY-MM-DD +
+ where YYYY is the year, MM is the month of the year, and DD is the day of the month. +
+assert(date("2000-12-31")==date(2000,12,31))
+assert(date(" 20001231 ")==date(2000,12,31)) -- Compact version
+
+ YYYY-DDD +
+ where YYYY is the year, DDD is the day of the year. +
+assert(date("1995-035")==date(1995,02,04))
+assert(date("1995035 ")==date(1995,02,04)) -- Compact version
+
+ YYYY-WDD-D +
+ where YYYY is the year, DD is the week of the year, D is the day of the week. +
+assert(date("1997-W01-1")==date(1996,12,30))
+assert(date("  1997W017")==date(1997,01,05)) -- Compact version
+
+ DATE HH:MM:SS.SSS +
+Where DATE is the date format discuss above, HH is the hour, MM is the miute, SS.SSS is the seconds (fraction is optional). +
+assert(date("1995-02-04 24:00:51.536")==date(1995,2,5,0,0,51.536))
+assert(date("1976-W01-1 12:12:12.123")==date(1975,12,29,12,12,12.123))
+assert(date("1995-035 23:59:59.99999")==date(1995,02,04,23,59,59.99999))
+-- Compact version separated by latin capital letter T
+assert(date("  19950205T000051.536  ")==date(1995,2,5,0,0,51.536))
+assert(date("  1976W011T121212.123  ")==date(1975,12,29,12,12,12.123))
+assert(date(" 1995035T235959.99999  ")==date(1995,02,04,23,59,59.99999))
+
DATE TIME +HH:MM, +DATE TIME -HHMM, +DATE TIME Z,
+Where DATE and TIME is the dateand time format discuss above. +First character is a sign "+" (east of UTC) or "-" (west of UTC). +HH and MM is Hours and minutes offset. The Z stands for the zero offset. +
+assert(date("1976-W01-1 12:00Z     ")==date(1975,12,29,12))
+assert(date("1976-W01-1 13:00+01:00")==date(1975,12,29,12))
+assert(date("1976-W01-1 0700-0500  ")==date(1975,12,29,12))
+
bool_now
Required boolean value. + if bool_now is false it returns the current local date and time. + if bool_now is true it returns the current UTC date and time. +
int_year
Required interger value. The year value.
var_month
Required. A parsable month value.
int_day
Required interger value. The day of month.
num_hour
Optional number value. Equal to the hours value. The default value is 0
num_min
Optional number value. Equal to the minutes value. The default value is 0
num_sec
Optional number value. Equal to the seconds value. The default value is 0
int_ticks
Optional interger value. Equal to the ticks value. The default value is 0
Returns
Successthe new dateObject
Failurenil.
Example
+a = date(2006, 8, 13)   assert(a == date("Sun Aug 13 2006"))
+b = date("Jun 13 1999") assert(b == date(1999, 6, 13))
+c = date(1234483200)    assert(c == date("Feb 13 2009"))
+d = date({year=2009, month=11, day=13, min=6})
+	assert(d == date("Nov 13 2009 00:06:00"))
+e = date() assert(e)
+

7. dateObject

dateObject is a table containing date and time value. + It has a metatable for manipulation and retrieval of dates and times. + Use the __call method of date to construct a dateObject. + +

7.1. How Date and Time are stored in dateObject

+ Time is stored in dateObject as Ticks or Day Fraction. + Date is stored in dateObject as Day Number. + Ticks is time unit per seconds. + For example, if the tick unit is 1000000. + 0.25 seconds is equivalent to 250000 ticks (0.25*1000000). + Day number, is the number days since the epoch, which is January 1, 0001 AD. + Example. +
+dobj = date("15:49:59.3669")
+
+If the tick unit is 1000000, dobj store this time as 56999366900 ticks and 0 days. +
+((((15*60)+49)*60)+59.3669)*1000000 == 56999366900
+
+Example Date and Time: +
+dobj = date("Jan-5-0001 10:30:15")
+
+If the tick unit is 1000000, dobj store this date and time as 37815000000 ticks and 4 days. +4 days becuase: + + + + + + + + + + + + + + + + + + + + + + + + +
Day#Date
0Jan 1 0001
1Jan 2 0001
2Jan 3 0001
3Jan 4 0001
+ 4 + + Jan 5 0001 +
5Jan 6 0001
......

The default tick unit is 1000000 (micro-second-ticks)

7.2. Supported MetaMethods

The a < b operation.
+ Returns true if date value of a is later than date value of b. + a > b is equivalent to b < a. +
The a <= b operation.
+ Returns true if date value of a is later than or equal to the date value of b. + a >= b is equivalent to b <= a. +
The a == b operation.
+ Returns true if date value of a is equal equal to the date value of b. + a ~= b is equivalent to not (a == b). +
The a .. b operation.
+ Equivalent to tostring(a) .. tostring(b). +
The a - b operation.
+ Subtract the date and time value of a to date and time value of b. +
The a + b operation.
+ Add the date and time value of a to date and time value of b. +
a and b must be a parsable date value or an error rises +
+a = date(1521,5,2)
+b = a:copy():addseconds(0.001)
+
+assert((a - b):spanseconds() == -0.001)
+assert((a + b) == (b + a))
+assert(a == (b - date("00:00:00.001")) )
+assert(b == (a + date("00:00:00.001")) )
+
+b:addseconds(-0.01)
+
+assert(a >  b and b <  a)
+assert(a >= b and b <= a)
+assert(a ~= b and (not(a == b)))
+
+a = b:copy()
+
+assert(not (a >  b and b <  a))
+assert(a >= b and b <= a)
+assert(a == b and (not(a ~= b)))
+
+assert((a .. 565369) == (b .. 565369))
+assert((a .. "????") == (b .. "????"))
+				

7.3. Method(s) of dateObject

7.3.1. adddays

Add days in dateObject
Syntax
dateObject:adddays(int_days)
Arguments
int_days
Required integer value. Days to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier.
Example
+a = date(2000,12,30)
+b = date(a):adddays(3)
+c = date.diff(b,a)
+assert(c:spandays() == 3)
+

7.3.2. addhours

Add hours in dateObject
Syntax
dateObject:addhours(num_hours)
Arguments
num_hours
Required number value. Hours to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier.
Example
+a = date(2000,12,30)
+b = date(a):addhours(3)
+c = date.diff(b,a)
+assert(c:spanhours() == 3)
+

7.3.3. addminutes

Add minutes in dateObject
Syntax
dateObject:addminutes(num_minutes)
Arguments
num_minutes
Required number value. Minutes to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier.
Example
+a = date(2000,12,30)
+b = date(a):addminutes(3)
+c = date.diff(b,a)
+assert(c:spanminutes() == 3)
+

7.3.4. addmonths

Add months in dateObject
Syntax
dateObject:addmonths(int_months)
Arguments
int_months
Required integer value. Months to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier.
Example
+a = date(2000,12,28):addmonths(3)
+assert(a:getmonth() == 3)
+

7.3.5. addseconds

Add seconds in dateObject
Syntax
dateObject:addseconds(num_sec)
Arguments
num_sec
Required number value. Seconds to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier.
Example
+a = date(2000,12,30)
+b = date(a):addseconds(3)
+c = date.diff(b,a)
+assert(c:spanseconds() == 3)
+

7.3.6. addticks

Add ticks in dateObject
Syntax
dateObject:addticks(num_ticks)
Arguments
num_ticks
Required number value. Ticks to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier. + For discussion about ticks see Section 7.1, “How Date and Time are stored in dateObject. +
Example
+a = date(2000,12,30)
+b = date(a):addticks(3)
+c = date.diff(b,a)
+assert(c:spanticks() == 3)
+

7.3.7. addyears

Add years in dateObject
Syntax
dateObject:addyears(int_years)
Arguments
int_years
Required integer value. Years to add.
Returns
Successreference to the adjusted dateObject
Failurenil.
Remarks
If the value is negative, the adjusted dateObject will be earlier.
Example
+a = date(2000,12,30):addyears(3)
+assert(a:getyear() == (2000+3))
+

7.3.8. copy

Returns a new date object having the same date and time value of dateObject
Syntax
dateObject:copy()
Example
+a = date(2000,12,30)
+b = a:copy()
+assert(a==b)
+

7.3.9. fmt

Returns a formatted version of dateObject.
Syntax
dateObject:fmt(str_code)
Arguments
str_code
string value. + The format string follows the same rules as the strftime standard C function. +

Table 3. Format Spec

SpecDescription
'%a'Abbreviated weekday name (Sun)
'%A'Full weekday name (Sunday)
'%b'Abbreviated month name (Dec)
'%B'Full month name (December)
'%C'Year/100 (19, 20, 30)
'%d'The day of the month as a number (range 1 - 31)
'%g'year for ISO 8601 week, from 00 (79)
'%G'year for ISO 8601 week, from 0000 (1979)
'%h'same as %b
'%H'hour of the 24-hour day, from 00 (06)
'%I'The hour as a number using a 12-hour clock (01 - 12)
'%j'The day of the year as a number (001 - 366)
'%m'Month of the year, from 01 to 12
'%M'Minutes after the hour 55
'%p'AM/PM indicator (AM)
'%S'The second as a number (59, 20 , 01)
'%u'ISO 8601 day of the week, to 7 for Sunday (7, 1)
'%U'Sunday week of the year, from 00 (48)
'%V'ISO 8601 week of the year, from 01 (48)
'%w'The day of the week as a decimal, Sunday being 0
'%W'Monday week of the year, from 00 (48)
'%y'The year as a number without a century (range 00 to 99)
'%Y'Year with century (2000, 1914, 0325, 0001)
'%z'Time zone offset, the date object is assumed local time (+1000, -0230)
'%Z'Time zone name, the date object is assumed local time
'%\b'Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to "%Y" (1 BCE, 40 BCE) #
'%\f'Seconds including fraction (59.998, 01.123) #
'%%'percent character %
'%r'12-hour time, from 01:00:00 AM (06:55:15 AM); same as "%I:%M:%S %p"
'%R'hour:minute, from 01:00 (06:55); same as "%I:%M"
'%T'24-hour time, from 00:00:00 (06:55:15); same as "%H:%M:%S"
'%D'month/day/year from 01/01/00 (12/02/79); same as "%m/%d/%y"
'%F'year-month-day (1979-12-02); same as "%Y-%m-%d"
'%c'The preferred date and time representation; same as "%x %X"
'%x'The preferred date representation, same as "%a %b %d %\b"
'%X'The preferred time representation, same as "%H:%M:%\f"
'${iso}'Iso format, same as "%Y-%m-%dT%T"
'${http}'http format, same as "%a, %d %b %Y %T GMT"
'${ctime}'ctime format, same as "%a %b %d %T GMT %Y"
'${rfc850}'RFC850 format, same as "%A, %d-%b-%y %T GMT"
'${rfc1123}'RFC1123 format, same as "%a, %d %b %Y %T GMT"
'${asctime}'asctime format, same as "%a %b %d %T %Y"

Remarks
Only English names are supported
Example
+d = date(1582,10,5)
+assert(d:fmt('%D') == d:fmt("%m/%d/%y"))	-- month/day/year from 01/01/00 (12/02/79)
+assert(d:fmt('%F') == d:fmt("%Y-%m-%d"))	-- year-month-day (1979-12-02)
+assert(d:fmt('%h') == d:fmt("%b"))			-- same as %b (Dec)
+assert(d:fmt('%r') == d:fmt("%I:%M:%S %p"))	-- 12-hour time, from 01:00:00 AM (06:55:15 AM)
+assert(d:fmt('%T') == d:fmt("%H:%M:%S"))	-- 24-hour time, from 00:00:00 (06:55:15)
+assert(d:fmt('%a %A %b %B') == "Tue Tuesday Oct October")
+assert(d:fmt('%C %d') == "15 05", d:fmt('%C %d'))
+
+print(d:fmt[[
+${iso} -- iso
+${http} -- http
+${ctime} -- ctime
+${rfc850} -- rfc850
+${rfc1123} -- rfc1123
+${asctime} -- asctime
+]])
+
Example (Prints the current date and time)
+-- Prints the current date and time, including time zone
+d = date(false);
+print(d:fmt("Today is %c GMT%z"))
+--> "Today is Tue Oct 31 2000 01:58:14 GMT-0330"
+
+-- Prints the current date and time in ISO format including time zone
+d = date(false);
+print(d:fmt("Today is ${iso}%z"))
+--> "Today is 2000-10-31T01:58:14-0330"
+
+-- Prints the current date and time in ISO format, indicates UTC
+d = date(true);
+print(d:fmt("Today is ${iso}Z"))
+--> "Today is 2000-10-31T05:28:14Z"
+

7.3.10. getbias

Assuming dateObject is a local time. + Returns the time zone offset. + Returns nil on failure. +
Syntax
dateObject:getbias()
Example
+a = date(2^16)
+print(a:getbias())
+

7.3.11. getclockhour

Returns the hours value using a 12-hour clock in a dateObject.
Syntax
dateObject:getclockhour()
Example
+a = date("10:59:59 pm")
+assert(a:getclockhour()==10)
+

7.3.12. getdate

Returns the + year, + month, and + day value in a dateObject.
Syntax
dateObject:getdate()
Example
+a = date(1970, 1, 1)
+y, m, d = a:getdate()
+assert(y == 1970 and m == 1 and d == 1)
+

7.3.13. getday

Returns the day of month value in a dateObject
Syntax
dateObject:getday()
Example
+d = date(1966, 'sep', 6)
+assert(d:getday() == 6)
+

7.3.14. getfracsec

Returns the seconds after the minute (fractional) value in a dateObject.
Syntax
dateObject:getfracsec()
Example
+d = date("Wed Apr 04 2181 11:51:06.996 UTC")
+assert(d:getfracsec() == 6.996)
+

7.3.15. gethours

Returns the hours value in a dateObject
Syntax
dateObject:gethours()
Example
+d = date("Wed Apr 04 2181 11:51:06 UTC")
+assert(d:gethours() == 11)
+

7.3.16. getisoweekday

Returns the day of week (sunday=7, monday=1, ...saturday=6) in a dateObject.
Syntax
dateObject:getisoweekday()
Example
+d = date(1970, 1, 1)
+assert(d:getisoweekday() == 4)
+

7.3.17. getisoweeknumber

Returns the ISO 8601 week number (01 to 53) in a dateObject. Using the Year-WeekOfYear-DayOfWeek date system
Syntax
dateObject:getisoweeknumber()
Example
+d = date(1975, 12, 29)
+assert(d:getisoweeknumber() == 1)
+assert(d:getisoyear() == 1976)
+assert(d:getisoweekday() == 1)
+d = date(1977, 1, 2)
+assert(d:getisoweeknumber() == 53)
+assert(d:getisoyear() == 1976)
+assert(d:getisoweekday() == 7)
+

7.3.18. getisoyear

Returns the ISO 8601 year in a dateObject. Using the Year-WeekOfYear-DayOfWeek date system
Syntax
dateObject:getisoyear()
Example
+d = date(1996, 12, 30)
+assert(d:getisoyear() == 1997)
+d = date(1997, 01, 05)
+assert(d:getisoyear() == 1997)
+

7.3.19. getminutes

Returns the minutes after the hour value in a dateObject
Syntax
dateObject:getminutes()
Example
+d = date("Wed Apr 04 2181 11:51:06 UTC")
+assert(d:getminutes() == 51)
+

7.3.20. getmonth

Returns the month value in a dateObject
Syntax
dateObject:getmonth()
Example
+d = date(1966, 'sep', 6)
+assert(d:getmonth() == 9)
+

7.3.21. getseconds

Returns the seconds after the minute value in a dateObject
Syntax
dateObject:getseconds()
Example
+d = date("Wed Apr 04 2181 11:51:06.123 UTC")
+assert(d:getseconds() == 6)
+

7.3.22. getticks

Returns the ticks after the seconds value in a dateObject
Syntax
dateObject:getticks()
Remarks
For discussion about ticks see Section 7.1, “How Date and Time are stored in dateObject. +
Example
+d = date("Wed Apr 04 2181 11:51:06.123 UTC")
+assert(d:getticks() == 123000)
+

7.3.23. gettime

Returns the hours, + minutes, + seconds and + ticks value in a dateObject.
Syntax
dateObject:gettime()
Example
+a = date({hour=5,sec=.5,min=59})
+h, m, s, t = a:gettime()
+assert(t == 500000 and s == 0 and m == 59 and h == 5, tostring(a))
+

7.3.24. getweekday

Returns the day of week (sunday=1, monday=2, ...saturday=7) in a dateObject.
Syntax
dateObject:getweekday()
Example
+d = date(1970, 1, 1)
+assert(d:getweekday() == 5)
+

7.3.25. getweeknumber

Returns the week number value in a dateObject.
Syntax
dateObject:getweeknumber([int_wdaybase])
Arguments
int_wdaybase
Optional integer value. + The starting day of week (1 for sunday, 2 for monday, ... 7 for saturday). + If omitted the starting day of week is sunday. +
Example
+a = date("12/31/1972")
+b,c = a:getweeknumber(), a:getweeknumber(2)
+assert(b==53 and c==52)
+

7.3.26. getyear

Returns the year value in a dateObject
Syntax
dateObject:getyear()
Example
+d = date(1965, 'jan', 0)
+assert(d:getyear() == 1964)
+

7.3.27. getyearday

Returns the day of year (1-366) in a dateObject.
Syntax
dateObject:getyearday()
Example
+d = date(2181, 1, 12)
+assert(d:getyearday() == 12)
+

7.3.28. setday

Sets the day of month value in dateObject
Syntax
dateObject:setday(int_mday)
Arguments
int_mday
integer value. A numeric value equal to the day of month. The default value is the current day of month
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1966, 'july', 6)
+d:setday(1)
+assert(d == date("1966 july 1"))
+

7.3.29. sethours

Sets the hour value in dateObject
Syntax
dateObject:sethours(num_hour, num_min, num_sec, num_ticks)
Arguments
num_hour
number value. The hours value. The default value is the current hours value
num_min
number value. The minutes after the hours value. The default value is the current minutes value
num_sec
number value. The seconds after the minute value. The default value is the current seconds value
num_ticks
number value. The ticks after the second value. The default value is the current ticks value
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1984, 12, 3, 4, 39, 54)
+d:sethours(1, 1, 1)
+assert(d == date("1984 DEc 3 1:1:1"))
+

7.3.30. setisoweekday

Sets the ISO 8601 week number value in dateObject. Using the Year-WeekOfYear-DayOfWeek date system
Syntax
dateObject:setisoweekday(int_wday)
Arguments
int_wday
integer value. The day of month. The default value is the current week day
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date.isodate(1999, 52, 1)
+d:setisoweekday(7)
+assert(d == date(2000, 1, 02))
+

7.3.31. setisoweeknumber

Sets the ISO 8601 week number value in dateObject. Using the Year-WeekOfYear-DayOfWeek date system
Syntax
dateObject:setisoweeknumber(int_week, int_wday)
Arguments
int_week
integer value. The month value. The default value is the current week
int_wday
integer value. The day of month. The default value is the current week day
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1999, 12, 27)
+d:setisoweeknumber(51, 7)
+assert(d == date(1999, 12, 26))
+

7.3.32. setisoyear

Sets the ISO 8601 year value in dateObject. Using the Year-WeekOfYear-DayOfWeek date system
Syntax
dateObject:setisoyear(int_year, int_week, int_wday)
Arguments
int_year
integer value. The year value. The default value is the current year
int_week
integer value. The month value. The default value is the current week
int_wday
integer value. The day of month. The default value is the current week day
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1999, 12, 27)
+d:setisoyear(2000, 1)
+assert(d == date.isodate(2000,1,1))
+assert(d:getyear() == 2000)
+assert(d:getday() == 3)
+

7.3.33. setminutes

Sets the minutes value in dateObject
Syntax
dateObject:setminutes(num_min, num_sec, num_ticks)
Arguments
num_min
number value. The minutes after the value. The default value is the current minutes value
num_sec
number value. The seconds after the minute value. The default value is the current seconds value
num_ticks
number value. The ticks after the second value. The default value is the current ticks value
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1984, 12, 3, 4, 39, 54)
+d:setminutes(59, 59, 500)
+assert(d == date(1984, 12, 3, 4, 59, 59, 500))
+

7.3.34. setmonth

Sets the month value in dateObject
Syntax
dateObject:setmonth(var_month, int_mday)
Arguments
var_month
parsable month value. The default value is the current month
int_mday
integer value. The day of month. The default value is the current day of month
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1966, 'july', 6)
+d:setmonth(1)
+assert(d == date("6 jan 1966"))
+

7.3.35. setseconds

Sets the seconds after the minute value in dateObject
Syntax
dateObject:setseconds(num_sec, num_ticks)
Arguments
num_sec
number value. The seconds after the minute value. The default value is the current seconds value
num_ticks
number value. The ticks after the second value. The default value is the current ticks value
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1984, 12, 3, 4, 39, 54)
+d:setseconds(59, date.ticks())
+assert(d == date(1984, 12, 3, 4, 40))
+

7.3.36. setticks

Sets the ticks after the second value in dateObject
Syntax
dateObject:setticks(num_ticks)
Arguments
num_ticks
number value. The ticks after the second value. The default value is the current ticks value
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1984, 12, 3, 4, 39, 54)
+d:setticks(444)
+assert(d == date(1984, 12, 3, 4, 39, 54, 444))
+

7.3.37. setyear

Sets the year value in dateObject
Syntax
dateObject:setyear(int_year, var_month, int_mday)
Arguments
int_year
integer value. The year value. The default value is the current year
var_month
The month value. The default value is the current month
int_mday
integer value. The day of month. The default value is the current day of month
Returns
Successreference to the dateObject
Failurenil.
Example
+d = date(1966, 'july', 6)
+d:setyear(2000)
+assert(d == date("jul 6 2000"))
+

7.3.38. spandays

Returns how many days the dateObject has
Syntax
dateObject:spandays()
Example
+a = date(2181, "aPr", 4, 6, 30, 30, 15000)
+b = date(a):adddays(2)
+c = date.diff(b, a)
+assert(c:spandays() == (2))
+

7.3.39. spanhours

Returns how many hours the dateObject has
Syntax
dateObject:spanhours()
Example
+a = date(2181, "aPr", 4, 6, 30, 30, 15000)
+b = date(a):adddays(2)
+c = date.diff(b, a)
+assert(c:spanhours() == (2*24))
+

7.3.40. spanminutes

Returns how many minutes the dateObject has
Syntax
dateObject:spanminutes()
Example
+a = date(2181, "aPr", 4, 6, 30, 30, 15000)
+b = date(a):adddays(2)
+c = date.diff(b, a)
+assert(c:spanminutes() == (2*24*60))
+

7.3.41. spanseconds

Returns how many seconds the dateObject has
Syntax
dateObject:spanseconds()
Example
+a = date(2181, "aPr", 4, 6, 30, 30, 15000)
+b = date(a):adddays(2)
+c = date.diff(b, a)
+assert(c:spanseconds() == (2*24*60*60))
+

7.3.42. spanticks

Returns how many ticks the dateObject has
Syntax
dateObject:spanticks()
Remarks
For discussion about ticks see Section 7.1, “How Date and Time are stored in dateObject.
Example
+a = date(2181, "aPr", 4, 6, 30, 30, 15000)
+b = date(a):adddays(2)
+c = date.diff(b, a)
+assert(c:spanseconds() == (2*24*60*60))
+

7.3.43. tolocal

Assuming dateObject is a utc time. + Convert its date and time value to local time. +
Syntax
dateObject:tolocal()
Example
+a = date(2^16)
+b = a:copy():tolocal();
+print(a,b)
+

7.3.44. toutc

Assuming dateObject is a local time. + Convert its date and time value to utc time. +
Syntax
dateObject:toutc()
Example
+a = date(2^16)
+b = a:copy():toutc();
+print(a,b)
+

8. History

v1 (2005)
Binary module
v2 (2006)
Lua module

9. Acknowledgement

http://alcor.concordia.ca/~gpkatch/gdate-method.html + - Date calculation algorithms is based on this site.

diff --git a/samples/mkcalendar.lua b/samples/mkcalendar.lua new file mode 100644 index 0000000..cb86ea2 --- /dev/null +++ b/samples/mkcalendar.lua @@ -0,0 +1,64 @@ +--[[--------------------- +This script makes an html calendar +Syntax: mkcalendar.lua year1 year2 year3 .. yearn > file +arg: + year1 .. yearn - the year(s) of the calendar to generate + file - the name of the file to write the generated text calendar +--]]--------------------- + +require"date" + +function makemonth(y,m) + local t = {} + local d = date(y,m,1) + t.name = d:fmt("%B") + t.year = y + -- get back to the nearest sunday + d:adddays(-(d:getweekday()-1)) + repeat + local tt = {} + table.insert(t,tt) + repeat -- insert the week days + table.insert(tt, d:getday()) + until d:adddays(1):getweekday() == 1 + until d:getmonth() ~= m + return t +end + +local htm_foot = '\n' +local htm_head = [[ + + +]] +local htm_yearhead = '\n' +local htm_monhead = '\n' +local htm_monweek = '\n' +local htm_yearfoot = '\n
%s, %s
sunmontuewedthufrisat
%s%s%s%s%s%s%s
' +function makecalendar(y, iox) + iox:write(htm_yearhead) + for i = 1, 12 do + local tm = makemonth(y, i) + iox:write(string.format(htm_monhead, tm.name, tm.year)) + for k, v in ipairs(tm) do + iox:write(string.format(htm_monweek, v[1], v[2], v[3], v[4], v[5], v[6], v[7])) + end + end + iox:write(htm_yearfoot) + +end + +io.stdout:write(htm_head) + +for k, v in ipairs(arg) do + local y = tonumber(v) + if y then makecalendar(y, io.stdout) end +end + +io.stdout:write(htm_foot) + + diff --git a/samples/mkisocal.lua b/samples/mkisocal.lua new file mode 100644 index 0000000..5eb905a --- /dev/null +++ b/samples/mkisocal.lua @@ -0,0 +1,41 @@ +--[[--------------------- +This script makes an iso year-week-day calendar +Syntax: mkisocal.lua year1 year2 year3 .. yearn > file +arg: + year1 .. yearn - the year(s) of the calendar to generate + file - the name of the file to write the generated text calendar +--]]--------------------- + +require"date" + +local htm_foot = [[]] +local htm_head = [[]] +local htm_yearhead = [[]] +local htm_yearfoot = [[
YearWeekMonTueWedThuFriSatSun
]] +function makecalendar(year, iow) + local d = date():setisoyear(year,1,1) + iow(htm_yearhead) + iow("\n") + while d:getisoyear() == year do + iow(d:fmt("%G%V
%Y-%j")) + repeat iow(d:fmt("%u
%b %d %Y")) + until d:adddays(1):getisoweekday() == 1 + iow("\n") + end + iow(htm_yearfoot) + +end + + +local out = io.write + +out(htm_head) + +for k, v in ipairs(arg) do + local d = tonumber(v); + if d then makecalendar(d, out) end +end + +out(htm_foot) + + diff --git a/samples/test.lua b/samples/test.lua new file mode 100644 index 0000000..8ee7e5e --- /dev/null +++ b/samples/test.lua @@ -0,0 +1,23 @@ +require 'date' + +d1 = date('July 4 2009') +assert(d1:getmonth() == 7) +d2 = d1:copy() +d2:adddays(2) +diff = d2 - d1 +assert(diff:spandays() == 2) +assert(diff:spanhours() == 48) + +assert(date 'July 4 2009' == date '2009-07-04') + +a = date(1970, 1, 1) +y, m, d = a:getdate() +assert(y == 1970 and m == 1 and d == 1) + +d = date(1966, 'sep', 6) +assert(d:getday() == 6) + + + + +