webmcp
changeset 106:bbfbbddf13ad
Support for intervals
author | jbe |
---|---|
date | Sun Nov 04 04:55:22 2012 +0100 (2012-11-04) |
parents | fd31e0aa629a |
children | 6b435d3c0b14 |
files | framework/env/format/interval.lua libraries/atom/atom.lua libraries/mondelefant/mondelefant_atom_connector.lua |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/framework/env/format/interval.lua Sun Nov 04 04:55:22 2012 +0100 1.3 @@ -0,0 +1,65 @@ 1.4 +--[[-- 1.5 +text = -- text with the value formatted as a time, according to the locale settings 1.6 +format.interval( 1.7 + value, -- a time, a timestamp or nil 1.8 + { 1.9 + nil_as = nil_text -- text to be returned for a nil value 1.10 + } 1.11 +) 1.12 + 1.13 +Formats an interval, according to the locale settings. 1.14 + 1.15 +--]]-- 1.16 + 1.17 +function format.interval(value, options) 1.18 + local options = options or {} 1.19 + if value == nil then 1.20 + return options.nil_as or "" 1.21 + end 1.22 + if not atom.has_type(value, atom.interval) then 1.23 + error("Value passed to format.interval(...) is neither an interval, nor nil.") 1.24 + end 1.25 + if value.invalid then 1.26 + return "invalid" 1.27 + end 1.28 + local parts = {} 1.29 + if value.years ~= 0 then 1.30 + parts[#parts+1] = tostring(value.years) 1.31 + if value.years == 1 or value.years == -1 then 1.32 + parts[#parts+1] = "year" -- TODO: localization 1.33 + else 1.34 + parts[#parts+1] = "years" -- TODO: localization 1.35 + end 1.36 + end 1.37 + if value.months ~= 0 then 1.38 + parts[#parts+1] = tostring(value.months) 1.39 + if value.months == 1 or value.months == -1 then 1.40 + parts[#parts+1] = "month" -- TODO: localization 1.41 + else 1.42 + parts[#parts+1] = "months" -- TODO: localization 1.43 + end 1.44 + end 1.45 + if value.days ~= 0 then 1.46 + parts[#parts+1] = tostring(value.days) 1.47 + if value.days == 1 or value.days == -1 then 1.48 + parts[#parts+1] = "day" -- TODO: localization 1.49 + else 1.50 + parts[#parts+1] = "days" -- TODO: localization 1.51 + end 1.52 + end 1.53 + if options.hide_seconds then 1.54 + parts[#parts+1] = string.format( 1.55 + "%02i:%02i", 1.56 + value.hours, 1.57 + math.abs(value.minutes) -- NOTE: hours and minutes always have equal sign 1.58 + ) 1.59 + else 1.60 + parts[#parts+1] = string.format( 1.61 + "%02i:%02i:%02i", 1.62 + value.hours, 1.63 + math.abs(value.minutes), -- NOTE: hours and minutes always have equal sign 1.64 + math.abs(value.seconds) -- NOTE: hours and seconds always have equal sign 1.65 + ) 1.66 + end 1.67 + return table.concat(parts, " ") 1.68 +end
2.1 --- a/libraries/atom/atom.lua Sun Nov 04 04:54:54 2012 +0100 2.2 +++ b/libraries/atom/atom.lua Sun Nov 04 04:55:22 2012 +0100 2.3 @@ -1015,7 +1015,7 @@ 2.4 error("Left operand of '-' operator has wrong type.") 2.5 end 2.6 if getmetatable(value2) == date then 2.7 - return value1.jd - value2.jd -- TODO: transform to interval 2.8 + return value1.jd - value2.jd 2.9 elseif type(value2) == "number" then 2.10 return date(value1.jd - value2) 2.11 else 2.12 @@ -1299,7 +1299,7 @@ 2.13 error("Left operand of '-' operator has wrong type.") 2.14 end 2.15 if getmetatable(value2) == timestamp then 2.16 - return value1.tsec - value2.tsec -- TODO: transform to interval 2.17 + return value1.tsec - value2.tsec 2.18 elseif type(value2) == "number" then 2.19 return timestamp(value1.tsec - value2) 2.20 else 2.21 @@ -1508,7 +1508,7 @@ 2.22 error("Left operand of '-' operator has wrong type.") 2.23 end 2.24 if getmetatable(value2) == time then 2.25 - return value1.dsec - value2.dsec -- TODO: transform to interval 2.26 + return value1.dsec - value2.dsec 2.27 elseif type(value2) == "number" then 2.28 return time((value1.dsec - value2) % 86400) 2.29 else 2.30 @@ -1539,4 +1539,154 @@ 2.31 2.32 2.33 2.34 +-------------- 2.35 +-- interval -- 2.36 +-------------- 2.37 + 2.38 +interval = create_new_type("interval") 2.39 + 2.40 +--[[-- 2.41 +atom.interval.invalid 2.42 + 2.43 +Value representing an invalid interval. 2.44 + 2.45 +--]]-- 2.46 +interval.invalid = interval:_create{ 2.47 + years = not_a_number, months = not_a_number, days = not_a_number, 2.48 + hours = not_a_number, minutes = not_a_number, seconds = not_a_number, 2.49 + hms_seconds = not_a_number, 2.50 + invalid = true 2.51 +} 2.52 +--//-- 2.53 + 2.54 +--[[-- 2.55 +t = -- time based on given data 2.56 +atom.interval:new{ 2.57 + years = years, 2.58 + months = months, 2.59 + days = days, 2.60 + hours = hours, 2.61 + minutes = minutes, 2.62 + seconds = seconds 2.63 +} 2.64 + 2.65 +This method returns a new time value, based on given data. 2.66 + 2.67 +--]]-- 2.68 +function interval:new(args) 2.69 + local args = args 2.70 + if type(args) == "number" then args = { seconds = args } end 2.71 + if type(args) == "table" then 2.72 + if 2.73 + (type(args.years) == "number" or not args.years ) and 2.74 + (type(args.months) == "number" or not args.months ) and 2.75 + (type(args.days) == "number" or not args.days ) and 2.76 + (type(args.hours) == "number" or not args.hours ) and 2.77 + (type(args.minutes) == "number" or not args.minutes) and 2.78 + (type(args.seconds) == "number" or not args.seconds) 2.79 + then 2.80 + local years = args.years and tonumber(args.years) or 0 2.81 + local months = args.months and tonumber(args.months) or 0 2.82 + local days = args.days and tonumber(args.days) or 0 2.83 + local hours = args.hours and tonumber(args.hours) or 0 2.84 + local minutes = args.minutes and tonumber(args.minutes) or 0 2.85 + local seconds = args.seconds and tonumber(args.seconds) or 0 2.86 + if not ( 2.87 + is_integer(years) and is_integer(months) and is_integer(days) and 2.88 + is_integer(hours) and is_integer(minutes) and is_integer(seconds) 2.89 + ) then 2.90 + return interval.invalid 2.91 + end 2.92 + local hms_seconds = 3600 * hours + 60 * minutes + seconds 2.93 + if not is_integer(hms_seconds) then return interval.invalid end 2.94 + local hms_negative = false 2.95 + if hms_seconds < 0 then 2.96 + hms_negative = true 2.97 + hms_seconds = -hms_seconds 2.98 + if not is_integer(hms_seconds) then return interval.invalid end 2.99 + end 2.100 + hours = math.floor(hms_seconds / 3600) 2.101 + minutes = math.floor(hms_seconds / 60) % 60 2.102 + seconds = hms_seconds % 60 2.103 + if hms_negative then 2.104 + hours = -hours 2.105 + minutes = -minutes 2.106 + seconds = -seconds 2.107 + hms_seconds = -hms_seconds 2.108 + end 2.109 + return interval:_create{ 2.110 + years = years, months = months, days = days, 2.111 + hours = hours, minutes = minutes, seconds = seconds, 2.112 + hms_seconds = hms_seconds, 2.113 + } 2.114 + end 2.115 + end 2.116 + error("Invalid arguments passed to interval constructor.") 2.117 +end 2.118 +--//-- 2.119 + 2.120 +--[[-- 2.121 +t = -- interval represented by the string 2.122 +atom.interval:load( 2.123 + string -- string representing an interval 2.124 +) 2.125 + 2.126 +This method returns an interval represented by the given string. 2.127 + 2.128 +--]]-- 2.129 +function interval:load(str) 2.130 + if str == nil or str == "" then 2.131 + return nil 2.132 + elseif type(str) ~= "string" then 2.133 + error("String expected") 2.134 + else 2.135 + local years, months, days, hours, minutes, seconds = string.match( 2.136 + str, 2.137 + "^(%-?[0-9]+) years (%-?[0-9]+) months (%-?[0-9]+) days (%-?[0-9]+) hours (%-?[0-9]+) minutes (%-?[0-9]+) seconds$" 2.138 + ) 2.139 + return interval:new{ 2.140 + years = tonumber(years), 2.141 + months = tonumber(months), 2.142 + days = tonumber(days), 2.143 + hours = tonumber(hours), 2.144 + minutes = tonumber(minutes), 2.145 + seconds = tonumber(seconds) 2.146 + } 2.147 + end 2.148 +end 2.149 +--//-- 2.150 + 2.151 +function interval:__tostring() 2.152 + if self.invalid then 2.153 + return "invalid_interval" 2.154 + else 2.155 + return string.format( 2.156 + "%i years %i months %i days %i hours %i minutes %i seconds", 2.157 + self.years, self.months, self.days, self.hours, self.minutes, self.seconds 2.158 + ) 2.159 + end 2.160 +end 2.161 + 2.162 +function interval.__eq(value1, value2) 2.163 + if value1.invalid or value2.invalid then 2.164 + return false 2.165 + else 2.166 + return ( 2.167 + value1.years == value2.years and 2.168 + value1.months == value2.months and 2.169 + value1.days == value2.days and 2.170 + value1.hms_seconds == value2.hms_seconds 2.171 + ) 2.172 + end 2.173 +end 2.174 + 2.175 +function interval:__unm() 2.176 + return self.new{ 2.177 + years = -self.years, months = -self.months, days = -self.days, 2.178 + hours = -self.hours, minutes = -self.minutes, seconds = -self.seconds 2.179 + } 2.180 +end 2.181 + 2.182 + 2.183 + 2.184 return _M
3.1 --- a/libraries/mondelefant/mondelefant_atom_connector.lua Sun Nov 04 04:54:54 2012 +0100 3.2 +++ b/libraries/mondelefant/mondelefant_atom_connector.lua Sun Nov 04 04:55:22 2012 +0100 3.3 @@ -75,6 +75,24 @@ 3.4 return conn:quote_string(tostring(value)) .. "::time" 3.5 end 3.6 3.7 +input_converters[atom.interval] = function(conn, value) 3.8 + return ( 3.9 + conn:quote_string( 3.10 + table.concat( 3.11 + { 3.12 + tostring(value.years), "years", 3.13 + tostring(value.months), "months", 3.14 + tostring(value.days), "days", 3.15 + tostring(value.hours), "hours", 3.16 + tostring(value.minutes), "minutes", 3.17 + tostring(value.seconds), "seconds" 3.18 + }, 3.19 + " " 3.20 + ) 3.21 + ) .. "::interval" 3.22 + ) 3.23 +end 3.24 + 3.25 3.26 output_converters = setmetatable({}, { __mode = "k" }) 3.27 3.28 @@ -129,6 +147,59 @@ 3.29 output_converters.time = time_loader_func 3.30 output_converters.timetz = time_loader_func 3.31 3.32 +output_converters.interval = function(str) 3.33 + local years, months, days, hours, minutes, seconds = 0, 0, 0, 0, 0, 0 3.34 + local any_match = false 3.35 + for amount, unit in string.gmatch(str, "(%-?[0-9]+)%s*([A-Za-z]+)") do 3.36 + local unit = string.lower(unit) 3.37 + if string.match(unit, "^y") then 3.38 + years = years + tonumber(amount) 3.39 + any_match = true 3.40 + elseif string.match(unit, "^mo") then 3.41 + months = months + tonumber(amount) 3.42 + any_match = true 3.43 + elseif string.match(unit, "^d") then 3.44 + days = days + tonumber(amount) 3.45 + any_match = true 3.46 + elseif string.match(unit, "^h") then 3.47 + hours = hours + tonumber(amount) 3.48 + any_match = true 3.49 + elseif string.match(unit, "^mi") then 3.50 + minutes = minutes + tonumber(amount) 3.51 + any_match = true 3.52 + elseif string.match(unit, "^s") then 3.53 + seconds = seconds + tonumber(amount) 3.54 + any_match = true 3.55 + else 3.56 + return atom.interval.invalid 3.57 + end 3.58 + end 3.59 + local sign, h, m, s = string.match(str, "(%-?)([0-9]+):([0-9]+):([0-9]+)") 3.60 + if h then 3.61 + if sign == "-" then 3.62 + hours = hours - tonumber(h) 3.63 + minutes = minutes - tonumber(m) 3.64 + seconds = seconds - tonumber(s) 3.65 + else 3.66 + hours = hours + tonumber(h) 3.67 + minutes = minutes + tonumber(m) 3.68 + seconds = seconds + tonumber(s) 3.69 + end 3.70 + any_match = true 3.71 + end 3.72 + if not any_match then 3.73 + return atom.interval.invalid 3.74 + end 3.75 + if string.match(str, "%sago%s*$") then 3.76 + years, months, days = -years, -months, -days 3.77 + hours, minutes, seconds = -hours, -minutes, -seconds 3.78 + end 3.79 + return atom.interval:new{ 3.80 + years = years, months = months, days = days, 3.81 + hours = hours, minutes = minutes, seconds = seconds 3.82 + } 3.83 +end 3.84 + 3.85 mondelefant.postgresql_connection_prototype.type_mappings = { 3.86 int8 = atom.integer, 3.87 int4 = atom.integer,