Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A better Date Proxy to expose java.util.Date or java.time.Instant into GraalVMs JS embedding.
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 17
//DEPS org.graalvm.sdk:graal-sdk:21.3.0
//DEPS org.assertj:assertj-core:3.21.0
// Only needed when running on vanilla JVM
//DEPS org.graalvm.js:js:21.3.0
package example;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.Set;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyExecutable;
import org.graalvm.polyglot.proxy.ProxyInstant;
import org.graalvm.polyglot.proxy.ProxyObject;
public class Application {
public static void main(String... a) {
try (var context = Context.newBuilder("js").build()) {
var today = LocalDate.now();
var bindings = context.getBindings("js");
bindings.putMember("javaInstant", new DateProxy(today.atStartOfDay().atZone(ZoneId.of("Europe/Berlin")).toInstant()));
bindings.putMember("yesterday", new DateProxy(today.minusDays(1).atStartOfDay().atZone(ZoneId.of("Europe/Berlin")).toInstant()));
var result = context.eval("js",
"""
var nativeDate = new Date(new Date().toLocaleString("en-US", {timeZone: "Europe/Berlin"}));
nativeDate.setHours(12);
nativeDate.setMinutes(0);
nativeDate.setSeconds(0);
nativeDate.setMilliseconds(0);
javaInstant.setHours(12);
({
nativeDate : nativeDate,
nativeTimeFromNativeDate : nativeDate.getTime(),
javaInstant: javaInstant,
diff: nativeDate.getTime() - javaInstant.getTime(),
isBefore: yesterday < nativeDate,
nextWeek: new Date(javaInstant.setDate(javaInstant.getDate() + 7))
})
""");
assertThat(result.getMember("nativeDate").asDate()).isEqualTo(today);
assertThat(result.getMember("diff").asLong()).isZero();
assertThat(result.getMember("isBefore").asBoolean()).isTrue();
assertThat(result.getMember("nextWeek").asDate()).isEqualTo(today.plusWeeks(1));
}
}
@SuppressWarnings("deprecation")
public static class DateProxy implements ProxyObject, ProxyInstant {
private static final Set<String> PROTOTYPE_FUNCTIONS = Set.of(
"getTime",
"getDate",
"getHours",
"getMinutes",
"getSeconds",
"setDate",
"setHours",
"toString"
);
private final Date delegate;
public DateProxy(Date delegate) {
this.delegate = delegate;
}
public DateProxy(Instant delegate) {
this(Date.from(delegate));
}
@Override
public Object getMember(String key) {
return switch (key) {
case "getTime" -> (ProxyExecutable) arguments -> delegate.getTime();
case "getDate" -> (ProxyExecutable) arguments -> delegate.getDate();
case "setHours" -> (ProxyExecutable) arguments -> {
delegate.setHours(arguments[0].asInt());
return delegate.getTime();
};
case "setDate" -> (ProxyExecutable) arguments -> {
delegate.setDate(arguments[0].asInt());
return delegate.getTime();
};
case "toString" -> (ProxyExecutable) arguments -> delegate.toString();
default -> throw new UnsupportedOperationException("This date does not support: " + key);
};
}
@Override
public Object getMemberKeys() {
return PROTOTYPE_FUNCTIONS.toArray();
}
@Override
public boolean hasMember(String key) {
return PROTOTYPE_FUNCTIONS.contains(key);
}
@Override
public void putMember(String key, Value value) {
throw new UnsupportedOperationException("This date does not support adding new properties/functions.");
}
@Override
public Instant asInstant() {
return delegate.toInstant();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment