fru-parser: Remove the use of mktime

mktime get the time in localtime and will mess up the time if the
timezone != UTC. The FRU data should be the raw data in UTC and the
reader will convert to localtime at their end.

This issue is detected when we change the timezone to PST/PDT with
https://gerrit.openbmc.org/c/openbmc/openbmc/+/58293
and the FRU EEPROM time does not match the expected Mfg Date anymore.

Used the timestamp of 1/1/1996 UTC directly.

Data Reader:
https://gerrit.openbmc.org/c/openbmc/phosphor-host-ipmid/+/58466

Tested:
Fru EEPROM Mfg Time now is in the same (after timezone conversion)
between the raw data and FRU output.

Change-Id: I9d2d045f037d1976e45ed4e0c1857bb8d5ebc06d
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/frup.cpp b/frup.cpp
index dddeee4..c30ea24 100644
--- a/frup.cpp
+++ b/frup.cpp
@@ -81,6 +81,8 @@
 #define OPENBMC_VPD_KEY_LEN 64
 #define OPENBMC_VPD_VAL_LEN 512
 
+constexpr long fruEpochMinutes = 820454400;
+
 struct ipmi_fru_field
 {
     uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX];
@@ -177,7 +179,7 @@
 
     t = mfg_date_time;
     gmtime_r(&t, &tm);
-    s = strftime(timestr, len, "%F - %H:%M:%S", &tm);
+    s = strftime(timestr, len, "%F - %H:%M:%S UTC", &tm);
 
     return s;
 }
@@ -329,7 +331,6 @@
     ipmi_fru_field_t* board_custom_fields, unsigned int board_custom_fields_len)
 {
     const uint8_t* areabufptr = (const uint8_t*)areabuf;
-    uint32_t mfg_date_time_tmp = 0;
     unsigned int area_offset = 0;
     unsigned int custom_fields_index = 0;
     uint8_t number_of_data_bytes;
@@ -360,46 +361,17 @@
 
     if (mfg_date_time)
     {
-        struct tm tm;
-        time_t t;
-
-        /* mfg_date_time is little endian - see spec */
-        mfg_date_time_tmp |= areabufptr[area_offset];
+        unsigned int minutes = areabufptr[area_offset];
         area_offset++;
-        mfg_date_time_tmp |= (areabufptr[area_offset] << 8);
+        minutes |= (areabufptr[area_offset] << 8);
         area_offset++;
-        mfg_date_time_tmp |= (areabufptr[area_offset] << 16);
+        minutes |= (areabufptr[area_offset] << 16);
         area_offset++;
 
-        /* mfg_date_time is in minutes, so multiple by 60 to get seconds */
-        mfg_date_time_tmp *= 60;
-
-        /* posix says individual calls need not clear/set all portions of
-         * 'struct tm', thus passing 'struct tm' between functions could
-         * have issues.  so we need to memset.
+        /* In fru, epoch is 0:00 hrs 1/1/96 == 820454400
+         * Remove it directly and remove the time conversion.
          */
-        memset(&tm, '\0', sizeof(struct tm));
-
-        /* in fru, epoch is 0:00 hrs 1/1/96
-         *
-         * so convert into ansi epoch
-         */
-
-        tm.tm_year = 96; /* years since 1900 */
-        tm.tm_mon = 0;   /* months since january */
-        tm.tm_mday = 1;  /* 1-31 */
-        tm.tm_hour = 0;
-        tm.tm_min = 0;
-        tm.tm_sec = 0;
-        tm.tm_isdst = -1;
-
-        if ((t = mktime(&tm)) == (time_t)-1)
-        {
-            goto cleanup;
-        }
-
-        mfg_date_time_tmp += (uint32_t)t;
-        (*mfg_date_time) = mfg_date_time_tmp;
+        (*mfg_date_time) = fruEpochMinutes + static_cast<long>(minutes) * 60;
     }
     else
         area_offset += 3;