2

As a result of the Year 2038 problem(https://en.wikipedia.org/wiki/Year_2038_problem), we get nil after calling os.time({year=2039, month=1, day=1, hour=0, sec=1}) on a 32bit machine.How to make it compatible in the lua layer,and get result like running on a 64bit machine? Is it prosible to write a function like the following? Otherwise, how to achieve it?

local function time32Compatibility(timeTable)
    local kMaxYearIn32Bit = 2037;
    if timeTable and timeTable.year and timeTable.year >= kMaxYearIn32Bit then
        local originalTable = clone(timeTable);
        timeTable.year = kMaxYearIn32Bit;
        local deltaTime = calculateDeltaTime(timeTable,originalTable)
        return os.time(timeTable) + kMaxYearIn32Bit*;
    else
        return os.time(timeTable);
    end
end

How to write calculateDeltaTime()?

  • Shift the year down by `4*N` and than add `N * number of seconds in 4 years` to the result – Egor Skriptunoff Sep 27 '17 at 04:07
  • Good idea! And there is a problem,about leap year. "Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 were not leap years, but the years 1600 and 2000 were." – zhangjiequan Sep 27 '17 at 07:19
  • @youzhiwan - In the range 1970-2038 all years divisible by 4 are leap years. – Egor Skriptunoff Sep 27 '17 at 07:49

1 Answers1

3
local orig_os_time = os.time

function os.time(timeTable)
   if timeTable then
      -- assume that all years divisible by 4 are leap years
      local four_year_ctr = math.floor((timeTable.year - 2000) / 4)
      timeTable.year = timeTable.year - four_year_ctr * 4
      local result = orig_os_time(timeTable) + four_year_ctr * ((365*4+1)*24*60*60)
      timeTable.year = timeTable.year + four_year_ctr * 4
      -- make a correction for non-leap years 2100,2200,2300, 2500,2600,2700,...
      -- subtract ("March 1, 2000" - 12 hours) and divide by 100 "wrong" years
      -- It should work for all time zones from UTC-1200 to UTC+1200
      local centuries = math.floor((result - (951868800 - 12*60*60)) / (25*(365*4+1)*24*60*60))
      local wrong_feb29_ctr = math.floor((centuries * 6 + 7) / 8)
      return result - wrong_feb29_ctr * (24*60*60)
   else
      return orig_os_time()
   end
end

-- Example:
print(os.time{year = 1002017, month = 9, day = 27, hour = 0, min = 0, sec = 0})
-- Will Lua be alive after million years?
-- Will 32-bit Linux systems be alive after 2038?
Egor Skriptunoff
  • 906
  • 1
  • 8
  • 23
  • Thank you very much for your complete code! But I found something wrong with it.I had compare the result of your code with some tool like"https://rimzy.net/tools/php_timestamp_converter.php", your code is right when year < 2100, while it is wrong when year >= 2100 ,month > 2. As I said, we shold think about "except for years that are exactly divisible by 100". I can't find a easy way to realize it. – zhangjiequan Sep 28 '17 at 08:13
  • Because 2100 is not a leap year, and your code has regarded it as a leap year. – zhangjiequan Sep 28 '17 at 08:58
  • @youzhiwan - Answer updated. Please test it for all possible dates starting from 1970. – Egor Skriptunoff Sep 28 '17 at 09:30
  • Thank you! But still something wrong here. When timeTable = {year =2100, month = 3, day =1, hour =0, min =0, sec = 0}, we get wrong_feb29_ctr = 0, and it should be 1. Actually, critical value is troublesome. And I would like to ask what the "magic numbers" mean, like "25*(365*4+1)","((centuries * 6 + 7) / 8)" etc. Would you fix the bug and write more comments to explain the magic numbers? Thanks again. – zhangjiequan Sep 30 '17 at 02:20
  • @youzhiwan - `25*(365*4+1)` is days per 100 years, `f(n) = floor((n * 6 + 7) / 8)` is a function which skips every fourth increment of `n`: for n = 1, 2, 3, 4, 5,... values of f(n) are: 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 9,... – Egor Skriptunoff Sep 30 '17 at 08:56
  • @youzhiwan - I can't reproduce the bug. Assuming your timezone is `GMT+8` (China) you should have your `result` variable value to be `4107600000` when timeTable = {year =2100, month = 3, day =1, hour =0, min =0, sec = 0}. Is it correct? – Egor Skriptunoff Sep 30 '17 at 09:35
  • @youzhiwan - Please make sure you are using the latest version of my answer (some constants were changed yesterday). – Egor Skriptunoff Sep 30 '17 at 09:44
  • @EgorSkriptunoff Yes,I get 4107600000. But the right timestamp is 4107513600(I get 4107513600 on a 64bit machine with os.time({year =2100, month = 3, day =1, hour =0, min =0, sec = 0})). – zhangjiequan Sep 30 '17 at 09:50
  • @EgorSkriptunoff I get 4107513600 after using the latest version. – zhangjiequan Sep 30 '17 at 09:53
  • @EgorSkriptunoff I think the problem is solved. Why should we subtract ("March 1, 2000" - 12 hours)? – zhangjiequan Sep 30 '17 at 10:06
  • @youzhiwan - Minus 12 hours is needed to clearly distinguish Feb 28 from Mar 1 despite of +/- 12 hour timezone bias. Unfortunately, my code will not work correctly for timezones beyond +/- 12 hours (for example, Kiribati UTC+13/UTC+14 and Chatham Islands UTC+12:45). – Egor Skriptunoff Sep 30 '17 at 11:04