I'm pretty new to WoW API coding myself, but if I understand correctly, it can be problematic to try to use the "elapsed" argument that way. WoW's event dispatcher may give you the elapsed time since the last OnUpdate cycle *started*, but since you don't know how long each cycle *takes*, you can't just tally it up every time you get one and treat that as an accurate measure of time. The more OnUpdate code is running (anywhere, from any addon), the more extra processing time will be taken up per frame, and the more skew you'll get by summing that "elapsed" argument.
If you're tracking very short-term buffs that might not be noticeable, but I suspect if you ran the above on a several-minute buff you'd see your buff disappear while the timer was still ticking down.
So if you want accuracy, I think you still need a GetTime() call -- but don't worry about that too much, built-in functions are implemented in C and are pretty fast. I'm not even sure that creating local references to built-in functions is any faster (local pointers to Lua-defined global functions certainly is); I'm actually working on a utility to profile that sort of thing right now.
You are wrong on several accounts. "elapsed" gives you exactly the time that occured since the previous frame. There is no scew. Imagine the main loop to do the following :
local prev_time = GetTime()
while true do
local time = GetTime()
for each visible frame do
BroadcastEvent("OnUpdate", time - prev_time)
prev_time = time
There is no scew (apart from the precision of double precision floating point numbers, but it's way too small to care about it.
Finally, the call to C is fast, but you don't just do that, you also do two GETGLOBAL, which is also slow. Both operations are fast indeed, but not THAT fast compared to accessing a parameter. The local declaration is important if you want fast code (and you want fast code in a OnUpdate handler).
Finally, declaring local variables cost a NIL OPCODE, which is tremendously fast compared to the rest, and I really don't think it's worth taking that out. And really, it does not compare at all with the cost of a GetTime()
Finally, I strongly suggest to use "luac -l" to see what your code and mine becomes, and you can read the code of lua to get a feeling of the cost of each opcodes (Spoiler: GETGLOBAL is expensive).