Adobe Launch Date Formatting
Overview
One issue I faced at my job was that the data layer contained different date formats in different site sections — and sometimes in different portions of the same site section. For the purposes of this example, let’s say I work on an airline website, and that early in the booking flow, the departure date is in the 'MM/DD/YY'
format. But later in the booking flow, that same date is in the 'YYYY-MM-DD'
format.
Adobe Analytics wants to receive the 'YYYY-MM-DD'
format, which means that the lower funnel is fine . . . but the upper funnel would need some custom logic to reformat it.
To further complicate matters, one of our marketing pixels wants that same date in the 'MM/DD/YYYY'
(four-digit year) format.
The solution:
Standardize the data layer so that all dates are JavaScript date objects instead of strings, giving us the most versatility to reformat any way we like from there.
Create a set of Adobe Launch data elements that can take in a JavaScript date object and return it in a predetermined format. (One data element per desired format — one that returns a
'YYYY-MM-DD'
string, one that returns a'MM/DD/YYYY'
string, etc.)
Date-Formatting Syntax
I’ll use the 'YYYY-MM-DD'
format as my example. The name of this data element will be date | YYYY-MM-DD format
.
The first thing to mention is that you can pass one data element into another. I didn’t know that at first, and it was one of those discoveries that kind of blew my mind. I had been thinking of data elements as variables, since that was how we’d been using them, but they are really more like functions — they return a value. And, optionally, they can take in an argument and do something with it before returning that value.
Here is our data element’s full syntax. (Scroll past to see an explanation of each code block.)
const inputDate = event;
let outputYYYYMMDD = (!!inputDate) ? inputDate : '';
const formattedDateArr = [];
const formatYYYYMMDD = (singleDateObj) => {
let yyyy = 'YYYY';
let mm = 'MM';
let dd = 'DD';
if (typeof singleDateObj.getMonth === 'function') {
yyyy = singleDateObj.getFullYear();
mm = singleDateObj.getMonth() + 1;
mm = (mm < 10) ? '0' + mm : mm;
dd = singleDateObj.getDate();
dd = (dd < 10) ? '0' + dd : dd;
}
return yyyy + '-' + mm + '-' + dd;
}
if (!!inputDate) {
if (Array.isArray(inputDate)) {
// If inputDate is an array:
if (inputDate.length > 0 && typeof inputDate[0].getMonth === 'function') {
inputDate.forEach((dateObj) => {
if (typeof dateObj.getMonth === 'function') {
formattedDateArr.push(formatYYYYMMDD(dateObj));
}
});
outputYYYYMMDD = (formattedDateArr.length > 0) ? formattedDateArr.join() : '';
} else {
outputYYYYMMDD = '';
}
} else if (typeof inputDate.getMonth === 'function') {
// If inputDate is an individual date object:
outputYYYYMMDD = formatYYYYMMDD(inputDate);
} else if (typeof inputDate === 'string' && inputDate !== 'any') {
outputYYYYMMDD = '';
}
}
return outputYYYYMMDD;
Explanation
Line 1:
event
represents the argument that was passed into this data element. For the most part, we will expect this to be a JavaScript date object. (Although, as you will see later in this flow, that is not the only use case, and we have plenty of logic to confirm the data type before we start calling methods on it that could throw an error.) We store it ininputDate
.Line 2: As the name implies,
outputYYYYMMDD
is thelet
that we will be returning at the end. But we set a default value up top, because there are some non-date-object use cases to account for — hence the ternary:First, the value pulled from the data layer may be
'any'
— a string, not a date object. This is the default value if the user is at the top of the booking funnel and has not filtered for a specific date (or range of dates) yet.Second, something may have gone wrong in the data layer, and something like
null
orundefined
or'NA'
has been passed into our data element. If so, we default to an empty string.
Line 3: Create an empty array for use cases where multiple dates are being handled. (The user may be filtering for a range of departure dates.)
Lines 5 - 19: Create a reusable function that takes in a date object and returns it in the
'YYYY-MM-DD'
string format. (The whole point of this particular data element.)Lines 6 - 8: Set string defaults. This way, if only part of the logic has a problem, you can easily see which part.
Lines 9 - 15: Parse the date object and store each piece (year, month, day) in its respective variable.
Line 11: Add
+1
to the month, since JavaScript months start at0
, and we don’t want all the data we send to Analytics to be off by one month.Lines 12 - 14: Prepend a
0
to any single-digit month or day values.
Line 16: Return the date as a string in the
'YYYY-MM-DD'
format. (NOTE: If you want this data element to return some other format, such as'MM/DD/YYYY'
, this is the line to edit.)
Line 19: Once again, use validation to confirm that something was actually passed in before attempting to run the rest of this logic on it.
Lines 20 - 31: Use Case 1 – We have taken in an array of date objects and need to convert them into one string in the
'YYYY-MM-DD,YYYY-MM-DD,YYYY-MM-DD'
format.Lines 22 - 28: If the array contains date objects:
Line 22: Confirm that we’re dealing with an array, that it contains at least one item, and that at least the first item is a JavaScript date object.
Lines 23 - 27: Loop over the array and format each date object into a
'YYYY-MM-DD'
string.Line 24: Validation to confirm that each item is in fact a date object.
Line 25: Format each date (using the
formatYYYYMMDD
function we created up top), and then push it into ourformattedDateArr
array.
Line 28: After that process is complete, if the
formattedDateArr
contains at least one item, join it with the default delimiter — commas — and store it in ouroutputYYYYMMDD
variable to be returned at the end. Otherwise, set it to an empty string. (NOTE: Do not encode the commas.)
Lines 29 - 31: If the array contains items other than date objects, set our final value to an empty string. (Remember, we are preemptively accounting for breakages in the data layer.)
Lines 32 - 34: Use Case 2 – We have taken in an individual date object, and need to convert it into a string formatted like
'YYYY-MM-DD'
.Line 34: This one’s much simpler — just pass the date object into our
formatYYYYMMDD
function we created up top, and store the result inoutputYYYYMMDD
.
Lines 35 - 37: Use Case 3 – We have taken in a string (other than
'any'
), and need to wipe it.Line 36: We have had recurring issues in our data layer where a date object somehow gets converted to a string. If this happens, we report it to that development team, and they fix it. But in the meantime, we want to wipe those strings so that we don’t start passing odd values. Especially to AEP, which is very finicky about expected values and data types.
Line 40: Return the final value. Note that Use Case 4 would be that an
'any'
string would have passed straight through this flow unscathed and been returned as the final value.
Data Element Argument Syntax
The date | YYYY-MM-DD format
data element is ready. Now I can pass anything you want into it — I’m not even limited to the departure date. It could be an arrival date, the current date, anything. It could even be the wrong data type, and our validation will prevent it from throwing a ton of errors.
But let’s say I do want to pass the departure date into it, and that data element is simply titled departure date
. I just use _satellite.getVar()
to retrieve each one, and pass the departure date
data element into the date | YYYY-MM-DD format
data element. Like this:
_satellite.getVar('date | YYYY-MM-DD format', _satellite.getVar('departure date'));
If it went in as a single date object, it will come out as a string, like
'2024-02-01'
.If it went in as an array of date objects, it will come out as a comma-delimited string of
YYYY-MM-DD
dates, like'2024-02-01,2024-02-02,2024-02-03'
.If it went in as an
'any'
string, it will emerge intact as an'any'
string.And if it went in as any other data type, or falsy value, it will emerge as an empty string.