Issues with Javascript Date and string.split()

I’m trying to get a temporal query going using Rob Ashton’s example as a starting point, but for some reason the Date Object doesn’t work as I’d expect. I’m using Event Store 3.4.0.

Here is my projection:

function convert_date(thedate){
var pieces = thedate.toString().split(".");
return pieces[0];
}

fromCategory(‘orderid’).foreachStream().when({
‘$init’: function(){
return { productStartDates: {}};
},
‘OrderEntry.Events.ProductAddedToOrder’ : function (state, event) {
state.productStartDates[event.data.ProductId] = event.data.Occurred;

        return state;
    },
    'OrderEntry.Events.ProductRemovedFromOrder' : function (state, event) {
        var productStartDate = convert_date(state.productStartDates[event.data.ProductId]);
        var productEndDate = convert_date(event.data.Occurred);
       
        var startDate = new Date(productStartDate);
        var endDate = new Date(productEndDate);
        var difference = (endDate.getTime() - startDate.getTime()) / 1000;
       
        emit('productRemovalSpans', 'ProductsLikelyToBeReordered', {
           CustomerId: event.data.CustomerId,
           ProductId: event.data.ProductId,
           StartDate: productStartDate,
           EndDate: productEndDate,
           Span: difference
        });

        if(difference > 10) {
            emit('productsLikelyToBeReordered', "ProductLikelyToBeReordered", {
              CustomerId: event.data.CustomerId,
              ProductId: event.data.ProductId
            });
        }
       
        return state;
    }
});

``

I’m only using the productRemovalSpans stream to verify that I have the correct data, here is an example event that gets created.

Data
{
“CustomerId”: “cate”,
“ProductId”: “hair spray”,
“StartDate”: “2015-12-24T17:04:57.228Z”,
“EndDate”: “2015-12-24T17:05:58.566Z”,
“Span”: null
}

Metadata
{
“$v”: “6:81:82:3”,
“$s”: {
“$ce-orderid”: 16
},
“$causedBy”: “a19f0f2e-6cf2-47b5-8c8a-3930ce5fb2b6”
}

``

I’ve put the date difference logic in a jsfiddle and it works just fine:

https://jsfiddle.net/5qy0qjnr/

Maybe not coincidentally, the string split function I use to convert the date doesn’t seem to be working either.

Is there some reason why the javascript Date object and string split method works differently “inside” Event Store?

Cheers,

Tim

Have you tried debugging the projection in chrome? Perhaps some of the
data is not coming out exactly as you think it is?

I had not since I was using the productRemovalSpans stream to output the data that was coming in. For instance, the StartDate and EndDate attributes in the sample I pasted has the dates as I expect them, except they shouldn’t have the .***Z part at the end because I’m stripping it off, and the Span attribute should have the difference in seconds (~61).

I did try debugging the projection, and weirdly enough, it acts as I expect it should. There are two cases (as I expect) where the it gets into the condition where it emits the ProductLikelyToBeReordered event to the productsLikelyToBeReordered stream.

So I stopped the projection, reset it, and then started it again. It still doesn’t create the two ProductLikelyToBeReordered events in the productsLikelyToBeReordered stream. It does update the productRemovalSpans stream.

When it runs normally outside of the debugger, the string.split function and Date constructor don’t work as expected.

I’ve included some of the logging:

[15816,11,16:15:38.560] Updating ‘Products Likely To Be Reordered’ projection source to 'function convert_date(thedate){

var pieces = thedate.toString().split(".");

return pieces[0];

}

fromCategory(‘orderid’).foreachStream().when({

‘$init’: function(){

return { productStartDates: {}};

},

‘OrderEntry.Events.ProductAddedToOrder’ : function (state, event) {

state.productStartDates[event.data.ProductId] = event.data.Occurred;

return state;

},

‘OrderEntry.Events.ProductRemovedFromOrder’ : function (state, event) {

var productStartDate = convert_date(state.productStartDates[event.data.ProductId]);

var productEndDate = convert_date(event.data.Occurred);

var startDate = new Date(productStartDate);

var endDate = new Date(productEndDate);

var difference = (endDate.getTime() - startDate.getTime()) / 1000;

emit(‘productRemovalSpans’, ‘ProductsLikelyToBeReordered’, {

CustomerId: event.data.CustomerId,

ProductId: event.data.ProductId,

StartDate: productStartDate,

EndDate: productEndDate,

Span: difference

});

if(difference > 10) {

emit(‘productsLikelyToBeReordered’, “ProductLikelyToBeReordered”, {

CustomerId: event.data.CustomerId,

ProductId: event.data.ProductId

});

}

return state;

}

});’ (Requested type is: ‘’)

[15816,11,16:15:38.560] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:15:41.663] Enabling ‘Products Likely To Be Reordered’ projection

[15816,11,16:15:41.796] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:32:55.921] Disabling ‘Products Likely To Be Reordered’ projection

[15816,14,16:32:55.929] Writing checkpoint for Products Likely To Be Reordered at $ce-orderid: 16 with expected version number 3

[15816,14,16:32:55.929] Checkpoint has been written for projection Products Likely To Be Reordered at sequence number 4 (current)

[15816,11,16:32:55.961] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:33:01.957] Resetting ‘Products Likely To Be Reordered’ projection

[15816,11,16:33:01.988] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:33:04.928] Disabling ‘Products Likely To Be Reordered’ projection

[15816,11,16:33:04.928] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:40:56.373] Enabling ‘Products Likely To Be Reordered’ projection

[15816,11,16:40:56.393] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:42:09.673] Resetting ‘Products Likely To Be Reordered’ projection

[15816,14,16:42:09.689] Writing checkpoint for Products Likely To Be Reordered at $ce-orderid: 16 with expected version number 4

[15816,14,16:42:09.689] Checkpoint has been written for projection Products Likely To Be Reordered at sequence number 5 (current)

[15816,11,16:42:09.735] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:42:13.968] Disabling ‘Products Likely To Be Reordered’ projection

[15816,14,16:42:14.093] Writing checkpoint for Products Likely To Be Reordered at $ce-orderid: 16 with expected version number 5

[15816,14,16:42:14.093] Checkpoint has been written for projection Products Likely To Be Reordered at sequence number 6 (current)

[15816,11,16:42:14.109] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:42:29.625] Resetting ‘Products Likely To Be Reordered’ projection

[15816,11,16:42:29.639] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:42:37.731] Disabling ‘Products Likely To Be Reordered’ projection

[15816,11,16:42:37.735] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:48:31.972] Disabling ‘Products Likely To Be Reordered’ projection

[15816,11,16:48:31.972] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:49:09.458] Resetting ‘Products Likely To Be Reordered’ projection

[15816,11,16:49:09.474] ‘Products Likely To Be Reordered’ projection source has been written

[15816,11,16:49:13.964] Enabling ‘Products Likely To Be Reordered’ projection

[15816,11,16:49:13.984] ‘Products Likely To Be Reordered’ projection source has been written

This will not likely help us in debugging your projection as it is
talking about checkpointing. You can log stuff from your projection to
the console while it is running. See the log() function.

Cheers,

Greg

Thanks for the log() suggestion, I had tried console.log() earlier and that gave me an error.

It turns out that the string representation was in the default .net format: /Date( )/

I’m guessing the Event Store converts it when showing it in the web interface, which threw me off.

Thanks for your help.

Cheers,

Tim

And this didn't come up in the debugger?

Nope, in the debugger it was a string in the format that shows in the Event Store UI like such: 2015-12-27T17:29:27.244Z. That explains why it worked in the debugger but not during runtime, although I have no clue why they would be different.

My guess is due to pretty printing on the REST API. This is worth looking at.

Just thought I’d put my updated projection that works for the default .NET serialized dates I have in case anyone else runs into this issue.

Also, is there a way that I can use this convert_date function in other projections? I replied to another thread that suggested adding support for npm packages, which would be nice.

function convert_date(thedate){
return new Date(parseInt(thedate.toString().substr(6)));
}

fromCategory(‘orderid’).foreachStream().when({
‘$init’: function(){
return { productStartDates: {}};
},
‘OrderEntry.Events.ProductAddedToOrder’ : function (state, event) {
state.productStartDates[event.data.ProductId] = event.data.Occurred;

    return state;
},
'OrderEntry.Events.ProductRemovedFromOrder' : function (state, event) {
    var productStartDate = convert_date(state.productStartDates[event.data.ProductId]);
    var productEndDate = convert_date(event.data.Occurred);

    var difference = (productEndDate.getTime() - productStartDate.getTime()) / 1000;

    if(difference > 10) {
        emit('productsLikelyToBeReordered', "ProductLikelyToBeReordered", {
           CustomerId: event.data.CustomerId,
           ProductId: event.data.ProductId,
           SecondsInOrder: difference
        });
    }
   
    return state;
}

});

``

Hi Tim,
Just out of curiosity, when you are serialising your objects to send to Event Store, do you specify any kind of handling for dates, e.g. With Newtonsoft.Json you can specify DateFormatHandling to MicrosoftDateFormat and it will appear in the format “/Date(111)/”.

http://www.newtonsoft.com/json/help/html/datesinjson.htm for more information.

So if you use IsoDateFormat you should see the same format of your data in the debugger, Event Store stream browser as well as the debugger.

But, there is however an issue in Event Store in that there is a difference between what the debugger shows you and the format of the data given to the projection.

The debugger gets it’s events via the projections HTTP API via /projections/read-events

The likely cause is probably the API which serialises the results using Newtonsoft.Json to be returned to the client, but uses DateFormatHandling of IsoDateFormat which is why you saw what you saw and not the real format the date is stored in.

For reference: https://github.com/EventStore/EventStore/blob/release-v3.5.0/src/EventStore.Projections.Core/Services/Http/ProjectionsController.cs#L445

This will need to be looked at as Greg mentioned.

Hi Pieter,

I don’t specify any handling for dates, I’m just using whatever the default is for the time being from System.Web.Script.Serialization.JavascriptSerializer:

var myEvent = new EventData(Guid.NewGuid(),
fullName,
true,
Encoding.UTF8.GetBytes(new JavaScriptSerializer().Serialize(message)),
Encoding.UTF8.GetBytes(""));

``

Where message is a message received by/through NServiceBus.

Likely I will change the serialization to use the IsoDateFormat so I don’t need to use the convert_date() javascript helper function everywhere I need to use dates.

Cheers,

Tim