Last active
November 13, 2017 02:19
-
-
Save xjdrew/ba8aabb7a9c4a1b41957f6ebc5c89ad9 to your computer and use it in GitHub Desktop.
pure lua version of os.date("!*t", time)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- port from golang: https://golang.org/src/time/time.go | |
-- | |
local secondsPerMinute = 60 | |
local secondsPerHour = 60 * 60 | |
local secondsPerDay = 24 * secondsPerHour | |
local secondsPerWeek = 7 * secondsPerDay | |
local daysPer400Years = 365*400 + 97 | |
local daysPer100Years = 365*100 + 24 | |
local daysPer4Years = 365*4 + 1 | |
local unixBase = (1969*365 + 1969//4 - 1969//100 + 1969//400) * secondsPerDay | |
-- daysBefore[m] counts the number of days in a non-leap year | |
-- before month m begins. There is an entry for m=12, counting | |
-- the number of days before January of next year (365). | |
local daysBefore = { | |
0, | |
31, | |
31 + 28, | |
31 + 28 + 31, | |
31 + 28 + 31 + 30, | |
31 + 28 + 31 + 30 + 31, | |
31 + 28 + 31 + 30 + 31 + 30, | |
31 + 28 + 31 + 30 + 31 + 30 + 31, | |
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, | |
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, | |
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, | |
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, | |
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, | |
} | |
local January = 1 | |
local February = 2 | |
local March = 3 | |
local April = 4 | |
local May = 5 | |
local June = 6 | |
local July = 7 | |
local August = 8 | |
local September = 9 | |
local October = 10 | |
local November = 11 | |
local December = 12 | |
local Sunday = 1 | |
local Monday = 2 | |
local Tuesday = 3 | |
local Wednesday = 4 | |
local Thursday = 5 | |
local Friday = 6 | |
local Saturday = 7 | |
-- absTimestamp returns the absolute second from {year=1, month=1, day=1, hour=0, mintue=0, second=0} | |
local function absTimestamp(ts) | |
return ts + unixBase | |
end | |
local function isLeap(year) | |
return year%4 == 0 and (year%100 ~= 0 or year%400 == 0) | |
end | |
local function absWeekday(abs) | |
-- January 1 of the absolute year, like January 1 of 2001, was a Monday. | |
local sec = (abs+1*secondsPerDay) % secondsPerWeek | |
return (sec // secondsPerDay) + 1 | |
end | |
-- convert timestamp to year, month, day, yday | |
local function absDate(abs) | |
local day = abs // secondsPerDay | |
local n, y | |
-- Account for 400 year cycles. | |
n = day // daysPer400Years | |
y = 400 * n | |
day = day - daysPer400Years * n | |
-- Cut off 100-year cycles. | |
-- The last cycle has one extra leap year, so on the last day | |
-- of that year, day / daysPer100Years will be 4 instead of 3. | |
-- Cut it back down to 3 by subtracting n>>2. | |
n = day // daysPer100Years | |
n = n - (n >> 2) | |
y = y + 100 *n | |
day = day - daysPer100Years * n | |
-- Cut off 4-year cycles. | |
-- The last cycle has a missing leap year, which does not | |
-- affect the computation. | |
n = day // daysPer4Years | |
y = y + 4 * n | |
day = day - daysPer4Years * n | |
-- Cut off years within a 4-year cycle. | |
-- The last year is a leap year, so on the last day of that year, | |
-- day / 365 will be 4 instead of 3. Cut it back down to 3 | |
-- by subtracting n>>2. | |
n = day // 365 | |
n = n - (n >> 2) | |
y = y + n | |
day = day - 365 * n | |
local year = y + 1 | |
local yday = day + 1 | |
if isLeap(year) then | |
-- Leap year | |
if day > 31+29-1 then | |
-- After leap day; pretend it wasn't there. | |
day = day - 1 | |
elseif day == 31+29-1 then | |
return year, February, 29, yday | |
end | |
end | |
-- Estimate month on assumption that every month has 31 days. | |
-- The estimate may be too low by at most one month, so adjust. | |
local month = (day // 31) + 1 | |
local monthDayEnd = daysBefore[month + 1] | |
local monthDayBegin | |
if day >= monthDayEnd then | |
month = month + 1 | |
monthDayBegin = monthDayEnd | |
else | |
monthDayBegin = daysBefore[month] | |
end | |
day = day - monthDayBegin + 1 | |
return year, month, day, yday | |
end | |
local M = {} | |
M.TZ = 8 | |
function M.localdate(sec, tz, t) | |
local tz = tz or M.TZ | |
return M.date(sec + tz*secondsPerHour, t) | |
end | |
function M.date(sec, t) | |
local abs = absTimestamp(sec) | |
local t = t or {} | |
t.year, t.month, t.day, t.yday = absDate(abs) | |
t.wday = absWeekday(abs) | |
local seconds = abs % secondsPerDay | |
t.hour = seconds // secondsPerHour | |
seconds = seconds - (t.hour * secondsPerHour) | |
t.min = seconds // secondsPerMinute | |
t.sec = seconds - (t.min * secondsPerMinute) | |
return t | |
end | |
local tmp = {} | |
function M.format(sec, utc) | |
if utc then | |
M.date(sec, tmp) | |
else | |
M.localdate(sec, nil, tmp) | |
end | |
return string.format("%4d-%02d-%02d %02d:%02d:%02d", | |
tmp.year, tmp.month, tmp.day, tmp.hour, tmp.min, tmp.sec) | |
end | |
-- test functions | |
-- | |
function M.run_random_test() | |
for i=1, 100 do | |
local ts = math.random(0, 2^31) | |
local t1 = os.date("!*t", ts) | |
local t2 = M.date(ts) | |
for k,v in pairs(t1) do | |
if k ~= "isdst" then | |
assert(v == t2[k], k) | |
end | |
end | |
end | |
end | |
function M.run_full_test() | |
local total = 2^31 | |
for i=1, total do | |
local t1 = os.date("!*t", i) | |
local t2 = M.date(i, tmp) | |
for k,v in pairs(t1) do | |
if k ~= "isdst" then | |
assert(v == t2[k], k) | |
end | |
end | |
if i % 100000 == 0 then | |
print("------------------------:", total, i) | |
end | |
end | |
end | |
function M.run_profile_test(times) | |
local times = times and times > 0 or 1000000 | |
local from = math.random(2^31 - times) | |
local to = from + times | |
local t1 = os.clock() | |
for ts = from, to do | |
os.date("!*t", ts) | |
end | |
local t = {} | |
local t2 = os.clock() | |
for ts = from, to do | |
M.date(ts, t) | |
end | |
local t3 = os.clock() | |
print(string.format("%d times call %s cost %s seconds", times, "os.date", t2-t1)) | |
print(string.format("%d times call %s cost %s seconds", times, "M.date", t3-t2)) | |
end | |
return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
我是@xjdrew的脑残粉