static JSValue js_sys_ntp_shift(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
	JSValue v;
	u64 sec, frac, shift;
	s64 frac_usec;
	if ((argc<2) || !JS_IsObject(argv[0]))
		return GF_JS_EXCEPTION(ctx);

	v = JS_GetPropertyStr(ctx, argv[0], "n");
	if (JS_IsNull(v)) return GF_JS_EXCEPTION(ctx);
	JS_ToInt64(ctx, &sec, v);
	JS_FreeValue(ctx, v);

	v = JS_GetPropertyStr(ctx, argv[0], "d");
	if (JS_IsNull(v)) return GF_JS_EXCEPTION(ctx);
	JS_ToInt64(ctx, &frac, v);
	JS_FreeValue(ctx, v);

	JS_ToInt64(ctx, &shift, argv[1]);
	frac_usec = (s64) (frac * 1000000) / 0xFFFFFFFFULL;
	frac_usec += shift;
	while (frac_usec<0) {
		frac_usec += 1000000;
		sec -= 1;
	}
	while (frac_usec>1000000) {
		frac_usec -= 1000000;
		sec += 1;
	}
	frac = (frac_usec * 0xFFFFFFFFULL) / 1000000;
	v = JS_NewObject(ctx);
	if (JS_IsException(v)) return v;
	JS_SetPropertyStr(ctx, v, "n", JS_NewInt64(ctx, sec));
	JS_SetPropertyStr(ctx, v, "d", JS_NewInt64(ctx, frac));
	return v;
}
static JSValue js_sys_ntp_shift(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
	JSValue v;
	u64 sec, frac, shift;
	s64 frac_usec;
	if ((argc<2) || !JS_IsObject(argv[0]))
		return GF_JS_EXCEPTION(ctx);

	v = JS_GetPropertyStr(ctx, argv[0], "n");
	if (JS_IsNull(v)) return GF_JS_EXCEPTION(ctx);
	JS_ToInt64(ctx, &sec, v);
	JS_FreeValue(ctx, v);

	v = JS_GetPropertyStr(ctx, argv[0], "d");
	if (JS_IsNull(v)) return GF_JS_EXCEPTION(ctx);
	JS_ToInt64(ctx, &frac, v);
	JS_FreeValue(ctx, v);

	JS_ToInt64(ctx, &shift, argv[1]);
	frac_usec = (s64) (frac * 1000000) / 0xFFFFFFFFULL;
	frac_usec += shift;
	while (frac_usec<0) {
		frac_usec += 1000000;
		sec -= 1;
	}
	while (frac_usec>1000000) {
		frac_usec -= 1000000;
		sec += 1;
	}
	frac = (frac_usec * 0xFFFFFFFFULL) / 1000000;
	v = JS_NewObject(ctx);
	if (JS_IsException(v)) return v;
	JS_SetPropertyStr(ctx, v, "n", JS_NewInt64(ctx, sec));
	JS_SetPropertyStr(ctx, v, "d", JS_NewInt64(ctx, frac));
	return v;
}