From 9179e74e369418fa3f36a452da9cf45624894efc Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Sun, 14 Jul 2013 17:28:38 +0000 Subject: [PATCH] Improve compatibility of os.date() function --- src/core/org/luaj/vm2/lib/OsLib.java | 99 +++++++++--------- .../junit/org/luaj/vm2/lib/jse/OsLibTest.java | 16 --- test/lua/luaj3.0-tests.zip | Bin 323012 -> 323417 bytes test/lua/oslib.lua | 17 +++ 4 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/core/org/luaj/vm2/lib/OsLib.java b/src/core/org/luaj/vm2/lib/OsLib.java index 8a2fa7b9..14b7a562 100644 --- a/src/core/org/luaj/vm2/lib/OsLib.java +++ b/src/core/org/luaj/vm2/lib/OsLib.java @@ -24,7 +24,6 @@ package org.luaj.vm2.lib; import java.io.IOException; import java.util.Calendar; import java.util.Date; -import java.util.TimeZone; import org.luaj.vm2.Buffer; import org.luaj.vm2.Globals; @@ -139,8 +138,23 @@ public class OsLib extends TwoArgFunction { case CLOCK: return valueOf(clock()); case DATE: { - String s = args.optjstring(1, null); - double t = args.optdouble(2,-1); + String s = args.optjstring(1, "%c"); + double t = args.isnumber(2)? args.todouble(2): time(null); + if (s.equals("*t")) { + Calendar d = Calendar.getInstance(); + d.setTime(new Date((long)(t*1000))); + LuaTable tbl = LuaValue.tableOf(); + tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR))); + tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1)); + tbl.set("day", LuaValue.valueOf(d.get(Calendar.DAY_OF_MONTH))); + tbl.set("hour", LuaValue.valueOf(d.get(Calendar.HOUR))); + tbl.set("min", LuaValue.valueOf(d.get(Calendar.MINUTE))); + tbl.set("sec", LuaValue.valueOf(d.get(Calendar.SECOND))); + tbl.set("wday", LuaValue.valueOf(d.get(Calendar.DAY_OF_WEEK))); + tbl.set("yday", LuaValue.valueOf(d.get(Calendar.DAY_OF_YEAR))); + tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d))); + return tbl; + } return valueOf( date(s, t==-1? time(null): t) ); } case DIFFTIME: @@ -164,8 +178,9 @@ public class OsLib extends TwoArgFunction { String s = setlocale(args.optjstring(1,null), args.optjstring(2, "all")); return s!=null? valueOf(s): NIL; } - case TIME: + case TIME: { return valueOf(time(args.arg1().isnil()? null: args.checktable(1))); + } case TMPNAME: return valueOf(tmpname()); } @@ -215,13 +230,17 @@ public class OsLib extends TwoArgFunction { * formatted according to the given string format. */ public String date(String format, double time) { + Calendar d = Calendar.getInstance(); + d.setTime(new Date((long)(time*1000))); + if (format.startsWith("!")) { + time -= timeZoneOffset(d); + d.setTime(new Date((long)(time*1000))); + format = format.substring(1); + } byte[] fmt = format.getBytes(); final int n = fmt.length; Buffer result = new Buffer(n); byte c; - Date date = new Date((long)(time*1000)); - Calendar d = Calendar.getInstance(); - d.setTime(date); for ( int i = 0; i < n; ) { switch ( c = fmt[i++ ] ) { case '\n': @@ -234,8 +253,7 @@ public class OsLib extends TwoArgFunction { if (i >= n) break; switch ( c = fmt[i++ ] ) { default: - result.append( (byte)'%' ); - result.append( (byte)c ); + LuaValue.argerror(1, "invalid conversion specifier '%"+c+"'"); break; case '%': result.append( (byte)'%' ); @@ -247,7 +265,6 @@ public class OsLib extends TwoArgFunction { result.append(WeekdayName[d.get(Calendar.DAY_OF_WEEK)-1]); break; case 'b': - case 'h': result.append(MonthNameAbbrev[d.get(Calendar.MONTH)]); break; case 'B': @@ -256,22 +273,9 @@ public class OsLib extends TwoArgFunction { case 'c': result.append(date("%a %b %d %H:%M:%S %Y", time)); break; - case 'C': - result.append(String.valueOf((100000+d.get(Calendar.YEAR))/100).substring(2)); - break; case 'd': result.append(String.valueOf(100+d.get(Calendar.DAY_OF_MONTH)).substring(1)); break; - case 'e': { - final String s = String.valueOf(d.get(Calendar.DAY_OF_MONTH)); - if (s.length() < 2) - result.append((byte)' '); - result.append(s); - break; - } - case 'F': - result.append(date("%Y-%m-%d", time)); - break; case 'H': result.append(String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)).substring(1)); break; @@ -290,30 +294,12 @@ public class OsLib extends TwoArgFunction { case 'M': result.append(String.valueOf(100+d.get(Calendar.MINUTE)).substring(1)); break; - case 'n': - result.append((byte)'\n'); - break; case 'p': result.append(d.get(Calendar.HOUR_OF_DAY) < 12? "AM": "PM"); break; - case 'R': - result.append(date("%H:%M", time)); - break; - case 'r': { - final String s = date("%I:%M:%S", time); - result.append(s); - result.append(d.get(Calendar.HOUR_OF_DAY) < 12? " am": " pm"); - break; - } case 'S': result.append(String.valueOf(100+d.get(Calendar.SECOND)).substring(1)); break; - case 't': - result.append((byte)'\t'); - break; - case 'u': - result.append(String.valueOf((d.get(Calendar.DAY_OF_WEEK)+5)%7+1)); - break; case 'U': result.append(String.valueOf(weekNumber(d, 0))); break; @@ -324,11 +310,9 @@ public class OsLib extends TwoArgFunction { result.append(String.valueOf(weekNumber(d, 1))); break; case 'x': - case 'D': result.append(date("%m/%d/%y", time)); break; case 'X': - case 'T': result.append(date("%H:%M:%S", time)); break; case 'y': @@ -338,12 +322,13 @@ public class OsLib extends TwoArgFunction { result.append(String.valueOf(d.get(Calendar.YEAR))); break; case 'z': { - final int tzo = d.getTimeZone().getRawOffset() / (60 * 1000); - result.append((tzo>0? "+": "") + String.valueOf(tzo)); + final int tzo = timeZoneOffset(d) / 60; + final int a = Math.abs(tzo); + final String h = String.valueOf(100 + a / 60).substring(1); + final String m = String.valueOf(100 + a % 60).substring(1); + result.append((tzo>=0? "+": "-") + h + m); break; } - case 'Z': - break; } } } @@ -368,17 +353,33 @@ public class OsLib extends TwoArgFunction { private int weekNumber(Calendar d, int startDay) { Calendar y0 = beginningOfYear(d); - System.out.println("Time Date(time) " + d.getTime() + " y0 " + y0.getTime()); y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7); if (y0.after(d)) { y0.set(Calendar.YEAR, y0.get(Calendar.YEAR) - 1); y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7); - System.out.println(" -> y0 " + y0.getTime()); } long dt = d.getTime().getTime() - y0.getTime().getTime(); return 1 + (int) (dt / (7L * 24L * 3600L * 1000L)); } + private int timeZoneOffset(Calendar d) { + int localStandarTimeMillis = ( + d.get(Calendar.HOUR_OF_DAY) * 3600 + + d.get(Calendar.MINUTE) * 60 + + d.get(Calendar.SECOND)) * 1000; + return d.getTimeZone().getOffset( + 1, + d.get(Calendar.YEAR), + d.get(Calendar.MONTH), + d.get(Calendar.DAY_OF_MONTH), + d.get(Calendar.DAY_OF_WEEK), + localStandarTimeMillis) / 1000; + } + + private boolean isDaylightSavingsTime(Calendar d) { + return timeZoneOffset(d) != d.getTimeZone().getRawOffset() / 1000; + } + /** * This function is equivalent to the C function system. * It passes command to be executed by an operating system shell. diff --git a/test/junit/org/luaj/vm2/lib/jse/OsLibTest.java b/test/junit/org/luaj/vm2/lib/jse/OsLibTest.java index c45aad2a..d8e19d09 100644 --- a/test/junit/org/luaj/vm2/lib/jse/OsLibTest.java +++ b/test/junit/org/luaj/vm2/lib/jse/OsLibTest.java @@ -25,37 +25,21 @@ public class OsLibTest extends TestCase { public void testStringDate_b() { t("%b", "Aug"); } public void testStringDate_B() { t("%B", "August"); } public void testStringDate_c() { t("%c", "Thu Aug 23 14:55:02 2001"); } - public void testStringDate_C() { t("%C", "20"); } public void testStringDate_d() { t("%d", "23"); } - public void testStringDate_D() { t("%D", "08/23/01"); } - public void testStringDate_e() { t("%e", "23"); } - public void testStringDate_F() { t("%F", "2001-08-23"); } - public void testStringDate_g() { t("%g", "%g"); } // not implemented. - public void testStringDate_G() { t("%G", "%G"); } // not implemented. - public void testStringDate_h() { t("%h", "Aug"); } public void testStringDate_H() { t("%H", "14"); } public void testStringDate_I() { t("%I", "02"); } public void testStringDate_j() { t("%j", "235"); } public void testStringDate_m() { t("%m", "08"); } public void testStringDate_M() { t("%M", "55"); } - public void testStringDate_n() { t("%n", "\n"); } public void testStringDate_p() { t("%p", "PM"); } - public void testStringDate_r() { t("%r", "02:55:02 pm"); } - public void testStringDate_R() { t("%R", "14:55"); } public void testStringDate_S() { t("%S", "02"); } - public void testStringDate_t() { t("%t", "\t"); } - public void testStringDate_T() { t("%T", "14:55:02"); } - public void testStringDate_u() { t("%u", "4"); } public void testStringDate_U() { t("%U", "33"); } - public void testStringDate_V() { t("%V", "%V"); } // not implemented. public void testStringDate_w() { t("%w", "4"); } public void testStringDate_W() { t("%W", "34"); } public void testStringDate_x() { t("%x", "08/23/01"); } public void testStringDate_X() { t("%X", "14:55:02"); } public void testStringDate_y() { t("%y", "01"); } public void testStringDate_Y() { t("%Y", "2001"); } - public void testStringDate_z() { t("%z", "-480"); } - public void testStringDate_Z() { t("%Z", ""); } public void testStringDate_Pct() { t("%%", "%"); } static final double DAY = 24. * 3600.; diff --git a/test/lua/luaj3.0-tests.zip b/test/lua/luaj3.0-tests.zip index 7bd0c5f04b2f1d088a3d8e76145ca745778f3a1b..3760762d1502ed0df033a8fe032f093ef6586212 100644 GIT binary patch delta 11025 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 LGMvo_1H=9Ys@kzX delta 10061 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