MikeMurko MikeMurko - 6 months ago 44
Java Question

Nashorn NativeDate conversion to java.util.Date

When returning Javascript

Date
objects to Java with Nashorn on Java 8 like so:

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("nashorn");
Object js = engine.eval("new Date();");


With the following attempts I get exceptions:


  1. Date javaDate = (Date)js;


    jdk.nashorn.api.scripting.ScriptObjectMirror cannot be cast to java.util.Date

  2. Date javaDate = js.to(Date.class);


    Cannot cast jdk.nashorn.internal.objects.NativeDate to java.util.Date

  3. Date javaDate = (Date)ScriptUtils.convert(js.to(NativeDate.class), Date.class);


    Cannot cast jdk.nashorn.internal.objects.NativeDate to java.util.Date



Back with Rhino I was simply using
context.jsToJava(nativeDateObj, Date.class);
.

Any ideas how I can actually cast this NativeDate when it's returned to Java?

P.S. If I do js.toString() then it gives me "[Date 2012-01-01T19:00:00.000Z]". I guess I could regex parse that ... but why-oh-why ...

Answer

Cast returned JavaScript object on jdk.nashorn.api.scripting.ScriptObjectMirror, then you will be able to access its properties in a "map-like" manner.

ScriptObjectMirror jsDate = (ScriptObjectMirror) engine.eval("new Date();")
long timestampLocalTime = (Long) jsDate.callMember("getTime"); 
// yes, js date returns timestamp in local time so you need to adjust it... ;)
int timezoneOffsetMinutes = (Integer) jsDate.callMember("getTimezoneOffset");
// java.util.Date construcctor utilizes UTC timestamp
Date jDate = new Date(timestampLocalTime + timezoneOffsetMinutes * 60 * 1000);

See also: http://cr.openjdk.java.net/~sundar/jdk.nashorn.api/8u20/javadoc/jdk/nashorn/api/scripting/ScriptObjectMirror.html

However if you are about to use some "JavaScript class" frequently on the Java side - you may find it useful to define "overlay" interface to access javascript object's methods in more convenient way. See the following example:

public interface JsDateWrap {
    long getTime();
    int getTimezoneOffset();
    // ...
}

Object jso = engine.eval("new Date();");
JsDateWrap jsDate = ((Invocable) engine).getInterface(jso, JsDateWrapper.class);
Date jDate = new Date(jsDate.getTime() + jsDate.getTimezoneOffset() * 60 * 1000);