<CharlieDigital/> Programming, Politics, and uhh…pineapples

23Feb/175

Calculating Date Differentials In Neo4j

Posted by Charles Chen

(Or calculating date differentials in any environment where you don't have a date object)

For all the awesomeness of graph databases and Neo4j, the lack of a native date object seems a glaring omission and makes it difficult to calculate date differentials if you have two date strings and did not have the foresight to store it as a numeric value.

To get around this, you will need to either update all of your relevant nodes to calculate a numeric value which can be used for date operations.  Alternatively, you can use the magic of Julian Dates!  Why Julian?  Because it's a continuous numeric value starting from the beginning of the Julian Period and can be "easily" calculated given a date string.

Consider a case where we have a task and a state and we want the average number of days between the created date of the task and the created date of the state.

In the sample Cypher below, the dates are stored in yyyy-MM-dd format:

MATCH (task:Task)-[:HAS_STATE]-(state:TaskState)
WITH 
    toInt(substring(task.CreatedUtc, 0, 4)) AS task_year, 
    toInt(substring(task.CreatedUtc, 5, 2)) AS task_month, 
    toInt(substring(task.CreatedUtc, 8, 2)) AS task_day,
    toInt(substring(state.CreatedUtc, 0, 4)) AS state_year, 
    toInt(substring(state.CreatedUtc, 5, 2)) AS state_month, 
    toInt(substring(state.CreatedUtc, 8, 2)) AS state_day,
    substring(task.CreatedUtc, 0, 10) AS task_created,
    substring(state.CreatedUtc, 0, 10) AS state_created
WITH 
    task_year, task_month, task_day, floor((14-task_month)/12) AS task_a,
    state_year, state_month, state_day, floor((14-state_month)/12) AS state_a,
    task_created, state_created
WITH 
    task_year, task_month, task_day, task_a, task_year + 4800 - task_a AS task_y, task_month + (12 * task_a) - 3 AS task_m,
    state_year, state_month, state_day, state_a, state_year + 4800 - state_a AS state_y, state_month + (12 * state_a) - 3 AS state_m,
    task_created, state_created 
WITH 
    task_day + floor(((153 * task_m) + 2)/5) + (365 * task_y) + floor(task_y/4) - floor(task_y/100) + floor(task_y/400) - 32045 AS task_jd,
    state_day + floor(((153 * state_m) + 2)/5) + (365 * state_y) + floor(state_y/4) - floor(state_y/100) + floor(state_y/400) - 32045 AS state_jd,
    task_created, state_created
RETURN avg(state_jd - task_jd) AS averageTimeToCompletionForScope

The actual algorithm is included below from the Wiki article:

Calculating a Julian Day value from a Gregorian Date

Filed under: Uncategorized 5 Comments