About TM1 Dates Vs Excel Dates
Both TM1 and Excel use a "date serial" system in which any date that you use in calculations is represented as a positive integer value. That integer value is calculated from an arbitrary starting date. Adding whole numbers to a specific serial date moves you forward through the calendar a day at a time, and subtracting whole numbers moves you backwards... as long as you don't go past the starting date of the serial number system and end up with a negative value. Times are represented as fractions of a day; 0.25 for 6am, 0.5 for noon, 0.75 for 6pm and so on.
Excel Dates
In the case of Excel for Windows, the starting date is 1 January 1900. That is, if you enter the value 1 into a cell in Excel and format it as a date, you'll see the value as 1 January 1900. 2 will be the 2nd of January 1900, 3 the 3rd of January, and so on. 367 represents 1 January 1901 because Excel treats 1900 as having been a leap year with 366 days (see below). In other words, every full day that passes adds 1 to the serial date. Jumping forward many decades, Friday 1 January 1960 is day number 21,916. The significance of that date to TM1 users will be seen later.
It's important to remember that the above relates to Excel only, and not to Access, SQL Server or other database products (or Visual Basic, for that matter). In Access, for example, the range of valid dates is 1 January 100 to 31 December 9999, the same range that can be stored in a VB or VBA variable with a Date data type.
Excel And The Macintosh
Macintosh systems use a start date of 1 January 1904, neatly bypassing the 1900 leap year issue described below. However that does mean that there's a 4 year discrepancy between the serial date values in a workbook created in Excel for Windows, and one created in Excel for the Mac. Fortunately under Tools -> Options-> Calculation (on pre-2007 versions of Excel) you'll find a workbook option called 1904 Date System. If that's checked, Excel knows that the workbook came from a Macintosh and will adjust its date calculations accordingly.
Excel Times
As noted in the introduction, times are calculated as a fraction of a day. For example 1.5 represents noon on 2 January 1900. 1.75 represents 6pm on 2 January 1900.
The Y1.9K Bug
Be aware that there is a bug in Excel's calculation of the year 1900. 1900 was not a leap year, but Excel treats it as if it were. This is a carry-over from a bug that was in Lotus 1-2-3, and was intentionally included in Excel to maintain compatibility with that program. As Microsoft explained it in KBA 214326 (http://support.microsoft.com/kb/214326):
From 1 March 1900 onward Excel's dates are correct, but if you format the number 1 using the format dddd, mmmm dd, yyyy you'll get the result Sunday, 1 January 1900. That is incorrect; 1 January 1900 was a Monday, not a Sunday. This day of week error continues until you reach 1 March, which is the first truly correct date in the Excel calendar.When Lotus 1-2-3 was first released, the program assumed that the year 1900 was a leap year, even though it actually was not a leap year. This made it easier for the program to handle leap years and caused no harm to almost all date calculations in Lotus 1-2-3.
When Microsoft Multiplan and Microsoft Excel were released, they also assumed that 1900 was a leap year. This assumption allowed Microsoft Multiplan and Microsoft Excel to use the same serial date system used by Lotus 1-2-3 and provide greater compatibility with Lotus 1-2-3. Treating 1900 as a leap year also made it easier for users to move worksheets from one program to the other.
Although it is technically possible to correct this behavior so that current versions of Microsoft Excel do not assume that 1900 is a leap year, the disadvantages of doing so outweigh the advantages.
Day Zero And Before In Excel
If you use the value zero and display it in date format you'll get the nonsense date Saturday 0 January 1900. If you try to format a negative value as a date, you'll just get a cell full of hash marks. Similarly if you try to obtain a date serial number using Excel functions like DateValue, you can only do so for dates on or after 1 January 1900. An attempt to specify an earlier date will result in an error.
The 1904 (Macintosh) system starts from zero. (1 January 1904 has a value of 0, not 1. Excel's on-line help describes the Mac system as starting from January 2, but that's probably easier than explaining to users why a serial date value of 0 works on the Mac but not Excel.) Negative numbers won't generate an error, but the number will be treated as absolute. That is, both 1 and -1 will be treated as 2 January 1904.
TM1 Dates
In the case of TM1, the arbitrary starting date is 1 January 1960. Why the difference?
CAUTION: There was a change in Planning Analytics 2.0 which allows you to set a server config parameter to use Excel serial dates instead of legacy TM1 ones. See this thread for details.
This is speculation, but early versions of TM1 (prior to version 8.4) only supported 2 digit years in the TM1 date functions. That means that you had only 99 years to play with. Had the starting date been 1 January 1900, then in the mid-90s when TM1 was getting a foothold in the market you would have had only about 4 or 5 years worth of future dates that could be calculated by the TM1 date functions. By 1 January 2000, you'd be completely out of luck. Starting at 1 January 1960 gave 40 years in the 20th century and 59 years in the 21st, which was a much more usable range of dates.
From version 8.4 many TM1 functions support 4 digit year dates in the range 1 Jan 1960 to 31 Dec 9999. Dates prior to 1 Jan 1960 are still not supported which can be a pain for certain H.R. related models, but doing so now would probably break a great many applications.
A major difference between TM1 and (Windows) Excel is that TM1 starts from 0 instead of 1. That is, 1 January 1960's serial date value is 0.
If you have a TM1 serial date and want to get the corresponding Excel serial date, you need to add 21,916.
Conversely if you have an Excel serial date and want to get the corresponding TM1 serial date, you need to subtract 21,916.
However, TM1 can no more handle negative dates than Excel can. If you try to use a negative serial date number in a TM1 time calculation, you will also get an error.
In the real world you won't normally be importing an Excel-sourced date as a serial number, but rather as a formatted string which you then need to parse to feed it into one of the TI date functions described later. (The exception may be if you're doing a DBSend into a cell, with the cell value being used in date calculations within rules. In such a case you may need to modify the value before sending it to ensure that it records TM1's serial date, and not Excel's.)
TM1 Times
TM1 times work in the same way as Excel's; just as integers represent whole days, decimal remainders represent times. 0.25 will represent 6am, 0.5 represents noon, 0.75 represents 6pm and so on.
Date And Time Functions In Excel Vs Date And Time Functions In TM1
Both Excel and TM1 provide functions for obtaining serial dates and times. TM1's set is more limited (and generally more focussed on date to string conversions and vice versa while Excel's tend to be more "value- (numeric) oriented), but many of them have equivalents in some form or another.
When you're importing dates into a TI process, the data source won't be Excel itself. Even if the data was originally in an Excel worksheet which was then exported to a .csv /.cma file, dates typically won't be in Excel's serial date format but rather written as a formatted date string.
Nor can TurboIntegrator use dates which are in the internal database formats of systems like Access or SQL Server. It can only interpret values coming from any data source as either numeric values, or string values. Dates should therefore normally be brought across as strings, which allows you to parse them to obtain their individual components (year, month, day, hour, minute and second, as applicable) using the SubSt() rules function. For example, if the data source has the value 21/03/10, you could parse it and convert it to a TM1 serial date as follows:
Code: Select all
s_Day = Subst ( DateOfEntry, 1, 2);
s_Month = Subst ( DateOfEntry, 4, 2);
s_Year = Subst ( DateOfEntry, 7, 2);
s_TM1DateString = s_Year | '-' | s_Month | '-' | s_Day;
l_DateSerial = DayNo( s_TM1DateString );
To make life easier it's suggested that you ensure that the data source outputs dates in a strict date format; specifically, with 2 digits for the day and month, using leading zeros if necessary. That ensures that you can always refer to the same character positions in your SubSt() formulas.
To Get The Serial Date Of The Current Day
Excel: = Today()
TM1: There is no exact equivalent. Instead, you can either:
- Use the Now() rules function which returns the date and time, and extract the integer component of it; or
- Use the Today() rules function (which, unlike Excel's, returns a string, not a value). That will give you a string in YY-MM-DD or YYYY-MM-DD format which you can then feed to the DayNo() function.
Code: Select all
l_TodaySerial = DayNo( Today(1) );
The (1) argument isn't strictly necessary for years before 2060 (it forces the returned string to have a 4 digit year) so this could also be expressed as
Code: Select all
l_TodaySerial = DayNo( Today );
To Get A Serial Date For Any Date Which Is In String Format
Excel's functions are in large part regulated by the regional settings of the user. In Australia, for example, functions would use the DD-MM-YY format for a date argument, while in the U.S.A. they would use MM-DD-YY. To ensure consistency and eliminate regional variations as an issue, TM1 works with only one order of the date components; YY-MM-DD or, in versions 8.4 onwards, YYYY-MM-DD.
Excel: =DATEVALUE("31-12-2002")(Australian / European format)
TM1: The string has to be in the TM1 specified format (i.e., YY-MM-DD or YYYY-MM-DD), or parsed into that. You can then use the DayNo() rules function like so:
Code: Select all
l_DateSerial1 = DayNo('02-12-31');
or if you prefer:
Code: Select all
l_DateSerial2 = DayNo('2002-12-31');
Excel: =DATE(YYYY,M,D) where YYYY is the year in numeric format (must be 4 digits), M is the month (the leading 0 is optional and will be dropped), and D is the day of the month (same). If the arguments are in text format, convert them with the Val command first. (Eg, =DATE(VALUE("2010"),3,21).)
TM1: If you have the day, month and year values in string format, concatenate them with the pipe operator to create a standard TM1 date string. For example:
Code: Select all
s_Datestr = '2010' | '-' | '03' | '-' | '21';
If you have the arguments in numeric format, use the DateS function to convert them to a string first, then feed that string into DayNo like so:
Code: Select all
l_SerialDate = DayNo(DateS(2010, 3, 21));
To Get A String Showing The Date For A Given Serial Number
Excel: Either =TEXT(A1,"YY-MM-DD") or =TEXT(A1,"YYYY-MM-DD") where A1 contains the serial number that you want to get, although more typically in Excel you'll simply use the value itself and format it as a date.
TM1: Use the Date() rules function. For example:
Code: Select all
s_Date1= Date(14544);
Code: Select all
s_Date2= Date(14544, 1);
Remember that if you want this for the current day, using the Today() or Today(1) (to get a 4 digit year) function is the most direct approach.
To Get The Day, Month Or Year Component Of A Date As A Value
Excel: You have to feed a date serial number to the Day(), Month() or Year() functions. If the date is in string format, convert it using the DateValue() function (described earlier) first.
TM1: If you have the value as a serial date value, use the TimVl() rules function which allows you to extract any of the time or date components (year, month, day, hour, minute or second) from it. Alternatively you can convert the serial date's value to a string using the Date() rules function (described above) and use the Day(), Month() and Year() rules functions, but using TimVl() is more direct.
If the date is a string (using the TM1-specified formats of YY-MM-DD or YYYY-MM-DD), you can use the Day(), Month() and Year() rules functions.
To Get The Weekday Of A Date As A Value
In some cases you need to know what the day of the week is. For example, you may want a TI process to only do certain tasks from Thursday to Saturday. To that end you need to determine the current day, ideally by getting a numeric code for the day, and compare it to the range of days that you want the task to run on.
Excel: Use the =WEEKDAY() function. An optional second argument allows you to set the return value as either (1 or omitted) Sunday = 1 to Saturday=7, (2) Monday = 1 to Sunday = 7 or (3) Monday = 0 to Sunday = 6.
TM1: There isn't a built-in option for this. However we use a standard block of code which will do that:
Code: Select all
# 0 = Sunday, 1 = Monday to 6 = Saturday.
l_DayOfWeek= Mod ( DayNo( Today ) + 21915, 7);
To cover the most commonly asked questions about the equation, "What's 21,915"? This equation could also be used with Excel (with a small amount of modification) and 21,915 is the difference between Excel's serial date and TM1's. "Hang on, didn't you say that the difference was 21,916?" Yes. I also said that there was a bug in the treatment of the (non-) leap year in 1900 which throws the day of week values out by 1 day for the first two months of that year. That's why the difference.
To Return The Date Or Time As Timestamps
Sometimes you'll want to return a string representing the current date or time. In TM1 these values may be used as part of a log file name when doing an AsciiOutput, or as part of the content of such a log.
Excel: Use the =Text() function, which gives you a full range of formatting string options.
TM1: The most flexible way is to use the TimSt() rules function. That needs to be fed a serial date and/or serial time value, but if it's the current time you're after you can easily get that from the Now() rules function.
For example, to obtain a timestamp in the format YYYYMMDDHHIISS (where II stands for minutes to avoid confusion with the more commonly used M for Months) you could use:
Code: Select all
s_TimeStamp1 = TimSt (Now, '\Y\m\d\h\i\s');
For example the formula shown above will return (on the day of writing) 20100321073654
However the following:
Code: Select all
s_TimeStamp2 = TimSt (Now, '\Y \M \d \h \i \s');
The Today() rules function will return the current date and the Time() rules function will return the current time, but you have no control over the formats that are returned.
A Quick Crib Of TM1 Date Functions
Functions Returning A Serial Date Or Numeric Value
- Day. Input: A date string in TM1 format (YY-MM-DD or (8.4+) YYYY-MM-DD). Output: The day of month component of the date (1 to 31), in numeric format.
- DayNo. Input: A date string in TM1 format (YY-MM-DD or (8.4+) YYYY-MM-DD). Output: The TM1 serial date counting from 0 = 1 January 1960.
- Month. Input: A date string in TM1 format (YY-MM-DD or (8.4+) YYYY-MM-DD). Output: The month component of the date (1 to 12), in numeric format.
- Now. Input: None. Output: Serial date and time based on the current server time. Be aware that although this function returns a value with a very large number of decimal places, it is still only accurate to about a second as Duncan P pointed out. The function will return fractions of a second in its value but is only updated around once per second, making more granular values worthless.
- TimVl. Input: A TM1 serial date / time value. Output: A numeric value giving the component of the date that you specify; eg the hour, or the month. If you want a year beyond 2059 you need to specify an optional Extended Years argument.
- Year. Input: A date string in TM1 format (YY-MM-DD or (8.4+) YYYY-MM-DD). Output: The year component of the date (1960 onwards), in numeric format.
- Date. Input: A TM1 serial date value. Output: YY-MM-DD or YYYY-MM-DD (8.4 or higher).
- DateS. Input: Year, month and day in numeric format. Output: YY-MM-DD or YYYY-MM-DD (8.4 or higher).
- Time. Input: None. Output: The current server time in HH:MM format. (Does not return seconds, hours are in 24 hour format.)
- TimSt. Input: A TM1 serial date / time value. Output: A time and/or date string, the content of which depends on the flags that you set in the arguments.
- Today. Input: None. Output: The current server date in YY-MM-DD or YYYY-MM-DD format.