Implement most '%' formatting types for os.date()

This commit is contained in:
James Roseborough
2013-07-14 15:31:32 +00:00
parent 07fb564edd
commit db1c7a17b2
4 changed files with 250 additions and 16 deletions

View File

@@ -458,7 +458,7 @@ A richer version for use by <em>JsePlatform</em> is :
src/jse/org/luaj/vm2/lib/jse/JseOsLib.java src/jse/org/luaj/vm2/lib/jse/JseOsLib.java
</pre> </pre>
Time is a represented as number of milliseconds since the epoch, Time is a represented as number of seconds since the epoch,
and most time and date formatting, locales, and other features and most time and date formatting, locales, and other features
are not implemented. are not implemented.
@@ -887,6 +887,7 @@ Files are no longer hosted at LuaForge.
<tr valign="top"><td>&nbsp;&nbsp;<b>3.0-beta2</b></td><td><ul> <tr valign="top"><td>&nbsp;&nbsp;<b>3.0-beta2</b></td><td><ul>
<li>LuaValue.checkfunction() now returns LuaFunction.</li> <li>LuaValue.checkfunction() now returns LuaFunction.</li>
<li>Fix os.time() to return a number of seconds.</li> <li>Fix os.time() to return a number of seconds.</li>
<li>Implement most '%' formatting types for os.date().</li>
</ul></td></tr> </ul></td></tr>
</table></td></tr></table> </table></td></tr></table>
@@ -901,6 +902,6 @@ Files are no longer hosted at LuaForge.
<li>negative zero is treated as identical to integer value zero throughout luaj <li>negative zero is treated as identical to integer value zero throughout luaj
<li>lua compiled into java bytecode using luajc cannot use string.dump() or xpcall() <li>lua compiled into java bytecode using luajc cannot use string.dump() or xpcall()
<li>number formatting with string.format() is not supported <li>number formatting with string.format() is not supported
<li>date formatting with os.date() is not supported <li>os.time(), and os.date() not completely supported
</ul> </ul>

View File

@@ -22,7 +22,11 @@
package org.luaj.vm2.lib; package org.luaj.vm2.lib;
import java.io.IOException; 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; import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
@@ -137,7 +141,7 @@ public class OsLib extends TwoArgFunction {
case DATE: { case DATE: {
String s = args.optjstring(1, null); String s = args.optjstring(1, null);
double t = args.optdouble(2,-1); double t = args.optdouble(2,-1);
return valueOf( date(s, t==-1? System.currentTimeMillis()/1000.: t) ); return valueOf( date(s, t==-1? time(null): t) );
} }
case DIFFTIME: case DIFFTIME:
return valueOf(difftime(args.checkdouble(1),args.checkdouble(2))); return valueOf(difftime(args.checkdouble(1),args.checkdouble(2)));
@@ -174,7 +178,8 @@ public class OsLib extends TwoArgFunction {
/** /**
* @return an approximation of the amount in seconds of CPU time used by * @return an approximation of the amount in seconds of CPU time used by
* the program. * the program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded.
*/ */
protected double clock() { protected double clock() {
return (System.currentTimeMillis()-t0) / 1000.; return (System.currentTimeMillis()-t0) / 1000.;
@@ -196,15 +201,9 @@ public class OsLib extends TwoArgFunction {
* (see the os.time function for a description of this value). * (see the os.time function for a description of this value).
* Otherwise, date formats the current time. * Otherwise, date formats the current time.
* *
* If format starts with '!', then the date is formatted in Coordinated * Date returns the date as a string,
* Universal Time. After this optional character, if format is the string * formatted according to the same rules as ANSII strftime, but without
* "*t", then date returns a table with the following fields: year * support for %g, %G, or %V.
* (four digits), month (1--12), day (1--31), hour (0--23), min (0--59),
* sec (0--61), wday (weekday, Sunday is 1), yday (day of the year),
* and isdst (daylight saving flag, a boolean).
*
* If format is not "*t", then date returns the date as a string,
* formatted according to the same rules as the C function strftime.
* *
* When called without arguments, date returns a reasonable date and * When called without arguments, date returns a reasonable date and
* time representation that depends on the host system and on the * time representation that depends on the host system and on the
@@ -215,10 +214,171 @@ public class OsLib extends TwoArgFunction {
* @return a LString or a LTable containing date and time, * @return a LString or a LTable containing date and time,
* formatted according to the given string format. * formatted according to the given string format.
*/ */
protected String date(String format, double time) { public String date(String format, double time) {
return new java.util.Date((long)(time*1000)).toString(); 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':
result.append( "\n" );
break;
default:
result.append( c );
break;
case '%':
if (i >= n) break;
switch ( c = fmt[i++ ] ) {
default:
result.append( (byte)'%' );
result.append( (byte)c );
break;
case '%':
result.append( (byte)'%' );
break;
case 'a':
result.append(WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
break;
case 'A':
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':
result.append(MonthName[d.get(Calendar.MONTH)]);
break;
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;
case 'I':
result.append(String.valueOf(100+(d.get(Calendar.HOUR_OF_DAY)%12)).substring(1));
break;
case 'j': { // day of year.
Calendar y0 = beginningOfYear(d);
int dayOfYear = (int) ((d.getTime().getTime() - y0.getTime().getTime()) / (24 * 3600L * 1000L));
result.append(String.valueOf(1001+dayOfYear).substring(1));
break;
}
case 'm':
result.append(String.valueOf(101+d.get(Calendar.MONTH)).substring(1));
break;
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;
case 'w':
result.append(String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
break;
case 'W':
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':
result.append(String.valueOf(d.get(Calendar.YEAR)).substring(2));
break;
case 'Y':
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));
break;
}
case 'Z':
break;
}
}
}
return result.tojstring();
} }
private static final String[] WeekdayNameAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
private Calendar beginningOfYear(Calendar d) {
Calendar y0 = Calendar.getInstance();
y0.setTime(d.getTime());
y0.set(Calendar.MONTH, 0);
y0.set(Calendar.DAY_OF_MONTH, 1);
y0.set(Calendar.HOUR_OF_DAY, 0);
y0.set(Calendar.MINUTE, 0);
y0.set(Calendar.SECOND, 0);
return y0;
}
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));
}
/** /**
* This function is equivalent to the C function system. * This function is equivalent to the C function system.
* It passes command to be executed by an operating system shell. * It passes command to be executed by an operating system shell.
@@ -305,7 +465,7 @@ public class OsLib extends TwoArgFunction {
* @return long value for the time * @return long value for the time
*/ */
protected double time(LuaTable table) { protected double time(LuaTable table) {
return System.currentTimeMillis() / 1000.0; return (new java.util.Date()).getTime() / 1000.;
} }
/** /**

View File

@@ -35,6 +35,7 @@ import org.luaj.vm2.compiler.SimpleTests;
import org.luaj.vm2.lib.jse.LuaJavaCoercionTest; import org.luaj.vm2.lib.jse.LuaJavaCoercionTest;
import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest; import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest;
import org.luaj.vm2.lib.jse.LuajavaClassMembersTest; import org.luaj.vm2.lib.jse.LuajavaClassMembersTest;
import org.luaj.vm2.lib.jse.OsLibTest;
import org.luaj.vm2.script.ScriptEngineTests; import org.luaj.vm2.script.ScriptEngineTests;
public class AllTests { public class AllTests {
@@ -81,6 +82,7 @@ public class AllTests {
lib.addTestSuite(LuajavaClassMembersTest.class); lib.addTestSuite(LuajavaClassMembersTest.class);
lib.addTestSuite(LuaJavaCoercionTest.class); lib.addTestSuite(LuaJavaCoercionTest.class);
lib.addTestSuite(RequireClassTest.class); lib.addTestSuite(RequireClassTest.class);
lib.addTestSuite(OsLibTest.class);
suite.addTest(lib); suite.addTest(lib);
// Script engine tests. // Script engine tests.

View File

@@ -0,0 +1,71 @@
package org.luaj.vm2.lib.jse;
import org.luaj.vm2.lib.OsLib;
import junit.framework.TestCase;
public class OsLibTest extends TestCase {
OsLib lib;
double time;
public void setUp() {
lib = new OsLib();
time = new java.util.Date(2001-1900, 7, 23, 14, 55, 02).getTime() / 1000.0;
}
void t(String format, String expected) {
String actual = lib.date(format, time);
assertEquals(expected, actual);
}
public void testStringDateChars() { t("foo", "foo"); }
public void testStringDate_a() { t("%a", "Thu"); }
public void testStringDate_A() { t("%A", "Thursday"); }
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.;
public void testStringDate_UW_neg4() { time-=4*DAY; t("%c %U %W", "Sun Aug 19 14:55:02 2001 33 33"); }
public void testStringDate_UW_neg3() { time-=3*DAY; t("%c %U %W", "Mon Aug 20 14:55:02 2001 33 34"); }
public void testStringDate_UW_neg2() { time-=2*DAY; t("%c %U %W", "Tue Aug 21 14:55:02 2001 33 34"); }
public void testStringDate_UW_neg1() { time-=DAY; t("%c %U %W", "Wed Aug 22 14:55:02 2001 33 34"); }
public void testStringDate_UW_pos0() { time+=0; t("%c %U %W", "Thu Aug 23 14:55:02 2001 33 34"); }
public void testStringDate_UW_pos1() { time+=DAY; t("%c %U %W", "Fri Aug 24 14:55:02 2001 33 34"); }
public void testStringDate_UW_pos2() { time+=2*DAY; t("%c %U %W", "Sat Aug 25 14:55:02 2001 33 34"); }
public void testStringDate_UW_pos3() { time+=3*DAY; t("%c %U %W", "Sun Aug 26 14:55:02 2001 34 34"); }
public void testStringDate_UW_pos4() { time+=4*DAY; t("%c %U %W", "Mon Aug 27 14:55:02 2001 34 35"); }
}