how to compare time in human format in lua? - lua

Is there a way to convert human readable time "09:41:43" to some comparable format?
What I want is function timeGreater(time1, time2), satisfied the below assertion
assert(true == timeGreater("09:41:43", "09:00:42"))
assert(false == timeGreater("12:55:43", "19:00:43")))

It seems like a simple string comparison may be sufficient (assuming time is valid):
function timeGreater(a, b) return a > b end
assert(true == timeGreater("09:41:43", "09:00:42"))
assert(false == timeGreater("12:55:43", "19:00:43"))

Converting your time to seconds should work. The code below might work, LUA isn't my strong suit!
function stime(s)
local pattern = "(%d+):(%d+):(%d+)"
local hours, minutes, seconds = string.match(s, pattern)
return (hours*3600)+(minutes*60)+seconds
end
function timeGreater(a, b)
return stime(a) > stime(b)
end

Related

How to check only numbers in value - Lua

I want to make sure players can't put letters or symbols in the value, how do I check if it is a number only?
function seed1()
ESX.UI.Menu.CloseAll()
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'amountseed1', {
title = 'Shop'
}, function(data, menu)
local amount = tostring(data.value)
if amount == nil then
...
else
[[What should i put here to check its only contain number ?]]
end
end, function(data, menu)
menu.close()
end)
end
I can put something like this, but maybe this isn't a good way to do that:
else
if amount > 0 and amount < 9999 then
...
else
print('Invalid amount or amount higher than 9999')
end
end
Since you only care about the number, there is no need to convert the value to string:
local amount = tostring(data.value)
-- ^^^^^^^^ partially useless
Instead, go for the number right away:
local amount = tonumber(data.value)
if amount == nil then
-- Not a number
else
-- A number
end
In the end, remember that tonumber attempts to convert the value to a number and returns nil in case of a failure.
Simply replace tostring with tonumber. This will turn strings into numbers if possible, and return nil if it can't.
Keep in mind: tonumber won't just take the largest valid prefix of a string, so tonumber("20 foo") will return nil and not 20. It also supports all ways to write number literals in Lua, so tonumber("2.3e2") will return 230 and tonumber("0xff") will return 255.

How can i check if strings are equal without using the eq operator ( == / ~=)

my idea is the convert the 2 strings into byte, subtract and then check if they're 0 using a for loop like this
function match(str1, str2, callback)
local res = string.byte(str1) - string.byte(str2)
for i = 1, res(0) do
spawn(callback)
end
end
but that just doesn't work can anyone write me a code would appreciate...
rawequal(str1, str2) -- Compares two values without calling any metamethods

How to put floating points in LUA [duplicate]

I'd like to format a number to look like 1,234 or 1,234,432 or 123,456,789, you get the idea. I tried doing this as follows:
function reformatint(i)
local length = string.len(i)
for v = 1, math.floor(length/3) do
for k = 1, 3 do
newint = string.sub(mystring, -k*v)
end
newint = ','..newint
end
return newint
end
As you can see, a failed attempt, my problem is that I can't figure out what the error is because the program I am running this in refuses to report an error back to me.
Here's a function that takes negative numbers, and fractional parts into account:
function format_int(number)
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
-- reverse the int-string and append a comma to all blocks of 3 digits
int = int:reverse():gsub("(%d%d%d)", "%1,")
-- reverse the int-string back remove an optional comma and put the
-- optional minus and fractional part back
return minus .. int:reverse():gsub("^,", "") .. fraction
end
assert(format_int(1234) == '1,234')
assert(format_int(1234567) == '1,234,567')
assert(format_int(123456789) == '123,456,789')
assert(format_int(123456789.1234) == '123,456,789.1234')
assert(format_int(-123456789.) == '-123,456,789')
assert(format_int(-123456789.1234) == '-123,456,789.1234')
assert(format_int('-123456789.1234') == '-123,456,789.1234')
print('All tests passed!')
Well, let's take this from the top down. First of all, it's failing because you've got a reference error:
...
for k = 1, 3 do
newint = string.sub(mystring, -k*v) -- What is 'mystring'?
end
...
Most likely you want i to be there, not mystring.
Second, while replacing mystring with i will fix the errors, it still won't work correctly.
> =reformatint(100)
,100
> =reformatint(1)
,000
That's obviously not right. It seems like what you're trying to do is go through the string, and build up the new string with the commas added. But there are a couple of problems...
function reformatint(i)
local length = string.len(i)
for v = 1, math.floor(length/3) do
for k = 1, 3 do -- What is this inner loop for?
newint = string.sub(mystring, -k*v) -- This chops off the end of
-- your string only
end
newint = ','..newint -- This will make your result have a ',' at
-- the beginning, no matter what
end
return newint
end
With some rework, you can get a function that work.
function reformatint(integer)
for i = 1, math.floor((string.len(integer)-1) / 3) do
integer = string.sub(integer, 1, -3*i-i) ..
',' ..
string.sub(integer, -3*i-i+1)
end
return integer
end
The function above seems to work correctly. However, it's fairly convoluted... Might want to make it more readable.
As a side note, a quick google search finds a function that has already been made for this:
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
You can do without loops:
function numWithCommas(n)
return tostring(math.floor(n)):reverse():gsub("(%d%d%d)","%1,")
:gsub(",(%-?)$","%1"):reverse()
end
assert(numWithCommas(100000) == "100,000")
assert(numWithCommas(100) == "100")
assert(numWithCommas(-100000) == "-100,000")
assert(numWithCommas(10000000) == "10,000,000")
assert(numWithCommas(10000000.00) == "10,000,000")
The second gsub is needed to avoid -,100 being generated.
I remember discussing about this in the LÖVE forums ... let me look for it...
Found it!
This will work with positive integers:
function reformatInt(i)
return tostring(i):reverse():gsub("%d%d%d", "%1,"):reverse():gsub("^,", "")
end
On the link above you may read details about implementation.

Timestamp pattern

Let's assume I have the following reminder timestamp
local reminder_timestamp = "2013-12-13T00:00:00+01:00"
And I'm using the below function to return time in UTC
local function makeTimeStamp(dateString)
local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)"
local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin = dateString:match(pattern)
local timestamp = os.time( {year=year, month=month, day=day, hour=hour, min=minute, sec=seconds} )
local offset = 0
if ( tzoffset ) then
if ( tzoffset == "+" or tzoffset == "-" ) then -- we have a timezone!
offset = offsethour * 60 + offsetmin
if ( tzoffset == "-" ) then
offset = offset * -1
end
timestamp = timestamp + offset
end
end
return timestamp
end
What should be the pattern above to match the reminder timestamp I mentioned earlier?
You need to use Lua's string parsing capabilities. Try a few of the techniques mentioned in the following, and if you still have issues, post specifically what is not working:
Question about splitting string and saving in several variables
Question about extracting data from a string, very similar to yours (although problem domain is GPS coordinates instead of date/time)
Question about how to do pattern matching in Lua, several good examples and links to docs
Here is the answer and the function actually works fine
pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)"
reminder_timestamp = "2013-12-23T08:00:00+01:00"
local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin = reminder_timestamp:match(pattern)
Resource: http://www.lua.org/manual/5.1/manual.html#5.4.1

Lua seconds format questions

I have this function:
function SecondsFormat(X)
if X <= 0 then return "" end
local t ={}
local ndays = string.format("%02.f",math.floor(X / 86400))
if tonumber(ndays) > 0 then table.insert(t,ndays.."d ") end
local nHours = string.format("%02.f",math.floor((X/3600) -(ndays*24)))
if tonumber(nHours) > 0 then table.insert(t,nHours.."h ") end
local nMins = string.format("%02.f",math.floor((X/60) - (ndays * 1440) - (nHours*60)))
if tonumber(nMins) > 0 then table.insert(t,nMins.."m ") end
local nSecs = string.format("%02.f", math.fmod(X, 60));
if tonumber(nSecs) > 0 then table.insert(t,nSecs.."s") end
return table.concat(t)
end
I would like to add weeks and months to it but cant get my head around the month part to move on to the week part just because the days in a month aren't always the same so can anyone offer some help?
The second question is, is using a table to store the results the most efficient way of dealing with this given the function will be called every 3 seconds for up to 100 items (in a grid)?
Edit:
function ADownload.ETA(Size,Done,Tranrate) --all in bytes
if Size == nil then return "--" end
if Done == nil then return "--" end
if Tranrate == nil then return "--" end
local RemS = (Size - Done) / Tranrate
local RemS = tonumber(RemS)
if RemS <= 0 or RemS == nil or RemS > 63072000 then return "--" end
local date = os.date("%c",RemS)
if date == nil then return "--" end
local month, day, year, hour, minute, second = date:match("(%d+)/(%d+)/(%d+) (%d+): (%d+):(%d+)")
month = month - 1
day = day - 1
year = year - 70
if tonumber(year) > 0 then
return string.format("%dy %dm %dd %dh %dm %ds", year, month, day, hour, minute, second)
elseif tonumber(month) > 0 then
return string.format("%dm %dd %dh %dm %ds",month, day, hour, minute, second)
elseif tonumber(day) > 0 then
return string.format("%dd %dh %dm %ds",day, hour, minute, second)
elseif tonumber(hour) > 0 then
return string.format("%dh %dm %ds",hour, minute, second)
elseif tonumber(minute) > 0 then
return string.format("%dm %ds",minute, second)
else
return string.format("%ds",second)
end
end
I merged the function into the main function as I figured it would probably be quicker but I now have two questions:
1: I had to add
if date == nil then return "--" end
because it errors occasionally with date:match trying to compare with "nil" however os.date mentions nothing in the literature about returning nil as its a string or a table so although the extra line of code fixes the issue I'm wondering why that behaviour occurs as I'm sure I caught all the non events in the previous returns?
2: Sometimes I see functions written like myfunction(...) and I'm sure that just does away with the arguments and if so is there a one line of code that could do away with the first 3 "if" statements?
You can use the os.date function to get date values in a useable format. The '*t' formating parameter makes the returned date into a table instead of a string.
local t = os.date('*t')
print(t.year, t.month, t.day, t.hour, t.min, t.sec)
print(t.wday, t.yday)
os.data uses the current time by default, you can pass it an explicit time if you want (see the os.data docs for more info on this)
local t = os.date('*t', x)
As for table performance, I wouldn't worry about that. Not only is your function not called all that often, but table handling is much cheaper than other things you might be doing (calling os.date, lots of string formatting, etc)
Why not let Lua's os library do the hard work for you?
There is probably an easier (read: better) way to figure out the difference to 01/01/70, but here is a quick idea of how you could use it:
function SecondsFormat(X)
if X <= 0 then return "" end
local date = os.date("%c", X) -- will give something like "01/03/70 03:40:00"
local inPattern = "(%d+)/(%d+)/(%d+) (%d+):(%d+):(%d+)"
local outPattern = "%dy %dm %dd %dh %dm %ds"
local month, day, year, hour, minute, second = date:match(inPattern)
month = month - 1
day = day - 1
year = year - 70
return string.format(outPattern, year, month, day, hour, minute, second)
end
I think that this should also be a lot quicker than constructing the table and calling string.format multiple times - but you'd have to profile that.
EDIT: I ran a quick test with two functions that concatenate "abc", "def" and "ghi" using both methods. Inserting those strings into a table an concatenating took 14 seconds (for several million runs of course) and using a single string.format() took 6 seconds. This does not take into account, that your code calls string.format() anyway (multiple times) - nor the difference between you figuring out the values by division and I by pattern matching. Pattern matching is certainly slower, but I doubt that it outweighs the gains from not having a table - and it's certainly convenient to be able to leverage os.time(). The fastest way would probably be figuring out the month and day manually and then using a single string.format(). But again - you'd have to profile that.
EDIT2: missingno has a good point with using the "*t" option with os.date to give you the values separately in the first place. Again, this depends on whether you want to have a table for convenience vs. a string for storage or whatever reasons. Combining "*t" and a single string.format:
function SecondsFormat(X)
if X <= 0 then return "" end
local date = os.date("*t", X) -- will give you a table
local outPattern = "%dy %dm %dd %dh %dm %ds"
date.month = date.month - 1
date.day = date.day - 1
date.year = date.year - 70
return string.format(outPattern, date.year, date.month, date.day, date.hour, date.min, date.sec)
end

Resources