From a9a0ae7cc86a3877c9f4a1a790f9c497c8919d8c Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:22:26 -0600
Subject: [PATCH 01/11] #2155 Keep the same login but to avoid the SonarQube
 findings

---
 src/basic/vx_util/data_line.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/basic/vx_util/data_line.cc b/src/basic/vx_util/data_line.cc
index ca1e8db145..f7692fdeb0 100644
--- a/src/basic/vx_util/data_line.cc
+++ b/src/basic/vx_util/data_line.cc
@@ -334,7 +334,7 @@ if ( ! read_single_text_line(ldf) )  { clear();  return ( 0 ); }
 
 size_t len, tpos = std::string::npos;
 
-if (!Line.find_first_not_of(Delimiter)) { // no leading delimiter
+if (0 == Line.find_first_not_of(Delimiter)) { // no leading delimiter
     ++count;
     Offset.push_back(pos);
     Items.push_back(Line.substr(pos, Line.find_first_of(Delimiter, pos) - pos));

From 96a1a04f37a8f59a2716b3250a77cb7c9ca1aa58 Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:33:48 -0600
Subject: [PATCH 02/11] #2155 SonarQube: correct hdr_arr_len for header, Check
 obs_arr is OBS_ARRAY_LEN (5)

---
 src/tools/dev_utils/met_nc_file.cc | 129 +++++++++++++++--------------
 1 file changed, 65 insertions(+), 64 deletions(-)

diff --git a/src/tools/dev_utils/met_nc_file.cc b/src/tools/dev_utils/met_nc_file.cc
index 20e11cbeaf..4ed5bed21c 100644
--- a/src/tools/dev_utils/met_nc_file.cc
+++ b/src/tools/dev_utils/met_nc_file.cc
@@ -101,13 +101,13 @@ bool MetNcFile::readFile(const int desired_grib_code,
   _nhdrDim   = &nhdrDim;
   _nobsDim   = &nobsDim;
   _strlDim   = &strlDim;
-  
+
   hdrArrVar  = get_nc_var(_ncFile, "hdr_arr");
   hdrTypeVar = get_nc_var(_ncFile, "hdr_typ");
   hdrSidVar  = get_nc_var(_ncFile, "hdr_sid");
   hdrVldVar  = get_nc_var(_ncFile, "hdr_vld");
   obsArrVar  = get_nc_var(_ncFile, "obs_arr");
-  
+
   _hdrArrVar  = &hdrArrVar ;
   _hdrTypeVar = &hdrTypeVar;
   _hdrSidVar  = &hdrSidVar ;
@@ -161,16 +161,16 @@ bool MetNcFile::readFile(const int desired_grib_code,
   // Loop through the observations, saving the ones that we are
   // interested in
 
-  
+
 
   //int buf_size = ((nobs_count > DEF_NC_BUFFER_SIZE) ? DEF_NC_BUFFER_SIZE : (nobs_count));
   int buf_size = obs_count;
   int hdr_buf_size = hdr_count;
-  
+
   //
   // Allocate space to store the data
   //
-  
+
   char hdr_typ_str_full[hdr_buf_size][typ_len];
   char hdr_sid_str_full[hdr_buf_size][sid_len];
   char hdr_vld_str_full[hdr_buf_size][vld_len];
@@ -182,7 +182,7 @@ bool MetNcFile::readFile(const int desired_grib_code,
 
   long offsets[2] = { 0, 0 };
   long lengths[2] = { 1, 1 };
-  
+
   lengths[0] = hdr_buf_size;
 
   //
@@ -224,10 +224,10 @@ bool MetNcFile::readFile(const int desired_grib_code,
         << "trouble getting hdr_arr\n\n";
     exit(1);
   }
-  
+
   //for(int i_start=0; i_start<nobs_count; i_start+=buf_size) {
   //   buf_size = ((nobs_count-i_start) > DEF_NC_BUFFER_SIZE) ? DEF_NC_BUFFER_SIZE : (nobs_count-i_start);
-     
+
   offsets[0] = 0;
   lengths[0] = buf_size;
   lengths[1] = obs_arr_len;
@@ -243,81 +243,82 @@ bool MetNcFile::readFile(const int desired_grib_code,
   //   mlog << Error << "\nmain() -> trouble getting obs_arr\n\n";
   //   exit(1);
   //}
-      
-  for (unsigned int i = 0; i < GET_NC_SIZE_P(_nobsDim); ++i)
-  {
-      
-    // Copy the current observation message
-    for (int k=0; k < obs_arr_len; k++)
-       obs_arr[k] = obs_arr_block[i][k];
 
-    if (obs_arr[0] >= 1.0E10 && obs_arr[1] >= 1.0E10)
-      break;
+  if (OBS_ARRAY_LEN == obs_arr_len) {
+    for (unsigned int i = 0; i < GET_NC_SIZE_P(_nobsDim); ++i)
+    {
+
+      // Copy the current observation message
+      for (int k=0; k < obs_arr_len; k++)
+         obs_arr[k] = obs_arr_block[i][k];
 
-    // Read the current observation quality flag
+      if (obs_arr[0] >= 1.0E10 && obs_arr[1] >= 1.0E10)
+        break;
 
-    // Get the header index and variable type for this observation.
+      // Read the current observation quality flag
 
-    int hdr_index = nint(obs_arr[0]);
-    int grib_code = nint(obs_arr[1]);
+      // Get the header index and variable type for this observation.
 
-    // Check if we want to plot this variable type.
+      int hdr_index = nint(obs_arr[0]);
+      int grib_code = nint(obs_arr[1]);
 
-    if (grib_code != desired_grib_code)
-      continue;
+      // Check if we want to plot this variable type.
 
-    // Get the corresponding header message type
-    // Read the corresponding header array for this observation
-    for (int k=0; k < obs_arr_len; k++)
-       hdr_arr[k] = hdr_arr_full[hdr_index][k];
-   
-    int  str_length;
-    char message_type_buffer[max_str_len];
-    char station_id_buffer[max_str_len];
-    char hdr_vld_buffer[max_str_len];
-    // Read the corresponding header type for this observation
-    str_length = m_strlen(hdr_typ_str_full[hdr_index]);
-    if (str_length > typ_len) str_length = typ_len;
-    m_strncpy(message_type_buffer, hdr_typ_str_full[hdr_index], str_length,
-              method_name.c_str(), "message_type_buffer");
-    message_type_buffer[str_length] = bad_data_char;
+      if (grib_code != desired_grib_code)
+        continue;
 
-    // Read the corresponding header Station ID for this observation
-    str_length = m_strlen(hdr_sid_str_full[hdr_index]);
-    if (str_length > sid_len) str_length = sid_len;
-    m_strncpy(station_id_buffer, hdr_sid_str_full[hdr_index], str_length,
-              method_name.c_str(), "station_id_buffer");
-    station_id_buffer[str_length] = bad_data_char;
+      // Get the corresponding header message type
+      // Read the corresponding header array for this observation
+      for (int k=0; k < hdr_arr_len; k++)
+         hdr_arr[k] = hdr_arr_full[hdr_index][k];
 
-    // Read the corresponding valid time for this observation
-    str_length = m_strlen(hdr_vld_str_full[hdr_index]);
-    if (str_length > vld_len) str_length = vld_len;
-    m_strncpy(hdr_vld_buffer, hdr_vld_str_full[hdr_index], str_length,
-              method_name.c_str(), "hdr_vld_buffer");
-    hdr_vld_buffer[str_length] = bad_data_char;
-    
+      int  str_length;
+      char message_type_buffer[max_str_len];
+      char station_id_buffer[max_str_len];
+      char hdr_vld_buffer[max_str_len];
+      // Read the corresponding header type for this observation
+      str_length = m_strlen(hdr_typ_str_full[hdr_index]);
+      if (str_length > typ_len) str_length = typ_len;
+      m_strncpy(message_type_buffer, hdr_typ_str_full[hdr_index], str_length,
+                method_name.c_str(), "message_type_buffer");
+      message_type_buffer[str_length] = bad_data_char;
 
-    string message_type = message_type_buffer;
+      // Read the corresponding header Station ID for this observation
+      str_length = m_strlen(hdr_sid_str_full[hdr_index]);
+      if (str_length > sid_len) str_length = sid_len;
+      m_strncpy(station_id_buffer, hdr_sid_str_full[hdr_index], str_length,
+                method_name.c_str(), "station_id_buffer");
+      station_id_buffer[str_length] = bad_data_char;
 
-    if (message_type != desired_message_type)
-      continue;
+      // Read the corresponding valid time for this observation
+      str_length = m_strlen(hdr_vld_str_full[hdr_index]);
+      if (str_length > vld_len) str_length = vld_len;
+      m_strncpy(hdr_vld_buffer, hdr_vld_str_full[hdr_index], str_length,
+                method_name.c_str(), "hdr_vld_buffer");
+      hdr_vld_buffer[str_length] = bad_data_char;
 
-    // Get the corresponding header station id
-    string station_id = station_id_buffer;
 
-    if (station_id != desired_station_id)
-      continue;
+      string message_type = message_type_buffer;
 
-    // Get the corresponding header valid time
+      if (message_type != desired_message_type)
+        continue;
 
+      // Get the corresponding header station id
+      string station_id = station_id_buffer;
 
-    // If we get here, this is an observation that we want to use
+      if (station_id != desired_station_id)
+        continue;
 
-    SDObservation obs(hdr_vld_buffer, obs_arr[4]);
+      // Get the corresponding header valid time
 
-    observations.push_back(obs);
-   } // end for i
 
+      // If we get here, this is an observation that we want to use
+
+      SDObservation obs(hdr_vld_buffer, obs_arr[4]);
+
+      observations.push_back(obs);
+    } // end for i
+  }
   // Cleanup
 
   if (obs_arr) { delete [] obs_arr; obs_arr = (float *) 0; }

From 69d009e5d5323599b247296ba4a406b2ec16706b Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:34:36 -0600
Subject: [PATCH 03/11] #2155 SonarQube: release memory

---
 src/libcode/vx_seeps/seeps.cc | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/libcode/vx_seeps/seeps.cc b/src/libcode/vx_seeps/seeps.cc
index fa8ce18715..d620e781b0 100644
--- a/src/libcode/vx_seeps/seeps.cc
+++ b/src/libcode/vx_seeps/seeps.cc
@@ -121,11 +121,11 @@ SeepsClimoRecord *SeepsClimo::create_climo_record(
 ////////////////////////////////////////////////////////////////////////
 
 SeepsRecord *SeepsClimo::get_record(int sid, int month, int hour) {
-   SeepsRecord *record = 0;
+   SeepsRecord *record = NULL;
    const char *method_name = "SeepsClimo::get_record() -> ";
 
    if (seeps_ready) {
-      SeepsClimoRecord *climo_record = 0;
+      SeepsClimoRecord *climo_record = NULL;
       map<int,SeepsClimoRecord *>::iterator it;
       if (hour < 6 || hour >= 18) {
          it = seeps_score_00_map.find(sid);
@@ -199,12 +199,13 @@ float SeepsClimo::get_score(int sid, float p_fcst, float p_obs,
    float score = (float)bad_data_double;
    SeepsRecord *record = get_record(sid, month, hour);
 
-   if (record) {
+   if (NULL != record) {
       // Determine location in contingency table
       int ic = (p_obs>record->t1)+(p_obs>record->t2);
       int jc = (p_fcst>record->t1)+(p_fcst>record->t2);
 
       score = record->scores[(jc*3)+ic];
+      delete record;
    }
 
    return score;
@@ -214,10 +215,10 @@ float SeepsClimo::get_score(int sid, float p_fcst, float p_obs,
 
 SeepsScore *SeepsClimo::get_seeps_score(int sid, float p_fcst,
                                         float p_obs, int month, int hour) {
-   SeepsScore *score = 0;
+   SeepsScore *score = NULL;
    SeepsRecord *record = get_record(sid, month, hour);
 
-   if (record) {
+   if (NULL != record) {
       score = new SeepsScore();
       score->p1 = record->p1;
       score->p2 = record->p1;
@@ -227,6 +228,7 @@ SeepsScore *SeepsClimo::get_seeps_score(int sid, float p_fcst,
       score->obs_cat = (p_obs>record->t1)+(p_obs>record->t2);
       score->model_cat = (p_fcst>record->t1)+(p_fcst>record->t2);
       score->score = record->scores[(score->model_cat*3)+score->obs_cat];
+      delete record;
    }
 
    return score;

From f3557eabfef86c8013eafdd67531593418a19432 Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:35:25 -0600
Subject: [PATCH 04/11] #2155 SonarQube: initialize is_ugrid and is_vgrid

---
 src/tools/core/point_stat/point_stat.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/tools/core/point_stat/point_stat.cc b/src/tools/core/point_stat/point_stat.cc
index 0d0dbbd1b4..670fe779d4 100644
--- a/src/tools/core/point_stat/point_stat.cc
+++ b/src/tools/core/point_stat/point_stat.cc
@@ -736,6 +736,7 @@ void process_obs_file(int i_nc) {
 
    // Perform GRIB table lookups, if needed
    if(!use_var_id) conf_info.process_grib_codes();
+   is_vgrd = is_ugrd = false;
 
    int hdr_count = met_point_obs->get_hdr_cnt();
    int obs_count = met_point_obs->get_obs_cnt();

From 67a625f89c963610c8b68bdf4587a1efbf56437c Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:36:35 -0600
Subject: [PATCH 05/11] #2155 SonarQube: release array variable

---
 src/libcode/vx_data2d_nc_met/get_met_grid.cc | 2 +-
 src/libcode/vx_nc_util/grid_output.cc        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libcode/vx_data2d_nc_met/get_met_grid.cc b/src/libcode/vx_data2d_nc_met/get_met_grid.cc
index c6882057e3..a2d224fd48 100644
--- a/src/libcode/vx_data2d_nc_met/get_met_grid.cc
+++ b/src/libcode/vx_data2d_nc_met/get_met_grid.cc
@@ -783,7 +783,7 @@ long count = get_data_size(&nc_var);
 double * data_values = new double[ count ];
 get_nc_data(&nc_var, data_values);
 for(int i=0; i<count; i++)  out_na.add(data_values[i]);
-if(data_values) { delete data_values; data_values = (double *) 0; }
+if(data_values) { delete [] data_values; data_values = (double *) 0; }
 
 return;
 
diff --git a/src/libcode/vx_nc_util/grid_output.cc b/src/libcode/vx_nc_util/grid_output.cc
index 1b9a0b1ccd..4a23516d78 100644
--- a/src/libcode/vx_nc_util/grid_output.cc
+++ b/src/libcode/vx_nc_util/grid_output.cc
@@ -749,7 +749,7 @@ if ( standard_name_str )  add_att(&nc_var, standard_name_att_name, standard_name
 
 put_nc_data(&nc_var, &var_data[0], nc_dim->getSize(), 0);
 
-if ( var_data )  { delete var_data; var_data = (float *) 0; }
+if ( var_data )  { delete [] var_data; var_data = (float *) 0; }
 
    //
    //  done

From 15d9f57ddbcab4c53fb8d993911df91f74221636 Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:37:10 -0600
Subject: [PATCH 06/11] #2155 SonarQube: to avoid a finding

---
 src/tools/other/mode_time_domain/mm_engine.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/tools/other/mode_time_domain/mm_engine.cc b/src/tools/other/mode_time_domain/mm_engine.cc
index 200823c9c5..820594c8ce 100644
--- a/src/tools/other/mode_time_domain/mm_engine.cc
+++ b/src/tools/other/mode_time_domain/mm_engine.cc
@@ -268,7 +268,8 @@ if ( mlog.verbosity_level() > 5 )  {
 
    s << "Composites ...\n";
 
-   for (j=0; j<N_Composites; ++j)  {
+   int tmp_len = comp_to_eq.n();
+   for (j=0; j<tmp_len; ++j)  {
 
       s << ' ' << comp_to_eq[j];
 

From 145f73fee0b2c96d815f6c7ae8fee33678b3eeeb Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 14:37:49 -0600
Subject: [PATCH 07/11] #21565 Support month and year unit

---
 src/basic/vx_cal/is_leap_year.cc   | 73 +++++++++++++++++++++++++-----
 src/libcode/vx_nc_util/nc_utils.cc | 10 +++-
 2 files changed, 69 insertions(+), 14 deletions(-)

diff --git a/src/basic/vx_cal/is_leap_year.cc b/src/basic/vx_cal/is_leap_year.cc
index 42cc100a76..6faeeddcaf 100644
--- a/src/basic/vx_cal/is_leap_year.cc
+++ b/src/basic/vx_cal/is_leap_year.cc
@@ -90,24 +90,73 @@ void increase_one_month(int &year, int &month) {
 
 ////////////////////////////////////////////////////////////////////////
 
-unixtime add_to_unixtime(unixtime base_unixtime,
-    int sec_per_unit, double time_value, bool no_leap) {
+#define SEC_MONTH (86400*30)
+#define SEC_YEAR  (86400*30*12)
+#define DAY_EPSILON 0.00002
+
+unixtime add_to_unixtime(unixtime base_unixtime, int sec_per_unit,
+                         double time_value, bool no_leap) {
   unixtime ut;
+  int month, day, year, hour, minute, second;
   unixtime time_value_ut = (unixtime)time_value;
   double time_fraction = time_value - time_value_ut;
-  if (!no_leap || sec_per_unit != 86400) {
+  const char *method_name = "add_to_unixtime() -->";
+
+  if (sec_per_unit == SEC_MONTH || sec_per_unit == SEC_YEAR) {
+    if (time_value < 0) {
+      mlog << Error << "\n" << method_name
+           << " the negative offset (" << time_value
+           << ") is not supported for unit months and years\n\n";
+      exit(-1);
+    }
+
+    unix_to_mdyhms(base_unixtime, month, day, year, hour, minute, second);
+
+    bool day_adjusted = false;
+    int day_org, day_offset, month_offset;
+    day_org = day;
+    if (sec_per_unit == SEC_YEAR) {
+      time_fraction *= 12;  // 12 months/year
+      month_offset = (unixtime)time_fraction;
+      time_fraction -= month_offset;
+    }
+    else month_offset = time_value_ut;
+    // Add 0.00002 for the precisiton - 0.3 becomes 0.299988
+    day += ((abs(time_fraction-0.5) < DAY_EPSILON))
+           ? 14 : (int)((time_fraction+DAY_EPSILON) * 30);
+
+    for (int idx=0; idx<month_offset; idx++) {
+      increase_one_month(year, month);
+    }
+    int max_day = monthly_days[month-1];
+    if (day > max_day) {
+      day = max_day;
+      day_adjusted = true;
+      if (month == 2 && is_leap_year(year)) {
+        if (day_org == 29) day_adjusted = false;
+        day = 29;
+      }
+    }
+    ut = mdyhms_to_unix(month, day, year, hour, minute, second);
+    if (day_adjusted) {
+      mlog << Debug(2) << method_name << "adjusted day " << day_org
+           << " to " << day << " for " << year << "-" << month << "\n";
+    }
+  }
+  else if (!no_leap || sec_per_unit != 86400) {
+    // seconds, minute, hours, and day unit with leap year
     bool use_ut = true;
+    // For the precision: case 1: 1.9999 to 2
+    //                    case 2: 2.0001 to 2
+    // Other cases are as floating number
     if ((1.0 - time_fraction) < TIME_EPSILON) time_value_ut += 1;
     else if (time_fraction > TIME_EPSILON) use_ut = false;
     if (use_ut) ut = (unixtime)(base_unixtime + sec_per_unit * time_value_ut);
     else ut = (unixtime)(base_unixtime + sec_per_unit * time_value);
   }
-  else {
-    int day_offset;
-    int month, day, year, hour, minute, second;
-    
+  else {    //  no_leap year && unit = day
     unix_to_mdyhms(base_unixtime, month, day, year, hour, minute, second);
-    day_offset = day + (int)time_value;
+    int day_offset = day + (int)time_value;
     if (day_offset < 0) {
       while (day_offset < 0) {
         decrease_one_month(year, month);
@@ -125,11 +174,11 @@ unixtime add_to_unixtime(unixtime base_unixtime,
     ut = mdyhms_to_unix(month, day, year, hour, minute, second);
     if (time_fraction > (1-TIME_EPSILON) ) ut += sec_per_unit;
     else if (time_fraction > TIME_EPSILON) ut += (time_fraction * sec_per_unit);
-    mlog << Debug(5) << "add_to_unixtime() -> "
-         << unix_to_yyyymmdd_hhmmss(base_unixtime)
-         << " plus " << time_value << " days = "
-         << unix_to_yyyymmdd_hhmmss(ut) << "\n";
   }
+  mlog << Debug(5) <<  method_name
+       << unix_to_yyyymmdd_hhmmss(base_unixtime)
+       << " plus " << time_value << " days = "
+       << unix_to_yyyymmdd_hhmmss(ut) << "\n";
   
   return ut;
 }
diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc
index dcfa3a2ea3..7975828dc2 100644
--- a/src/libcode/vx_nc_util/nc_utils.cc
+++ b/src/libcode/vx_nc_util/nc_utils.cc
@@ -3226,7 +3226,7 @@ unixtime get_reference_unixtime(NcVar *time_var, int &sec_per_unit,
 
    if (get_var_units(time_var, time_unit_str)) {
       parse_cf_time_string(time_unit_str.c_str(), ref_ut, sec_per_unit);
-      no_leap_year = (86400 == sec_per_unit) ? get_att_no_leap_year(time_var) : false;
+      no_leap_year = (sec_per_day == sec_per_unit) ? get_att_no_leap_year(time_var) : false;
    }
    else {
       sec_per_unit = 1;
@@ -3304,7 +3304,13 @@ void parse_cf_time_string(const char *str, unixtime &ref_ut,
               tok.has("h"))      sec_per_unit = 3600;
       else if(tok.has("day")     ||
               tok.has("days")    ||
-              tok.has("d"))      sec_per_unit = 86400;
+              tok.has("d"))      sec_per_unit = sec_per_day;
+      else if(tok.has("month")   ||
+              tok.has("months")  ||
+              tok.has("m"))      sec_per_unit = sec_per_day * 30;
+      else if(tok.has("year")    ||
+              tok.has("years")   ||
+              tok.has("y"))      sec_per_unit = sec_per_day * 30 * 12;
       else {
          mlog << Warning << "\n" << method_name
               << "Unsupported time step in the CF convention time unit \""

From a8ca8a5e462b0427161f315fae7d3cdbec0303c2 Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Fri, 7 Oct 2022 15:52:49 -0600
Subject: [PATCH 08/11] #2155 SonarQube: to avoid a finding

---
 src/tools/other/mode_time_domain/mm_engine.cc | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/tools/other/mode_time_domain/mm_engine.cc b/src/tools/other/mode_time_domain/mm_engine.cc
index 820594c8ce..f114544f56 100644
--- a/src/tools/other/mode_time_domain/mm_engine.cc
+++ b/src/tools/other/mode_time_domain/mm_engine.cc
@@ -268,8 +268,7 @@ if ( mlog.verbosity_level() > 5 )  {
 
    s << "Composites ...\n";
 
-   int tmp_len = comp_to_eq.n();
-   for (j=0; j<tmp_len; ++j)  {
+   for (j=0; j<index_list.n(); ++j)  {
 
       s << ' ' << comp_to_eq[j];
 

From d10bff5e18d1051c09d18628c0217bc8d95e7b6c Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Wed, 12 Oct 2022 11:01:45 -0600
Subject: [PATCH 09/11] #2155Added months and years units

---
 docs/Users_Guide/data_io.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/docs/Users_Guide/data_io.rst b/docs/Users_Guide/data_io.rst
index 6c05461f43..395b66a96d 100644
--- a/docs/Users_Guide/data_io.rst
+++ b/docs/Users_Guide/data_io.rst
@@ -109,6 +109,8 @@ MET gets the valid time from the time variable and the "forecast_reference_time"
       "minutes since YYYY-MM-DD HH:MM:SS",
       "hours since YYYY-MM-DD HH:MM:SS",
       "days since YYYY-MM-DD HH:MM:SS",
+      "months since YYYY-MM-DD HH:MM:SS",
+      "years since YYYY-MM-DD HH:MM:SS",
       Accepts "Y", "YY", "YYY", "M", "D", "HH", and "HH:MM".
       "HH:MM:SS" is optional
     - "degrees_north",

From 1b1102ec7d0638c849a3d3813aa4f838351ae43f Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Wed, 12 Oct 2022 11:02:26 -0600
Subject: [PATCH 10/11] #2155 Added unit test for months and years units

---
 internal/test_unit/xml/unit_netcdf.xml | 56 ++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/internal/test_unit/xml/unit_netcdf.xml b/internal/test_unit/xml/unit_netcdf.xml
index 5c0b255240..a6e98311a9 100644
--- a/internal/test_unit/xml/unit_netcdf.xml
+++ b/internal/test_unit/xml/unit_netcdf.xml
@@ -111,4 +111,60 @@
     </output>
   </test>
 
+  <test name="netcdf_months_units">
+    <exec>&MET_BIN;/regrid_data_plane</exec>
+    <param> \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_month_376p5.nc \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_month_376p5.nc \
+      &OUTPUT_DIR;/netcdf/regrid_data_plane_months_units.nc \
+      -field 'name="t_an"; level="(0,0,*,*)";' \
+      -v 1
+    </param>
+    <output>
+      <grid_nc>&OUTPUT_DIR;/netcdf/regrid_data_plane_months_units.nc</grid_nc>
+    </output>
+  </test>
+
+  <test name="netcdf_months_units_from_day2">
+    <exec>&MET_BIN;/regrid_data_plane</exec>
+    <param> \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_month_376p5_day2.nc \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_month_376p5_day2.nc \
+      &OUTPUT_DIR;/netcdf/regrid_data_plane_months_units_day2.nc \
+      -field 'name="t_an"; level="(0,0,*,*)";' \
+      -v 1
+    </param>
+    <output>
+      <grid_nc>&OUTPUT_DIR;/netcdf/regrid_data_plane_months_units_day2.nc</grid_nc>
+    </output>
+  </test>
+
+  <test name="netcdf_months_units_to_next_month">
+    <exec>&MET_BIN;/regrid_data_plane</exec>
+    <param> \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_month_d25_3p66.nc \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_month_d25_3p66.nc\
+      &OUTPUT_DIR;/netcdf/regrid_data_plane_months_units_to_next_month.nc \
+      -field 'name="t_an"; level="(0,0,*,*)";' \
+      -v 1
+    </param>
+    <output>
+      <grid_nc>&OUTPUT_DIR;/netcdf/regrid_data_plane_months_units_to_next_month.nc</grid_nc>
+    </output>
+  </test>
+
+  <test name="netcdf_years_units">
+    <exec>&MET_BIN;/regrid_data_plane</exec>
+    <param> \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_year_3p66.nc \
+      &DATA_DIR_MODEL;/nccf/woa18_decav_t05_04_small_year_3p66.nc \
+      &OUTPUT_DIR;/netcdf/regrid_data_plane_years_units.nc \
+      -field 'name="t_an"; level="(0,0,*,*)";' \
+      -v 1
+    </param>
+    <output>
+      <grid_nc>&OUTPUT_DIR;/netcdf/regrid_data_plane_years_units.nc</grid_nc>
+    </output>
+  </test>
+
 </met_test>

From 21db2102975a9f9912685d1491539c8496080546 Mon Sep 17 00:00:00 2001
From: Howard Soh <hsoh@seneca.rap.ucar.edu>
Date: Wed, 12 Oct 2022 11:05:42 -0600
Subject: [PATCH 11/11] #2155 months and years units are proessed differently
 when the start day is the first of ythe month

---
 src/basic/vx_cal/is_leap_year.cc | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/src/basic/vx_cal/is_leap_year.cc b/src/basic/vx_cal/is_leap_year.cc
index 6faeeddcaf..efc5f66f05 100644
--- a/src/basic/vx_cal/is_leap_year.cc
+++ b/src/basic/vx_cal/is_leap_year.cc
@@ -112,22 +112,40 @@ unixtime add_to_unixtime(unixtime base_unixtime, int sec_per_unit,
 
     unix_to_mdyhms(base_unixtime, month, day, year, hour, minute, second);
 
-    bool day_adjusted = false;
-    int day_org, day_offset, month_offset;
-    day_org = day;
+    int month_offset;
+    double day_offset;
     if (sec_per_unit == SEC_YEAR) {
-      time_fraction *= 12;  // 12 months/year
+      year += time_value_ut;
+      time_fraction *= 12;      // 12 months/year
       month_offset = (unixtime)time_fraction;
       time_fraction -= month_offset;
     }
     else month_offset = time_value_ut;
-    // Add 0.00002 for the precisiton - 0.3 becomes 0.299988
-    day += ((abs(time_fraction-0.5) < DAY_EPSILON))
-           ? 14 : (int)((time_fraction+DAY_EPSILON) * 30);
 
     for (int idx=0; idx<month_offset; idx++) {
       increase_one_month(year, month);
     }
+    if (day == 1) {
+      if (abs(time_fraction-0.5) < DAY_EPSILON) day = 15;
+      else {
+        day_offset = time_fraction * 30;
+        day += (int)day_offset;
+        if (day_offset - (int)day_offset > 0.5) day++;
+      }
+    }
+    else {
+      day_offset = time_fraction * 30;
+      time_value_ut = (int)day_offset;
+      day += time_value_ut;
+      if (day_offset - time_value_ut > 0.5) day++;
+      if (day > 30) {
+         day -= 30;
+         increase_one_month(year, month);
+      }
+    }
+
+    int day_org = day;
+    bool day_adjusted = false;
     int max_day = monthly_days[month-1];
     if (day > max_day) {
       day = max_day;