Wednesday 9 February 2011

MS Word: Automatic date fields


This is a post detailing the way I managed to get Word to create a date automatically with ordinal suffixes and superscript and is written in something of a tutorial style. If you already know about / aren’t interested in the details of Word fields, skip to the highlighted block about 2/3 of the way down and you’ll find the field codes necessary to achieve the result.

I recently decided to write myself a letter template. Even these days, everyone writes letters from time to time and so it seemed a good idea to have a .dotx file that would generate a suitable blank whenever I wanted to start a new one. I like to have the numbers in a date be followed by “st”, “nd” etc., and for these postscripts to be in superscript. This doesn’t seem difficult; after all, word adds the suffix automatically if you type the date in manually and you can insert a field that will fill the date in automatically. Unfortunately, these two techniques do not work together.

The first problem is getting the right date. Inserting a date using the ribbon (I’m using Word 2007 – I expect earlier versions are similar but I haven’t tried, nor have I experimented with 2010 yet) gives you the current date. That means that the date will never change, or if you tick the box that says “Update Automatically” will change every time the field is updated – not much good if you go back to the letter later for reference purposes and cannot tell when you wrote it because it has today’s date on it.

The second is that the list of formats you can choose from does not include a format which produces the “st”, “nd”, etc. suffixes. A number followed by these letters is said to be in ordinal format in Word-speak, incidentally. Perhaps elsewhere too, but I’m not sure it’s universal nomenclature.

Both these problems can be solved rather easily by amending the field directly. If you don’t know, the contents of Word’s fields are represented internally in a slightly cryptic written language. You can view this by right-clicking a field and selecting “Toggle Field Codes”. Alt-F9 will toggle code viewing for the entire document and is quite useful for fields within fields (nested fields) as they don’t all expand at once if you go down the right-click the path.

If you insert an automatically updating date and view the field code, you will see that it is created like this:
{ DATE \@ "dd MMMM yyyy" }
What’s between the inverted commas will be different unless you’ve picked the same date format as me. You can now directly change the code that generates this field.

The first problem (constantly updated date) is solved by changing the word “DATE” to “CREATEDATE”. This keyword determines what the field code does (and you will see one at the start of most fields if you look at the code). In this case, CREATEDATE generates a date based on the time the document was first created. Be aware that this is determined by the “Save As” dialog being used, so if you save an existing document with another name from within Word, this date will change in the newly named document at that point.

The “\@” merely tells Word that what comes next is a template for the date. Word calls this template a picture and the “\@” is the “picture switch”. In more general terms, a switch is a modifier to a command and is indicated here by the backslash. The picture template for this and other date fields has many components to do with date and time and this is beyond our scope here.

We can also use a switch to use the ordinal format for numbers:
{CREATEDATE \@ "d" \* ordinal}
This switch, however, modifies any numeric field to use the ordinal suffix, not just dates. As such, it does not understand the special needs of a date field and produces rubbish if you include it in a full date (with months and years in it). This is why the example above includes a directive only to show the day of the month.
To show a complete date, two adjacent fields must be employed:
{CREATEDATE \@ "d" \* ordinal}{CREATEDATE \@ " MMMM yyyy"}
Note the space before the “MMMM” inside the quotes – this gives us the space we need between the ordinal suffix and month name.

In order to form this into a single field you can use a “QUOTE” type field to encapsulate the other two. QUOTE fields simply repeat what is in them, but they can include embedded fields and so you end up with a single field that can be moved and formatted as a single object in your text:
{ QUOTE{CREATEDATE \@ "d" \* ORDINAL }{CREATEDATE \@ " MMMM yyyy" } }
So this is almost what we want. What we have now is something like this:
9th February 2010
The last problem is the superscript we want for the ordinal day suffix. Relatively simple? I’m afraid not. The \* ORDINAL switch must apply to a number, which is also printed. There is no way to make it print the suffix on its own, which means that we cannot simply create a separate field for the suffix and superscript it. There is no switch to make the suffix superscript on its own and so we cannot separate the two to allow us to format the digits and the suffix separately.

The only way to do it is to write some relatively complicated code to produce the suffixes (suffices?) based on the number from scratch and then formatting (i.e. superscript) can be applied to these letters within the field and when they are printed they will be as we want. Here is the code (and note the use of superscript within it):
{ QUOTE { CREATEDATE \@ “D” }{ IF { =MOD( { CREATEDATE \@ “d” },20) } = 1 “st” “{ IF { = MOD( { CREATEDATE \@ “d” } ,20) } = 2 “nd” “{ IF { =MOD( { CREATEDATE \@ “d” } ,20) } = 3 “rd” “{ IF { CREATEDATE \@ “d” } = 31 “st” “th” }” }“ }“ }{ CREATEDATE \@ “ MMMM yyyy” } }

Finally, we have the format I was originally after:
9th February 2010
I’ve seen some more complicated versions of this in other places, but this is my take on it. Do note, however, that the suffix creation algorithm is only suitable for dates – it will fail as soon as you count above 31! In a more easily read form it looks like this (and there’s no reason not to put it like this into your documents – it won’t spoil the layout once the field codes are hidden):
{ QUOTE
{ CREATEDATE \@ "D" }
{ IF { =MOD( { CREATEDATE \@ "d" },20) } = 1
"st"
"{ IF { = MOD( { CREATEDATE \@ "d" } ,20) } = 2
"nd"
"{ IF { =MOD( { CREATEDATE \@ "d" } ,20) } = 3
"rd"
"{ IF { CREATEDATE \@ "d" } = 31 "st" "th" }"
}"
}"
}
{ CREATEDATE \@ " MMMM yyyy" }
}
The new things introduced here are IF and MOD, as well as the “=” just before the MOD operator.

MOD is not a type of field, so we cannot put it at the start of a field as a type identifier. Instead it is a function – something we might choose to use in a mathematical expression. So, we need to know how to make a field produce the result of a mathematical expression and it will come as no surprise from the usage above that “=” is the field type identifier that does that: whatever is displayed from an “=” field is the result of a mathematical expression.

Those of you familiar with the MOD function will know that it simply divides one number (in this case the first number in brackets after the word) by another (the second number in the brackets) and keeps only the remainder. You will see that the first number (or argument) to the MOD operation is generated by a nested field (the CREATEDATE field just after the opening bracket. In this case we are using MOD to ensure that 21, 22 and 23 but not 11, 12 and 13 are treated the same as 1, 2 and 3, which is what we want (so we don't need separate tests for 1 and 21). There is also a special test right at the end for 31, which is an exception to the rule we have created (which generalised is that numbers in the 0s, 20s, 40s etc. receive special treatment and 10s, 30s, 50s etc. do not).

The IF field type prints one of two things depending on a comparison:
{ IF a = b "True Text" "False Text" }
Although I have written it as an equality test here, the comparison can be one of the other mathematical operations, such as greater than, less than or equal to, etc. The result is whatever is in the first set of quotes if the comparison is true and whatever is in the second if it is false. And you can nest other fields in these quotes which is how the composite field above is created. In the spaced out version, each new line starts further indented if the field is nested inside the previous one. As each field closes, the indentation is also reduced.

Just as a reminder, you cannot simply type (or copy and paste), unfortunately – each field must be created as a field in word. There is a quick way and that is to write the contents of the field (don’t include the outer curly brackets – these are displayed by word only to tell you that you are looking at field codes and are not part of the field), select the text that will form the field and press Ctrl-F9. You can also copy and paste fields, of course, which makes the plethora of identical CREATEDATE fields easier to deal with. Also, watch your spaces - you may end up quoting some you don't want as you construct the fields.

As a final note, when you are experimenting, don’t forget to update the field if you make changes to the field code or your changes may not be processed (F9 does this). If you are working with more than one field, the contents of all of them might not be updated together.

5 comments:

  1. hello. I was looking for the way to insert the ordinal numbers in the field of word 2013. here I thought I've found the way almost, but I need to know how to use these code with MERGEFIELD. what I'd like to do is pick up the date data from Excel file using the system of "Letters" , and on the word to shoe like 9th February 2010 as you wrote here.
    and the problem I have is I'm using Word 2013 in ITALIAN. so if I just simply changed the code like { MERGEFIELD "DATE" \@ "dd MMMM yyyy" } , it shows 09 febbraio 2014.( febbraio=february)

    someone has any idea?

    ReplyDelete
  2. My template still works in Word 2013. I haven't looked into what (if anything) has changed in field codes since I produced this on Word 2007. I shouldn't think Italian language settings will have any impact, except, of course, if the ordinal rules in Italian are different from English (in which case you will obviously need to alter my technique to accommodate them).

    In terms of your actual problem, I haven't ever experimented with MERGEFIELD in this way, but isn't it basically the same problem, except with [MERGEFIELD "DATE"] instead of [CREATEDATE]? What happens if you use my technique, but swap each instance of [CREATEDATE] with [MERGEFIELD "Date"]?

    Sorry if I've missed something. I don't understand what you mean by "the system of LETTERS".

    ReplyDelete
  3. Just re-read these comments. @Anonymous, are you saying that you want the month name in English, rather than Italian? Have you tried setting the language for the document (or just the date field) to English?

    You may have to install English as an additional editing language (in the "Language" section of the Options). This and the tools for changing language for a section of the document can be accessed from the "Review" tab of the ribbon.

    I haven't tried this, incidentally, it's just an idea.

    ReplyDelete
  4. Hi, I have tried your code and exchanged "date" with mergefield from mailings but problem is that st,nd,rd,th not works correct. For each date i receive answer extension th. do you have idea where can be problem ?

    ReplyDelete
    Replies
    1. Thanks for your comment, Anon. The only thing I can imagine is that the field you have substituted is not in the same format as that produced by CREATEDATE and / or the picture field ('/@ "d"') cannot be correctly applied to it to just get the day number.

      Delete