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 a
		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 new1
		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 = lua_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)
			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)
			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=to_int((yy-1900)/4);
    nonleapyears=to_int((yy-1900)/100)
    nonnonleapyears=to_int((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 February 8, 2009 8:07 pm GMT (diff)