Correct Global Day Conversion functions for ROOT::TDatime

Dear Rooters,

I recently tried to use the TDatime class to perform plotting and numerical operations with dates but I found out that the functions

TDatime::GetDateFromGlobalDay()

and

TDatime::GetGlobalDayFromDate()

are not implemented in root version 6 and the implementation present in older headers seems to not be correct.

Attached an implementation that I wrote within my framework to go around the issue that seems to be working with all performed tests. You will notice that the conversion tot he Gregorian calendar is performed with a cumulative array for the number of days per month of year. The input and output character string format is compatible with the TDatetime class.
Hope to help get this implemented in the next version.

unsigned int RadBase::GetGlobalDayFromDate(std::string date)
{
    // date is in form yyyymmdd
    unsigned int ui_date = atoi(date.c_str());
    unsigned int dy = ui_date / 10000;                     // Extract year
    unsigned int dm = (ui_date - dy * 10000) / 100;        // Extract month
    unsigned int dd = (ui_date - dy * 10000 - dm * 100);   // Extract day of the month
    dm = (dm + 9) % 12;                                    // Shift month to start with 0 for March and add up to 
                                                           // 11 for February to correctly acount for leap years
    unsigned int df = dy;                                  // Calculate the corrections year
    if (dm / 10 > 0) df = df - dm/10;
    // else dm--;
    unsigned int cumulday[12] = {59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 31};
    return (dy-1)*365 + df/4 - df/100 + df/400 + cumulday[dm] + dd;
}
Int_t RadBase::GetDateFromGlobalDay(Int_t day)
{
   unsigned int cumulday[12] = {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
   unsigned int y = (unsigned int)(day/365) + 1;
   int d1 = day - ((y-1)*365 + (y-1)/4 - (y-1)/100 + (y-1)/400);
   while (d1 < 0)
         {
          y--;
          d1 = day - ((y-1)*365 + (y-1)/4 - (y-1)/100 + (y-1)/400);
         }
   unsigned int m = 0;
   if (d1 != 0)
      {
       unsigned int y1 = y;
       for (unsigned int k = 0; k < 12; k++)
           {
            if (d1 <= cumulday[k])
               {
                m = k + 1;
                if (m < 3) y1 = y - 1;
                if (k > 0) d1 = day - ((y-1)*365 + y1/4 - y1/100 + y1/400) - cumulday[k-1];
                else d1 = day - ((y-1)*365 + y1/4 - y1/100 + y1/400);
                if (d1 == 0) // we are at the last day of the month
                   {
                    unsigned int mday[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
                    d1 = mday[m - 1];
                    if (m == 2)  // correction for february
                       {
                        if (y1 % 4) d1++;
                        if (y1 % 100) d1--;
                        if (y1 % 400) d1++;
                       }
                   }
                break;
               }
           }
      }
   else { m = 12; d1 = 31; y--; } // we are at the last day of the year
   if (GetDebug()) std::cout << __FUNCTION__ << " INFO: " << day << "\t" << d1 
                             << "\t" << y << "\t" << m << "\t" << d1 << std::endl;
   return y * 10000 + m * 100 + d1;
}
// --------------------------------------------------------------------------------------------------------------

Functions are part of the radiation damage estimation framework available here:
Radiation Damage Calculation


Please read tips for efficient and successful posting and posting code

_ROOT Version: 6.24
_Platform: Windows 10
_Compiler: MSVC12


Seems to me they are implemented in ROOT 6:

https://root.cern/doc/master/classTDatime.html#a15fcdce6ba55533937291f243403a5b2

The implementation is definitely wrong.
Please try:

TDatime *now = new TDatime()
now->GetDate()

should return today’s date: 20230309

Then try the following:

now->GetGlobalDayFromDate(now->GetDate())

It should return 738893
Normally if I do then the inverse transform i should find back the same date but:

now->GetDateFromGlobalDay(7388993)

returns 201231222 and not 20230309.

Indeed I get this:

root [0] TDatime *now = new TDatime()
(TDatime *) 0x600000734220
root [1] now->GetDate()
(int) 20230309
root [2] now->GetGlobalDayFromDate(now->GetDate())
(int) 738893
root [3] now->GetDateFromGlobalDay(7388993)
(int) 202300717
root [4] 

As you said the last value is not 20230309. I do not get the same value as you btw. Some investigations are needed.

Interesting, I re-verified and I always get 201231222 with root 6.24.00 in windows. If the implementation is the one in the referenced files in the twiki, although wrong since there are a number of logic mistakes, it should not change from system to system…

Since I had to do an analysis project urgently though I re-wrote the functions in a way that the transformation is reversible at any system and for any date, hence I posted them above.

Well…

void globalday() {
   auto *now = new TDatime();
   int in = now->GetDate();
   printf ("Now               = %d\n",in);

   int GlobalDayFromDate = now->GetGlobalDayFromDate(in);
   printf("GlobalDayFromDate = %d\n",GlobalDayFromDate);

   int DateFromGlobalDay = now->GetDateFromGlobalDay(GlobalDayFromDate);
   printf("DateFromGlobalDay = %d\n",DateFromGlobalDay);

   if (in == DateFromGlobalDay) printf("DateFromGlobalDay is fine\n");
   else                         printf("DateFromGlobalDay is wrong\n");
}

gives:

root [0] .x globalday.C
Now               = 20230309
GlobalDayFromDate = 738893
DateFromGlobalDay = 20230309
DateFromGlobalDay is fine
root [1] 

Looks ok…

Interesting… Here is what I get on Windows:

Now               = 20230309
GlobalDayFromDate = 738893
DateFromGlobalDay = 20100826

I forgot to mention I am on Mac with master.

FYI, there is now a PR #12461 fixing this issue on Windows

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.