summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-07-24 15:01:59 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-07-24 15:01:59 -0300
commit0eb6aa4013051c8c0148c09d8c85ee7cbdc96f42 (patch)
treedcf79de5e0efc790142ed7e2fd418813c70d0c60
parent7f5c31cdcac5388b3c48a26112dfb6d2cadb7321 (diff)
downloadlua-0eb6aa4013051c8c0148c09d8c85ee7cbdc96f42.tar.gz
lua-0eb6aa4013051c8c0148c09d8c85ee7cbdc96f42.tar.bz2
lua-0eb6aa4013051c8c0148c09d8c85ee7cbdc96f42.zip
Some improvements in date/time functions
- Range in date table extended to full 32 bits. - Easier support for times represented as floats. - Added more tests.
-rw-r--r--loslib.c76
-rw-r--r--testes/files.lua65
2 files changed, 100 insertions, 41 deletions
diff --git a/loslib.c b/loslib.c
index 8809e5ea..7812d29b 100644
--- a/loslib.c
+++ b/loslib.c
@@ -58,18 +58,20 @@
58** =================================================================== 58** ===================================================================
59*/ 59*/
60 60
61#if !defined(l_time_t) /* { */
62/* 61/*
63** type to represent time_t in Lua 62** type to represent time_t in Lua
64*/ 63*/
64#if !defined(LUA_NUMTIME) /* { */
65
65#define l_timet lua_Integer 66#define l_timet lua_Integer
66#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) 67#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
68#define l_gettime(L,arg) luaL_checkinteger(L, arg)
67 69
68static time_t l_checktime (lua_State *L, int arg) { 70#else /* }{ */
69 lua_Integer t = luaL_checkinteger(L, arg); 71
70 luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); 72#define l_timet lua_Number
71 return (time_t)t; 73#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t))
72} 74#define l_gettime(L,arg) luaL_checknumber(L, arg)
73 75
74#endif /* } */ 76#endif /* } */
75 77
@@ -193,11 +195,25 @@ static int os_clock (lua_State *L) {
193** ======================================================= 195** =======================================================
194*/ 196*/
195 197
196static void setfield (lua_State *L, const char *key, int value) { 198/*
197 lua_pushinteger(L, value); 199** About the overflow check: an overflow cannot occurr when time
200** is represented by a lua_Integer, because either lua_Integer is
201** large enough to represent all int fields or it is not large enough
202** to represent a time that cause a field to overflow. However, if
203** times are represented as doubles and lua_Integer is int, then the
204** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900
205** to compute the year.
206*/
207static void setfield (lua_State *L, const char *key, int value, int delta) {
208 #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
209 if (value > LUA_MAXINTEGER - delta)
210 luaL_error(L, "field '%s' is out-of-bound", key);
211 #endif
212 lua_pushinteger(L, (lua_Integer)value + delta);
198 lua_setfield(L, -2, key); 213 lua_setfield(L, -2, key);
199} 214}
200 215
216
201static void setboolfield (lua_State *L, const char *key, int value) { 217static void setboolfield (lua_State *L, const char *key, int value) {
202 if (value < 0) /* undefined? */ 218 if (value < 0) /* undefined? */
203 return; /* does not set field */ 219 return; /* does not set field */
@@ -210,14 +226,14 @@ static void setboolfield (lua_State *L, const char *key, int value) {
210** Set all fields from structure 'tm' in the table on top of the stack 226** Set all fields from structure 'tm' in the table on top of the stack
211*/ 227*/
212static void setallfields (lua_State *L, struct tm *stm) { 228static void setallfields (lua_State *L, struct tm *stm) {
213 setfield(L, "sec", stm->tm_sec); 229 setfield(L, "year", stm->tm_year, 1900);
214 setfield(L, "min", stm->tm_min); 230 setfield(L, "month", stm->tm_mon, 1);
215 setfield(L, "hour", stm->tm_hour); 231 setfield(L, "day", stm->tm_mday, 0);
216 setfield(L, "day", stm->tm_mday); 232 setfield(L, "hour", stm->tm_hour, 0);
217 setfield(L, "month", stm->tm_mon + 1); 233 setfield(L, "min", stm->tm_min, 0);
218 setfield(L, "year", stm->tm_year + 1900); 234 setfield(L, "sec", stm->tm_sec, 0);
219 setfield(L, "wday", stm->tm_wday + 1); 235 setfield(L, "yday", stm->tm_yday, 1);
220 setfield(L, "yday", stm->tm_yday + 1); 236 setfield(L, "wday", stm->tm_wday, 1);
221 setboolfield(L, "isdst", stm->tm_isdst); 237 setboolfield(L, "isdst", stm->tm_isdst);
222} 238}
223 239
@@ -230,11 +246,6 @@ static int getboolfield (lua_State *L, const char *key) {
230} 246}
231 247
232 248
233/* maximum value for date fields (to avoid arithmetic overflows with 'int') */
234#if !defined(L_MAXDATEFIELD)
235#define L_MAXDATEFIELD (INT_MAX / 2)
236#endif
237
238static int getfield (lua_State *L, const char *key, int d, int delta) { 249static int getfield (lua_State *L, const char *key, int d, int delta) {
239 int isnum; 250 int isnum;
240 int t = lua_getfield(L, -1, key); /* get field and its type */ 251 int t = lua_getfield(L, -1, key); /* get field and its type */
@@ -247,7 +258,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
247 res = d; 258 res = d;
248 } 259 }
249 else { 260 else {
250 if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) 261 /* unsigned avoids overflow when lua_Integer has 32 bits */
262 if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
263 : (lua_Integer)INT_MIN + delta <= res))
251 return luaL_error(L, "field '%s' is out-of-bound", key); 264 return luaL_error(L, "field '%s' is out-of-bound", key);
252 res -= delta; 265 res -= delta;
253 } 266 }
@@ -275,6 +288,13 @@ static const char *checkoption (lua_State *L, const char *conv,
275} 288}
276 289
277 290
291static time_t l_checktime (lua_State *L, int arg) {
292 l_timet t = l_gettime(L, arg);
293 luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
294 return (time_t)t;
295}
296
297
278/* maximum size for an individual 'strftime' item */ 298/* maximum size for an individual 'strftime' item */
279#define SIZETIMEFMT 250 299#define SIZETIMEFMT 250
280 300
@@ -293,7 +313,7 @@ static int os_date (lua_State *L) {
293 stm = l_localtime(&t, &tmr); 313 stm = l_localtime(&t, &tmr);
294 if (stm == NULL) /* invalid date? */ 314 if (stm == NULL) /* invalid date? */
295 return luaL_error(L, 315 return luaL_error(L,
296 "time result cannot be represented in this installation"); 316 "date result cannot be represented in this installation");
297 if (strcmp(s, "*t") == 0) { 317 if (strcmp(s, "*t") == 0) {
298 lua_createtable(L, 0, 9); /* 9 = number of fields */ 318 lua_createtable(L, 0, 9); /* 9 = number of fields */
299 setallfields(L, stm); 319 setallfields(L, stm);
@@ -329,12 +349,12 @@ static int os_time (lua_State *L) {
329 struct tm ts; 349 struct tm ts;
330 luaL_checktype(L, 1, LUA_TTABLE); 350 luaL_checktype(L, 1, LUA_TTABLE);
331 lua_settop(L, 1); /* make sure table is at the top */ 351 lua_settop(L, 1); /* make sure table is at the top */
332 ts.tm_sec = getfield(L, "sec", 0, 0);
333 ts.tm_min = getfield(L, "min", 0, 0);
334 ts.tm_hour = getfield(L, "hour", 12, 0);
335 ts.tm_mday = getfield(L, "day", -1, 0);
336 ts.tm_mon = getfield(L, "month", -1, 1);
337 ts.tm_year = getfield(L, "year", -1, 1900); 352 ts.tm_year = getfield(L, "year", -1, 1900);
353 ts.tm_mon = getfield(L, "month", -1, 1);
354 ts.tm_mday = getfield(L, "day", -1, 0);
355 ts.tm_hour = getfield(L, "hour", 12, 0);
356 ts.tm_min = getfield(L, "min", 0, 0);
357 ts.tm_sec = getfield(L, "sec", 0, 0);
338 ts.tm_isdst = getboolfield(L, "isdst"); 358 ts.tm_isdst = getboolfield(L, "isdst");
339 t = mktime(&ts); 359 t = mktime(&ts);
340 setallfields(L, &ts); /* update fields with normalized values */ 360 setallfields(L, &ts); /* update fields with normalized values */
diff --git a/testes/files.lua b/testes/files.lua
index c8f23d18..6e7bd9e2 100644
--- a/testes/files.lua
+++ b/testes/files.lua
@@ -775,11 +775,24 @@ assert(os.date(string.rep("%d", 1000), t) ==
775 string.rep(os.date("%d", t), 1000)) 775 string.rep(os.date("%d", t), 1000))
776assert(os.date(string.rep("%", 200)) == string.rep("%", 100)) 776assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
777 777
778local t = os.time() 778local function checkDateTable (t)
779D = os.date("*t", t) 779 _G.D = os.date("*t", t)
780load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and 780 assert(os.time(D) == t)
781 D.hour==%H and D.min==%M and D.sec==%S and 781 load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
782 D.wday==%w+1 and D.yday==%j)]], t))() 782 D.hour==%H and D.min==%M and D.sec==%S and
783 D.wday==%w+1 and D.yday==%j)]], t))()
784 _G.D = nil
785end
786
787checkDateTable(os.time())
788if not _port then
789 -- assume that time_t can represent these values
790 checkDateTable(0)
791 checkDateTable(1)
792 checkDateTable(1000)
793 checkDateTable(0x7fffffff)
794 checkDateTable(0x80000000)
795end
783 796
784checkerr("invalid conversion specifier", os.date, "%") 797checkerr("invalid conversion specifier", os.date, "%")
785checkerr("invalid conversion specifier", os.date, "%9") 798checkerr("invalid conversion specifier", os.date, "%9")
@@ -793,11 +806,24 @@ checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5})
793 806
794checkerr("missing", os.time, {hour = 12}) -- missing date 807checkerr("missing", os.time, {hour = 12}) -- missing date
795 808
809
810if string.packsize("i") == 4 then -- 4-byte ints
811 checkerr("field 'year' is out-of-bound", os.time,
812 {year = -(1 << 31) + 1899, month = 1, day = 1})
813end
814
796if not _port then 815if not _port then
797 -- test Posix-specific modifiers 816 -- test Posix-specific modifiers
798 assert(type(os.date("%Ex")) == 'string') 817 assert(type(os.date("%Ex")) == 'string')
799 assert(type(os.date("%Oy")) == 'string') 818 assert(type(os.date("%Oy")) == 'string')
800 819
820 -- test large dates (assume at least 4-byte ints and time_t)
821 local t0 = os.time{year = 1970, month = 1, day = 0}
822 local t1 = os.time{year = 1970, month = 1, day = 0, sec = (1 << 31) - 1}
823 assert(t1 - t0 == (1 << 31) - 1)
824 t0 = os.time{year = 1970, month = 1, day = 1}
825 t1 = os.time{year = 1970, month = 1, day = 1, sec = -(1 << 31)}
826 assert(t1 - t0 == -(1 << 31))
801 827
802 -- test out-of-range dates (at least for Unix) 828 -- test out-of-range dates (at least for Unix)
803 if maxint >= 2^62 then -- cannot do these tests in Small Lua 829 if maxint >= 2^62 then -- cannot do these tests in Small Lua
@@ -812,25 +838,37 @@ if not _port then
812 -- time_t has 8 bytes; an int year cannot represent a huge time 838 -- time_t has 8 bytes; an int year cannot represent a huge time
813 print(" 8-byte time_t") 839 print(" 8-byte time_t")
814 checkerr("cannot be represented", os.date, "%Y", 2^60) 840 checkerr("cannot be represented", os.date, "%Y", 2^60)
815 -- it should have no problems with year 4000 841
816 assert(tonumber(os.time{year=4000, month=1, day=1})) 842 -- this is the maximum year
843 assert(tonumber(os.time
844 {year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=59}))
845
846 -- this is too much
847 checkerr("represented", os.time,
848 {year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=60})
817 end 849 end
850
851 -- internal 'int' fields cannot hold these values
852 checkerr("field 'day' is out-of-bound", os.time,
853 {year = 0, month = 1, day = 2^32})
854
855 checkerr("field 'month' is out-of-bound", os.time,
856 {year = 0, month = -((1 << 31) + 1), day = 1})
857
858 checkerr("field 'year' is out-of-bound", os.time,
859 {year = (1 << 31) + 1900, month = 1, day = 1})
860
818 else -- 8-byte ints 861 else -- 8-byte ints
819 -- assume time_t has 8 bytes too 862 -- assume time_t has 8 bytes too
820 print(" 8-byte time_t") 863 print(" 8-byte time_t")
821 assert(tonumber(os.date("%Y", 2^60))) 864 assert(tonumber(os.date("%Y", 2^60)))
865
822 -- but still cannot represent a huge year 866 -- but still cannot represent a huge year
823 checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1}) 867 checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1})
824 end 868 end
825 end 869 end
826end 870end
827 871
828
829D = os.date("!*t", t)
830load(os.date([[!assert(D.year==%Y and D.month==%m and D.day==%d and
831 D.hour==%H and D.min==%M and D.sec==%S and
832 D.wday==%w+1 and D.yday==%j)]], t))()
833
834do 872do
835 local D = os.date("*t") 873 local D = os.date("*t")
836 local t = os.time(D) 874 local t = os.time(D)
@@ -844,6 +882,7 @@ do
844 assert(t == t1) -- if isdst is absent uses correct default 882 assert(t == t1) -- if isdst is absent uses correct default
845end 883end
846 884
885local D = os.date("*t")
847t = os.time(D) 886t = os.time(D)
848D.year = D.year-1; 887D.year = D.year-1;
849local t1 = os.time(D) 888local t1 = os.time(D)