Simphony JavaScript Extensibility

DateTime Access in JavaScript Extensibility

Introduction

Simphony JavaScript Extensibility tries its best to convert between C# and Javascript native types (ints, doubles, dates).

One slightly complicated area is the C# DateTime/Javascript Date conversion. If a script reads the check CheckOpenTime property, the C# DateTime struct is automatically converted to a Javascript Date object. In fact, since the conversion happens automatically, it is not possible to access the C# DateTime struct directly.

The conversion from C# DateTime to JavaScript Date has issues, especially when the DateTime.Kind property in C# is “Unknown”. Both objects offer their own functionality, but they are not equivalent.

There are times where direct access to the C# DateTime struct is necessary. For example, a script would prefer the C# DateTime parsing and formatting functions. In order to help JavaScript applications access the C# DateTime struct directly, the SimphonyExtensibilityAPI.DateTimeAccess API object was introduced.

API

DateTimeAccess API functionality:

  • Wraps C# DateTime with DateTimeScriptable. This class provides identical DateTime functionality, but it hides the underlying DateTime to prevent Javascript native Date conversion.
  • All DateTime constructors are duplicated for DateTimeScriptable in the DateTimeAccess API.
  • All DateTime methods/properties are duplicated as pass-throughs in the DateTimeScriptable object.
  • Overloaded operators are not available in DateTimeScriptable.

Construction

There are 2 ways to construct a DateTimeScriptable object

  1. Use the DateTimeAccess methods to create the object from constituent parts (year, months, …)
  2. Use the DateTimeAccess methods to extract a DateTime from an existing object (for example, CheckHeader.CheckOpenTime)

(See API documentation below for all methods on DateTimeAccess.)

Using DateTimeAccess to construct a DateTimeScriptable is straightforward; the DateTime constructor methods are duplicated in DateTimeAccess.

var _api = SimphonyExtensibilityAPI;
let dts = null;

// allocate with date
dts = _api.DateTimeAccess.AllocateScriptable(1980, 1, 10);
 
// allocate with date, time, and ms
dts = _api.DateTimeAccess.AllocateScriptable(1980, 1, 10, 12, 30, 45, 500);

Constructing a DateTimeScriptable from an existing DateTime is subtle. While a constructor exists with a DateTime, the Javascript engine will automatically convert the argument to a Javascript Date, and then convert back to DateTime, which may lose important information such as the Kind property. In general, this conversion is fine.

// allocate directly, DateTime converted to Date, and then back to DateTime
let dts = _api.DateTimeAccess.AllocateScriptable(args.Check.Header.CheckOpenTime);

To prevent C# DateTime from being converted to Date, DateTimeAccess provides a method which uses reflection to access the DateTime without the Javascript engine converting it to a Date.

// method will use reflection to access the DateTime property,
// Javascript engine will not be involved
let dts = _api.DateTimeAccess.AllocateScriptableFromProperty(args.Check.Header, 'CheckOpenTime');

What does this accomplish? The moment the Javascript engine accesses the C# CheckOpenTime property, it is converted to a Javascript Date. By using reflection, the DateTimeScriptable is allocated without the Javascript engine ever touching the property.

Why would a script writer the original DateTime? C# DateTime has properties not available in Javascript Date. The “Kind” property (Local, UTC, Unknown) can affect how a Javascript Date is created. If Kind==Unknown, many problems can arise.

DateTimeScriptable

A DateTimeScriptable object wraps a C# DateTime struct. Most methods in DateTime are provided in DateTimeScriptable. (See API reference below)

Once a script has access to DateTimeScriptable, it can treat it in the same way as it would a native DateTime.

The only unimplemented methods are operator overloads.

Code Samples

// use DateTime.Parse() to create a js Date
let jsDate = _net.System.DateTime.Parse("April 15, 2025 12:30:35pm");
 
// convert to DateTime wrapper object
let netDate = _api.DateTimeAccess.AllocateScriptable(jsDate);
 
// format
let fmtText = null;
fmtText = netDate.ToString("yyyy-MM-dd HH:mm:ss.fff");
fmtText = netDate.ToLongDateString() + ' ' + netDate.ToLongTimeString();
 
// get day of week
let dayOfWeek = netDate.DayOfWeek.ToString();
 
// date arithmetic, Add/Subtract return a DateTimeScriptable
netDate = netDate.AddMonths(5);
netDate = netDate.AddMinutes(30);

API Reference

DateTimeAccess

public class DateTimeScriptable
{
   public DateTimeScriptable Add( TimeSpan value );
   public DateTimeScriptable AddDays( double value );
   public DateTimeScriptable AddHours( double value );
   public DateTimeScriptable AddMilliseconds( double value );
   public DateTimeScriptable AddMinutes( double value );
   public DateTimeScriptable AddMonths( int value );
   public DateTimeScriptable AddSeconds( double value );
   public DateTimeScriptable AddTicks( long value );
   public DateTimeScriptable AddYears( int value );
   public DateTimeScriptable Subtract( TimeSpan value );
   public bool IsDaylightSavingTime();
   public DateTimeScriptable ToLocalTime();
   public DateTimeScriptable ToUniversalTime();
   public double ToOADate();
   public int CompareTo( DateTimeScriptable value );
   public long ToBinary();
   public long ToFileTime();
   public long ToFileTimeUtc();
   public string ToLongDateString();
   public string ToLongTimeString();
   public string ToShortDateString();
   public string ToShortTimeString();
   public String[] GetDateTimeFormats( char format );
   public String[] GetDateTimeFormats( char format, IFormatProvider provider );
   public String[] GetDateTimeFormats( IFormatProvider provider );
   public String[] GetDateTimeFormats();
   public TimeSpan Subtract( DateTimeScriptable value );
   public TypeCode GetTypeCode();
   public DateTime Actual { get; }
   public DateTime Date { get; }
   public DateTimeKind Kind { get; }
   public DayOfWeek DayOfWeek { get; }
   public int Day { get; }
   public int DayOfYear { get; }
   public int Hour { get; }
   public int Millisecond { get; }
   public int Minute { get; }
   public int Month { get; }
   public int Second { get; }
   public int Year { get; }
   public long Ticks { get; }
   public TimeSpan TimeOfDay { get; }
}