The definitive guide to setting up copyright year {{my.tokens}} for Emails and LPs

SanfordWhiteman
Level 10 - Community Moderator
Level 10 - Community Moderator

2020-12-29 17_59_37-JS __ _img onerror_ or self-replacing _script_ for inline date — Mozilla Firefox.png

Despite some recurring wishful (Christmas-wishful?) thinking on the Marketo Community, no, Marketo does not have a {{system.token}} that outputs only the current year.

 

You have to build your own token — or rather, at least two {{my.tokens}}, one for Emails (using Velocity) and one for LPs (using Javascript).

 

There’s code below for cutting-and-pasting, but I want to take a moment to describe the technical goal.

 

“The current year” seems self-explanatory, but there’s a hidden question: Whose current year? Remember, the year changes over the course of 24 hours on New Year’s Day. So across Marketo email & LP contexts, you really have at least six (!) possible years:

 

(1) the year in Marketo’s internal time zone (CST, if you don’t know)
(2) the year in your company’s main time zone, as set in your Marketo instance
(3) the year in the time zone of the office/group that owns an asset, if different from the company HQ time zone
(4) the year in Marketo’s best-guess Person Time Zone field (derived from the lead’s City and Country fields, which may not reflect the person’s real-life day)
(5) the year in the true operating time zone of the lead, provided authoritatively by the lead via form fillout or other contact
(6) the year on the machine the lead is using to view your web page

 

Clearly, you want to output the year that’s the closest to what the lead thinks the year is, technology permitting. Now, it may not be critically necessary to hit the target for every single lead, but let’s make a good faith effort to get there.

 

To not get too crazy with variations, I settled on the following rules:

 

  • for emails: the time zone shall be the lead’s known operating time zone (option 5 above), or their Person Time Zone (option 4) if the operating time zone is not known, and finally falling back to the office time zone (option 3)
  • for LPs: the time zone shall be the person’s machine time zone (option 6 above)

 

The {{my.Current Year - Email}} Email Script token

The Velocity logic is quite simple: loop over the list of potential timezone fields and use the first one that’s non-empty, non-null, and is a valid IANA time zone name.

 

## @requires-fields
## Person.Operating Time Zone (OperatingTimeZone)
## Person.Person Time Zone (Person_Time_Zone)
##
#set( $officeTimeZone = "America/New_York" )
#set( $userTimeZones = [$lead.OperatingTimeZone, $lead.Person_Time_Zone, $officeTimeZone] )
#foreach( $userTZ in $userTimeZones )
#if( $display.alt($bestFitTimeZoneObj,"").isEmpty() && !$display.alt($userTZ,"").isEmpty() )
#set( $bestFitTimeZoneObj = $date.getTimeZone().getTimeZone($userTZ) )
#end
#end
#set( $currentYear = $date.format("yyyy", $date.getDate(), $date.getLocale(), $bestFitTimeZoneObj ) )
${currentYear}

 

  • yes, the code does gracefully fall back if a field is filled but has a typo — but keep your data clean and don’t let this happen!
  • make sure to check off both the Operating Time Zone (or equivalent field in your instance) and Person Time Zone field in Script Editor, as noted in the @requires-fields annotation
  • set $officeTimeZone to the best-fit time zone for each level of the Marketing Activities tree, overriding the token as necessary for different geographical teams

If you haven’t created an Operating Time Zone-type field in your instance, I strongly recommend you do — Person Time Zone is simply not enough if you want to be aware of where your leads actually are in the world. But you can take that field out if you want to wait on it:

 

## @requires-fields
## Person.Person Time Zone (Person_Time_Zone)
##
#set( $officeTimeZone = "America/New_York" )
#set( $userTimeZones = [$lead.OperatingTimeZone, $lead.Person_Time_Zone] )
#foreach( $userTZ in $userTimeZones )
#if( $display.alt($bestFitTimeZoneObj,"").isEmpty() && !$display.alt($userTZ,"").isEmpty() )
#set( $bestFitTimeZoneObj = $date.getTimeZone().getTimeZone($userTZ) )
#end
#end
#set( $currentYear = $date.format("yyyy", $date.getDate(), $date.getLocale(), $bestFitTimeZoneObj ) )
${currentYear}

 

The {{my.Current Year - LP}} Text token

The JavaScript for LPs is significantly simpler than the Velocity snippet. That’s because on web pages, we can take advantage of how users themselves set up their computer’s locale, something that’s impossible with email. In JavaScript, Date#getFullYear outputs the year in their local time.

 

<script>(document.currentScript || document.scripts[document.scripts.length - 1]).outerHTML = new Date().getFullYear();</script>

 

The fundamental difference between the above code and some less-than-good code that’s in circulation on the Nation (as well as on the net in general) is that I’m following best practices by  not using document.write.

 

The ancient document.write method has many dangers, so it’s officially discouraged in the current HTML spec, and browsers are making specific interventions to make it harder to use.

 

Admittedly, using document.write in this particular case wouldn’t be dangerous, and browsers won’t outright block you (yet). But it just looks bad: if I see that method these days, I just assume there’s something wrong with the rest of the code.☺

 

The {{my.Current Year - LP}} code is still somewhat unusual. We get a handle to the code’s own <script> element, using the newfangled document.currentScript or a fallback for IE, and replace its outerHTML at exactly the same place in the DOM. You won’t even see the <script> in F12 Dev Tools afterward!

 

It works in all browsers, but you’re sure not gonna see this kind of thing much on the web. Marketo LPs are a rare beast in that they’re treated as static pages — we don’t usually drop JS templating logic on top of LPs — yet the back end is limited to (plenty cool) stuff like dynamic content and tokens, as opposed to a full-fledged template language.

 

An interesting-but-failed attempt at a pure CSS solution

Before retreating to the JavaScript solution above, I spent some time working on a CSS-only approach for LPs (assuming off the bat that whatever CSS3+ tricks I came up with wouldn’t work in emails, no matter what).

 

Found a method that’s usable under specific circumstances, and while it can’t be my primary recommendation, it’s worth noting for educational purposes.

 

Consider that the {{system.dateTime}} token outputs the current time, in the Marketo instance timezone (option 2 up above), in yyyy-MM-dd HH:mm:ss format: 2021-01-01 12:34:56.

 

(The instance time zone isn’t the best-fit zone for the end user, as noted, but let’s say we only care about 364 days out of the year.)

 

The first 4 characters, 2021 are therefore the current year. If we could only use CSS to tell the browser to show only the first 4 characters in a container, right? Well, this is indeed possible... if you’re using certain fonts.

 

When you hear “set an element’s width based on font size” you probably imagine setting a width property (width, max-width, or min-width) using a unit like em or rem. But that’s technically basing the width of an element on the height of the font.

 

Remember, the CSS font-size is what becomes a single em, and font-size refers to the height of letters.

 

So if you do font-size: 16px; width: 4em;, that’s setting the width of a box to 4x the height of the tallest letter in the font, or 64px. Can't think of any layouts that benefit from such a proportion!

 

But there’s a little-used CSS unit called ch that’s incredibly convenient for today’s task: displaying the first 4 numeric characters (N.B. the numeric part) in an element.

 

ch is a unit based on the width of the numeral zero (0) in the font. That’s promising, right? So if you have the string 0000000 (8 zeroes) and you set width: 4ch and hide overflow, you should see only 4 zeroes.

 

And that works perfectly (except in IE — shocking, I’m sure — though that’s fixable and isn’t the main reason this remains interesting rather than recommended).

 

So, timidly, I tried this markup, using {{system.dateTime}} and the necessary inline styles:

 

<span style="white-space: nowrap; overflow: hidden; max-width: 4ch; display: inline-block; vertical-align: bottom;">{{system.dateTime}}</span>

 

Works perfectly if you use a monospace font like Courier New. Makes sense: In a monospace font, by definition, all characters are the same width, so the numerals 2020 are all the same width and the element shows just the 4-digit year.

 

Even more pleasant-surprisingly, the styles also work with some common proportional fonts like Arial, Verdana, and Times New Roman! You might expect those fonts to use different widths for different numbers, like making the number 1 slightly narrower, but their designers opted not to. So for characters 0-9, they’re effectively monospace.

 

Ah, but it couldn’t last.

 

The more elegant Georgia font has proportional numbers, specifically a narrower 1. So, since 4ch is the width of 4 0 characters, that leaves extra space at the end to reveal the first hyphen in 2021-01-01:

 

2020-12-25-20_43_02-Using--ch--to-truncate-width

 

Naturally, across the range of web fonts, there are going to be a whole bunch of fonts that do what Georgia does. Pro typography is like that.

 

If you use Arial, Verdana, or another font that tests out correctly, that’s safe. (Though you can’t guarantee there won’t be corporate rebranding at some point.) If you decide to play with the CSS version, you’d then need to work around the IE bug. Hint: the fix is to use 4.648ch instead of 4ch, but I’ll leave you to figure out how to apply that style only to IE.

3985
3
3 Comments