Date Formatting Functions

lua-users home
wiki

The following code provides date formatting functions which are not available in the string.format function.

Ideal for formatting dates in reports, or converting dates between different formats when importing data.

..from Sam Lie


--=====================================================
-- the reason this routine is needed is because lua does not 
-- have a sort indexed table function
function table_sort(a, sortfield)
local new1 = {}
local new2 = {}
    for k,v in pairs(a) do
        table.insert(new1, { key=k, val=v } )       
    end
    table.sort(new1, function (a,b) return (a.val[sortfield] < b.val[sortfield]) end)  
    for k,v in pairs(new1) do
        table.insert(new2, v.val)
    end
    return new2
end

---============================================================
function padzero(s, count)
    return string.rep("0", count-string.len(s)) .. s 
end

--========================================================================
-- get date parts for a given ISO 8601 date format (http://richard.warburton.it )
function get_date_parts(date_str)
  _,_,y,m,d=string.find(date_str, "(%d+)-(%d+)-(%d+)")
  return tonumber(y),tonumber(m),tonumber(d)
end

--====================================================
function getmonth(month)
    local months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
    return months[tonumber(month)]
end

--====================================================
function getday_posfix(day)
local idd = math.mod(day,10)
       return   (idd==1 and day~=11 and "st")  or (idd==2 and day~=12 and "nd") or (idd==3 and day~=13 and "rd") or "th"
end


--========================================================================
--  Given a date of certain date_format, returns the date parts yy,mm,dd
--  eg. "12 May 2007" with date_format "dd mmm yyyy"
--   or   "12.05.07" with date_format "dd.mm.yy"
--  will return 2007, 5, 12
--
function get_formatted_date_parts(date_str, date_format)
    local d,m,y, arr, x, yy, mm, dd, use_month_names
    local months = { jan=1, feb=2, mar=3, apr=4, may=5, jun=6, jul=7, aug=8, sep=9, oct=10, nov=11, dec=12 }

    if (date_format) then
    
        if string.find(date_format, "mmm") then
            use_month_names = true
        else
            use_month_names = false
        end

        d = string.find(date_format, "dd")
        m = string.find(date_format, "mm")
        y = string.find(date_format, "yy")
        
        arr = { { pos=y, b="yy" }, { pos=m, b="mm" } , { pos=d, b="dd" }  }
        arr = table_sort(arr, "pos")

        date_format = string.gsub(date_format,"yyyy","(%%d+)")
        date_format = string.gsub(date_format,"mmm","(%%a+)")
        date_format = string.gsub(date_format,"yy","(%%d+)")
        date_format = string.gsub(date_format,"mm","(%%d+)")
        date_format = string.gsub(date_format,"dd","(%%d+)")
        date_format = string.gsub(date_format," ","%%s")
    else
        date_format = "(%d+)-(%d+)-(%d+)"
        arr = { { pos=1, b="yy" }, { pos=2, b="mm" } , { pos=3, b="dd" }  }
    end

    if (date_str and date_str~="") then     
        _, _, arr[1].c, arr[2].c, arr[3].c = string.find(string.lower(date_str), date_format)
    else
        return nil, nil, nil
    end


    arr = table_sort(arr, "b")
    yy = arr[3].c
    mm = arr[2].c
    dd = arr[1].c

    if (use_month_names) then       
    
        mm = months[lower(string.sub(mm,1,3))]
        if (not mm) then
            error("Invalid month name.")
        end
    end

    -- for naughty people who still use two digit years.

    if (string.len(yy)==2) then
        if (tonumber(yy)>40) then
            yy = "19"..yy
        else
            yy = "20"..yy
        end
    end

    return tonumber(yy),tonumber(mm),tonumber(dd)
end



--========================================================================
-- Note : date_str has to be  ISO 8601 date format  ie. yyyy-mm-dd
--
function format_date(date_str, dateformat)
local iyy, imm, idd 

    if (date_str and date_str~="") then
        iyy, imm, idd =  get_date_parts(date_str)
        dateformat = string.gsub(dateformat, "DDD",  idd..string.upper(getday_posfix(idd)))
        dateformat = string.gsub(dateformat, "ddd",  idd..getday_posfix(idd) )
        dateformat = string.gsub(dateformat, "dd", padzero(idd,2))
        dateformat = string.gsub(dateformat, "MMM", string.upper(getmonth(imm)))
        dateformat = string.gsub(dateformat, "mmm", getmonth(imm))
        dateformat = string.gsub(dateformat, "mm", padzero(imm,2))
        dateformat = string.gsub(dateformat, "yyyy", padzero(iyy,4))
        dateformat = string.gsub(dateformat, "yy", string.sub(padzero(iyy,4),3,4))
    else
        dateformat = ""
    end

    return(dateformat)
end

--===============================================
-- convert date to excel serial day number
--
function date_to_excel_date(dd, mm, yy) 
local days, monthdays, leapyears, nonleapyears, nonnonleapyears

    monthdays= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }

    leapyears=tonumber(math.floor((yy-1900)/4));
    nonleapyears=tonumber(math.floor((yy-1900)/100))
    nonnonleapyears=tonumber(math.floor((yy-1600)/400))

    if ((math.mod(yy,4)==0) and mm<3) then
      leapyears = leapyears - 1
    end

    days= 365 * (yy-1900) + leapyears - nonleapyears + nonnonleapyears

    c=1
    while (c<mm) do
      days = days + monthdays[c]
    c=c+1
    end

    days=days+dd+1

    return days
end
	

Here is a test suite.


adate = os.date()
print("Todays date according to your OS format :", adate)

-- for example "mm/dd/yyyy" could be your OS locale date format.
yy,mm,dd =  get_formatted_date_parts(adate, "mm/dd/yyyy")

ISO_date = yy .. "-" .. mm .. "-" .. dd

print("Date formated in ISO 8601", ISO_date)

print(format_date(ISO_date, "dd MMM yyyy") )
print(format_date(ISO_date, "ddd mmm yyyy") )
print(format_date(ISO_date, "dd/mm/yy") )
print(format_date(ISO_date, "yyyy.mm.dd") )
print(format_date(ISO_date, "Today is the ddd of mmm, yyyy.") )

print( date_to_excel_date(22, 2, 2009)  )   -- outputs 39866
  




RecentChanges · preferences
edit · history
Last edited May 15, 2017 11:15 am GMT (diff)