Domino on Linux/Unix, Troubleshooting, Best Practices, Tips and more ...

alt

Daniel Nashed

Notes Timedate explained

Daniel Nashed – 4 January 2025 15:47:17

There have been a couple of partner blog posts speculating about the background of the recent Domino 13.12.2024 problem, which might be a bit misleading.
For the background of what happened in detail and how HCL addressed the problem please wait for the official technote update.

But what I can tell is that HCL fixed it on a lower level function addressing all functionality in Domino and business partner applications using the effected functionality.

This means the only safe way is to apply the Interim fix provided by HCL for all supported releases including the extended support versions!

What I also can state is that all Notes TIMEDATE functionality is working as intended and are designed to handle date times from 1.1.1 to the end of all times.

Sorry for a long blog post like this. But I hope this helps to understand the Notes TIMEDATE a bit better.

The takeaway is that the Notes TIMEDATE works as intended for now and the far future.



I am briefly comparing the approach Notes took compared to the Linux/UNIX date, but will not go into all details about the Linux EPOCH date -- which would be maybe something for a future blog post.
But comparing two implementations makes it easier to understand.


To understand how time date functionality works it makes sense to look into the lowest level of presentation, which you can find in the C-API toolkit.

Notes/Domino uses the TIMEDATE structure to internally represent time.


The time is stored always in GMT/UTC and contains timezone and summer/winter (DST) information of how the data was stored.
The internal representation is always in UTC to ensure all operations are compatible for all timezones in summer and winter time and that no conversion is needed to compare them.
That also means everything displayed is only a conversion based on UTC with the choosen timezone at run-time.


typedef struct tagTIMEDATE

{

DWORD Innards[2];

} TIMEDATE;



The structure is a binary representation which should not be manipulated low level. It contains the date and time + zone + DST flag.


Linux time


In contrast the Linux timedate is a single number of seconds since 1.1.1970 midnight UTC, which will wrap around in 2038 in it's 32bit implementation.

The good news here is that Linux glibc has switched to a 64bit integer when 64bit Linux was introduced.
So today you will hopefully all run Linux environments with a time_t with 64bit which will work far beyond year 2028.

But there might be many older legacy environments without a 64bit time_t format which are limited to the 32bit integer design of the Linux time which can only hold around 68 years in seconds.


Notes TIMEDATE functions


The TIMEDATE can be converted to a TIME structure very similar to how Linux converts it's internal format to a human readable structure.

This structure is mainly intended for input/output.
There are also conversion functions to format a date based on time settings to format and read from/string buffers.



Structure


typedef struct {

int year;       /* 1-32767 */

int month;      /* 1-12 */

int day;        /* 1-31 */

int weekday;    /* 1-7, Sunday is 1 */

int hour;       /* 0-23 */

int minute;     /* 0-59 */

int second;     /* 0-59 */

int hundredth;  /* 0-99 */

int dst;        /* FALSE or TRUE */

int zone;       /* -11 to +11 */

TIMEDATE GM;

} TIME;



Most relevant functions to handle conversions to help understanding the TIMEDATE.



BOOL LNPUBLIC TimeLocalToGM(TIME far *Time);

BOOL LNPUBLIC TimeGMToLocal(TIME far *Time);

BOOL LNPUBLIC TimeGMToLocalZone(TIME far *Time);


Conversions take either the GM TIMEDATE format to convert it to individual values or the other way round.

The internally structure of a TIMEDATE contains two main components:


1.The elapsed in days since a certain EPOCH time which is different then the Linux EPOC and much lower.

2. Number of 1/100 seconds since midnight UTC/GMT



Both values are DWORD values which can hold the hundred seconds since midnight and sufficient number of days until the end of all times.

The Notes EPOC value is also way below 1.1.1 so it can handle dates earlier than 1900.


So both assumptions that Domino's EPOCH date is 1.1.1970 or 1.1.1900 are wrong.



TIMEDATE implementation benefits and facts


The design of the TIMEDATE brings a couple of benefits which makes it more flexible then other time formats.
  1. Works since 1.1.1 until very far into the future
  2. Supports timezone and summer/winter time
  3. Compatible between timezones (this is specially important for replication)
  4. Resolution is 1/100 second in contrast to for example Linux where the resolution is 1 second


How to make sure you are not running into issues in your applications


All the functionality described here is part of the original design of Notes and work unchanged since then.

This includes a function to calculate the difference between two TIMEDATES in seconds.


The resulting number if a LONG, which is a 32bit signed integer. This integer value can represent the delta in seconds for a round 68 years.

Usually this isn't a problem in applications and in the time when Notes was designed there was no 64bit Integer value.

68 years in seconds was sufficient and you could not calculate with larger numbers.


When calculating larger numbers you had always to switch to a float value else the resulting value would have wrapped.
Notes also provides a function returning the difference in seconds in a float value returning a NUMBER.

This result will allow longer time intervals but might loose precision in detail.



Compare without calculating the difference


In many cases you don't need to know the difference. You just want to check which date is younger.
The TimeDateCompare() works independently of the time difference and is the safer bet when comparing.


In future Notes should introduce a TIMEDATE difference routine returning LONG64.
This would help to calculate longer time spans without switching to floating point operations, which work well for larger numbers but might loose precision.


Today Notes and Domino is only available for 64bit and all platforms support 64bit integer data types. So it would be possible to implement a LONG64 version of the time difference function.

But until then you just need to take care of the LONG (32bit signed integer limits of 68 years -- more exact 2147483647 seconds).


-----


Side Note:


Usually you would not use time functionality for earlier dates than 1900.

I have tested with 1.1.1 but I would assume this isn't officially intended to work since that time.

There is a special leap year rule that might not have been taken into account those special rules omitting certain leap years (see reference below).

I have not spent any time validating that for earlier times or later times. But maybe I will look into this in another blog post.


"The Gregorian calendar therefore omits 3 leap days every 400 years, which is the length of its leap cycle.

This is done by omitting 29 February in the 3 century years (multiples of 100) that are not multiples of 400.

The years 2000 and 2400 are leap years, but not 1700, 1800, 1900, 2100, 2200, and 2300."

--- Ref:
https://en.wikipedia.org/wiki/Leap_year


References:


I would also have a small C-API program to play around with the Notes TIMEDATE and to see how the functionality works.

But I am leaving that out here. If anyone is interested, I could share it including a Linux makefile.



LONG LNPUBLIC TimeDateDifference(const TIMEDATE far *t1, const TIMEDATE far *t2);

void LNPUBLIC TimeDateDifferenceFloat(const TIMEDATE far *t1, const TIMEDATE far *t2, NUMBER far *difference);

int  LNPUBLIC TimeDateCompare(const TIMEDATE far *t1, const TIMEDATE far *t2);



Extract from global.h


BYTE    = unsigned  8 bit integer

WORD    = unsigned 16 bit integer

DWORD   = unsigned 32 bit integer

LONG    = signed   32 bit integer


DWORD64 = unsigned 64 bit integer (on 32 and 64 bit operating systems)

LONG64  = signed 64 bit integer (on 32 and 64 bit operating systems)

NUMBER  = A platform-independent IEEE-64 floating point number



Number 2147483647


The number 2147483647 (or hexadecimal 7FFFFFFF) is the maximum positive value for a 32-bit signed binary integer

More information -->  
https://en.wikipedia.org/wiki/2,147,483,647

----

Update 6. January 2025

There is an interesting technote Julian came up with. I wasn't aware of those details about the historic dates.
But this really matches what the C-API provides.

KB0039502  -- Error "Unable to interpret time or date" when entering specific dates from the year 1752


https://support.hcl-software.com/csm?id=kb_article&sysparm_article=KB0039502

Now you know the base year for the Notes TIMEDATE: year 4713 B.C.

If you take the number of days from today's date and divide them by 365,25 + take into account that there was no year zero, this matches the start date 4713 B.C.

So in contrast to most implementations taking 1970 or 1900 as the base, Notes uses the Julian date EPOCH.  

Now looking into it closer, there is a call to extract the Julian date:

DWORD LNPUBLIC TimeExtractJulianDate(const TIMEDATE far *Time);

Which turns out to be the TIMEDATE Innards value for the days.

See details about the Julian date here --> https://en.wikipedia.org/wiki/Julian_day



Links

    Archives


    • [HCL Domino]
    • [Domino on Linux]
    • [Nash!Com]
    • [Daniel Nashed]