From 3d3c80e4fc10bb038d2d922b2712cfd33886506e Mon Sep 17 00:00:00 2001
From: pcain-usgs <pcain@usgs.gov>
Date: Tue, 6 Oct 2020 16:28:56 -0600
Subject: [PATCH 1/5] Round to nearest tenth of second

---
 geomagio/edge/RawInputClient.py       | 17 ++++++++++--
 test/edge_test/RawInputClient_test.py | 37 ++++++++++++++++++++++++++-
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/geomagio/edge/RawInputClient.py b/geomagio/edge/RawInputClient.py
index f50a93cd2..7259c2798 100644
--- a/geomagio/edge/RawInputClient.py
+++ b/geomagio/edge/RawInputClient.py
@@ -6,6 +6,7 @@ import struct
 import sys
 from datetime import datetime
 from ..TimeseriesFactoryException import TimeseriesFactoryException
+import logging
 from obspy.core import UTCDateTime
 from time import sleep
 
@@ -425,12 +426,24 @@ class RawInputClient:
             secs: int
             usecs: int
         """
+        usecs = time.microsecond / 100000
+        # round microseconds to nearest microsecond(whole number)
+        rounded_usecs = int(round(usecs, 0))
+        # add warning to log if residual microsecond values are received
+        if rounded_usecs != usecs:
+            logging.warning(
+                "residual microsecond values encountered, rounding to nearest microsecond"
+            )
+        # in the case that microseconds rounds up, account for next second
+        if rounded_usecs == 10:
+            time += 1
+            rounded_usecs = 0
+        # establish rest of date
         yr = time.year
         doy = time.datetime.timetuple().tm_yday
         secs = time.hour * 3600 + time.minute * 60 + time.second
-        usecs = time.microsecond
 
-        return (yr, doy, secs, usecs)
+        return (yr, doy, secs, rounded_usecs)
 
     def _open_socket(self):
         """Open a socket
diff --git a/test/edge_test/RawInputClient_test.py b/test/edge_test/RawInputClient_test.py
index 850f1a7df..5996fd79e 100644
--- a/test/edge_test/RawInputClient_test.py
+++ b/test/edge_test/RawInputClient_test.py
@@ -1,6 +1,8 @@
 """Tests for RawInputClient.py"""
 
 import numpy
+from datetime import datetime
+import logging
 from obspy.core import Stats, Trace, UTCDateTime
 from geomagio.edge import EdgeFactory, RawInputClient
 from numpy.testing import assert_equal
@@ -56,7 +58,7 @@ def test_raw_input_client():
 
 
 def test__get_tag():
-    """edge_test.RawInputClient_test.test_raw_input_client()"""
+    """edge_test.RawInputClient_test.test__get_tag()"""
     network = "NT"
     station = "BOU"
     channel = "MVH"
@@ -72,3 +74,36 @@ def test__get_tag():
     )
     tag_send = client._get_tag()
     assert_equal(tag_send is not None, True)
+
+
+def test__get_time_values(caplog):
+    """edge_test.RawInputClient_test.test__get_time_values()"""
+    network = "NT"
+    station = "BOU"
+    channel = "MVH"
+    location = "R0"
+    client = MockRawInputClient(
+        tag="tag",
+        host="host",
+        port="port",
+        station=station,
+        channel=channel,
+        location=location,
+        network=network,
+    )
+    # define expected date from _get_time_values
+    expected_time = UTCDateTime(2020, 10, 7, 0, 0, 0, 0)
+    # define date with residual microseconds
+    residual_time = UTCDateTime(2020, 10, 6, 23, 59, 59, 999999)
+    r_yr, r_doy, r_secs, r_usecs = client._get_time_values(residual_time)
+    # check if input microsecond value changes within function
+    assert_equal(
+        caplog.text,
+        "WARNING  root:RawInputClient.py:434 residual microsecond values encountered, rounding to nearest microsecond\n",
+    )
+    e_yr, e_doy, e_secs, e_usecs = client._get_time_values(expected_time)
+    # test if residual result matches expected result
+    assert_equal(e_yr, r_yr)
+    assert_equal(e_doy, r_doy)
+    assert_equal(e_secs, r_secs)
+    assert_equal(e_usecs, r_usecs)
-- 
GitLab


From 99e182fb926ff04dfc5f28009c3f26f3919c1c17 Mon Sep 17 00:00:00 2001
From: pcain-usgs <pcain@usgs.gov>
Date: Tue, 6 Oct 2020 16:41:19 -0600
Subject: [PATCH 2/5] get message from caplog

---
 test/edge_test/RawInputClient_test.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/test/edge_test/RawInputClient_test.py b/test/edge_test/RawInputClient_test.py
index 5996fd79e..73f2e1984 100644
--- a/test/edge_test/RawInputClient_test.py
+++ b/test/edge_test/RawInputClient_test.py
@@ -97,9 +97,10 @@ def test__get_time_values(caplog):
     residual_time = UTCDateTime(2020, 10, 6, 23, 59, 59, 999999)
     r_yr, r_doy, r_secs, r_usecs = client._get_time_values(residual_time)
     # check if input microsecond value changes within function
+    message = caplog.messages[0]
     assert_equal(
-        caplog.text,
-        "WARNING  root:RawInputClient.py:434 residual microsecond values encountered, rounding to nearest microsecond\n",
+        message,
+        "residual microsecond values encountered, rounding to nearest microsecond",
     )
     e_yr, e_doy, e_secs, e_usecs = client._get_time_values(expected_time)
     # test if residual result matches expected result
-- 
GitLab


From 2571fc3fa9803cdf1601531a7774e1d883306a83 Mon Sep 17 00:00:00 2001
From: pcain-usgs <pcain@usgs.gov>
Date: Wed, 7 Oct 2020 12:08:04 -0600
Subject: [PATCH 3/5] Update rounding to microseconds, move method, split tests

---
 geomagio/TimeseriesUtility.py         | 24 +++++++++++
 geomagio/edge/RawInputClient.py       | 26 ++++++------
 test/TimeseriesUtility_test.py        | 13 ++++++
 test/edge_test/RawInputClient_test.py | 60 +++++++++++++++++++--------
 4 files changed, 92 insertions(+), 31 deletions(-)

diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py
index c40ba8609..9aa99a895 100644
--- a/geomagio/TimeseriesUtility.py
+++ b/geomagio/TimeseriesUtility.py
@@ -535,3 +535,27 @@ def pad_and_trim_trace(trace, starttime, endtime):
             trace.data = numpy.concatenate(
                 [trace.data, numpy.full(cnt, numpy.nan, dtype=numpy.float64)]
             )
+
+
+def round_usecs(time):
+    """Rounds residual microseconds to milliseconds.
+
+    Parameters
+    ----------
+    time: UTCDateTime
+        time containing microsecond values
+
+    Returns
+    ----------
+    time: UTCDateTime
+        time containing rounded(or non-rounded) microsecond values
+    """
+    usecs = time.microsecond / 1000
+    # round microseconds to nearest millisecond
+    rounded_usecs = int(round(usecs, 0)) * 1000
+    # reset microseconds to 0 at top of second, add second to input time
+    if rounded_usecs > 999000:
+        rounded_usecs = 0
+        time += 1
+    time.microsecond = rounded_usecs
+    return time
diff --git a/geomagio/edge/RawInputClient.py b/geomagio/edge/RawInputClient.py
index 7259c2798..e6483c7fa 100644
--- a/geomagio/edge/RawInputClient.py
+++ b/geomagio/edge/RawInputClient.py
@@ -6,6 +6,7 @@ import struct
 import sys
 from datetime import datetime
 from ..TimeseriesFactoryException import TimeseriesFactoryException
+from ..TimeseriesUtility import round_usecs
 import logging
 from obspy.core import UTCDateTime
 from time import sleep
@@ -415,7 +416,7 @@ class RawInputClient:
         """
         PARAMETERS
         ----------
-        time: UTCDateTime
+        input_time: UTCDateTime
             time of the first sample
 
         RETURNS
@@ -426,24 +427,21 @@ class RawInputClient:
             secs: int
             usecs: int
         """
-        usecs = time.microsecond / 100000
-        # round microseconds to nearest microsecond(whole number)
-        rounded_usecs = int(round(usecs, 0))
-        # add warning to log if residual microsecond values are received
-        if rounded_usecs != usecs:
+
+        # check for presence of residual microseconds
+        if (time.microsecond / 1000).is_integer() == False:
+            # create warning message when rounding is necessary
             logging.warning(
-                "residual microsecond values encountered, rounding to nearest microsecond"
+                "residual microsecond values encountered, rounding to nearest millisecond"
             )
-        # in the case that microseconds rounds up, account for next second
-        if rounded_usecs == 10:
-            time += 1
-            rounded_usecs = 0
-        # establish rest of date
+            # round residual microsecond values
+            time = round_usecs(time)
+
         yr = time.year
         doy = time.datetime.timetuple().tm_yday
         secs = time.hour * 3600 + time.minute * 60 + time.second
-
-        return (yr, doy, secs, rounded_usecs)
+        usecs = time.microsecond
+        return (yr, doy, secs, usecs)
 
     def _open_socket(self):
         """Open a socket
diff --git a/test/TimeseriesUtility_test.py b/test/TimeseriesUtility_test.py
index 827b0afec..512db7edf 100644
--- a/test/TimeseriesUtility_test.py
+++ b/test/TimeseriesUtility_test.py
@@ -449,6 +449,19 @@ def test_pad_and_trim_trace_fixfloat():
     )
 
 
+def test_round_usecs():
+    """TimeseriesUtility_test.test_round_usecs()
+    This tests whether microsecond values are rounded or
+    not depending on residual microsecond values
+    """
+    # test case with no residual microseconds
+    time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00Z"))
+    assert_equal(time, UTCDateTime("2020-10-07T00:00:00Z"))
+    # test case with residual microseconds
+    time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00.995600Z"))
+    assert_equal(time, UTCDateTime("2020-10-07T00:00:00.996000Z"))
+
+
 def _create_trace(data, channel, starttime, delta=60.0):
     stats = Stats()
     stats.channel = channel
diff --git a/test/edge_test/RawInputClient_test.py b/test/edge_test/RawInputClient_test.py
index 73f2e1984..ef21290ac 100644
--- a/test/edge_test/RawInputClient_test.py
+++ b/test/edge_test/RawInputClient_test.py
@@ -91,20 +91,46 @@ def test__get_time_values(caplog):
         location=location,
         network=network,
     )
-    # define expected date from _get_time_values
-    expected_time = UTCDateTime(2020, 10, 7, 0, 0, 0, 0)
-    # define date with residual microseconds
-    residual_time = UTCDateTime(2020, 10, 6, 23, 59, 59, 999999)
-    r_yr, r_doy, r_secs, r_usecs = client._get_time_values(residual_time)
-    # check if input microsecond value changes within function
-    message = caplog.messages[0]
-    assert_equal(
-        message,
-        "residual microsecond values encountered, rounding to nearest microsecond",
-    )
-    e_yr, e_doy, e_secs, e_usecs = client._get_time_values(expected_time)
-    # test if residual result matches expected result
-    assert_equal(e_yr, r_yr)
-    assert_equal(e_doy, r_doy)
-    assert_equal(e_secs, r_secs)
-    assert_equal(e_usecs, r_usecs)
+
+    # test rounding up of microsecond value
+    time = UTCDateTime("2020-10-07T00:00:15.196855Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 15)
+    assert_equal(usecs, 197000)
+    # test rounding down of microsecond value
+    time = UTCDateTime("2020-10-07T00:00:15.196455Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 15)
+    assert_equal(usecs, 196000)
+    # test top of second adjustment
+    time = UTCDateTime("2020-10-07T00:00:00.999999Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 1)
+    assert_equal(usecs, 0)
+    # test top of day adjustment
+    time = UTCDateTime("2020-10-07T23:59:59.999999Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 282)
+    assert_equal(secs, 0)
+    assert_equal(usecs, 0)
+    # assert if previous 4 tests generate 4 warning messages
+    assert_equal(len(caplog.messages), 4)
+
+    # clear warnings from log
+    caplog.clear()
+    # test ideal case
+    time = UTCDateTime("2020-10-07T00:00:00.232000Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 0)
+    assert_equal(usecs, 232000)
+    # assert if previous test does not generate a warning message
+    assert_equal(len(caplog.messages), 0)
-- 
GitLab


From f7df44998934c94f43e93d1c2414d80e99414b02 Mon Sep 17 00:00:00 2001
From: Jeremy M Fee <jmfee@usgs.gov>
Date: Wed, 7 Oct 2020 18:47:17 +0000
Subject: [PATCH 4/5] Update `usecs` value to match variable name. Add test for
 round-to-next-second branch.

---
 geomagio/TimeseriesUtility.py  | 4 ++--
 test/TimeseriesUtility_test.py | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py
index 9aa99a895..adecbcb06 100644
--- a/geomagio/TimeseriesUtility.py
+++ b/geomagio/TimeseriesUtility.py
@@ -550,9 +550,9 @@ def round_usecs(time):
     time: UTCDateTime
         time containing rounded(or non-rounded) microsecond values
     """
-    usecs = time.microsecond / 1000
+    usecs = time.microsecond
     # round microseconds to nearest millisecond
-    rounded_usecs = int(round(usecs, 0)) * 1000
+    rounded_usecs = int(round(usecs / 1000, 0)) * 1000
     # reset microseconds to 0 at top of second, add second to input time
     if rounded_usecs > 999000:
         rounded_usecs = 0
diff --git a/test/TimeseriesUtility_test.py b/test/TimeseriesUtility_test.py
index 512db7edf..a664b2e28 100644
--- a/test/TimeseriesUtility_test.py
+++ b/test/TimeseriesUtility_test.py
@@ -460,6 +460,9 @@ def test_round_usecs():
     # test case with residual microseconds
     time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00.995600Z"))
     assert_equal(time, UTCDateTime("2020-10-07T00:00:00.996000Z"))
+    # test case with rounding to next second
+    time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00.9995Z"))
+    assert_equal(time, UTCDateTime("2020-10-07T00:00:01.000Z"))
 
 
 def _create_trace(data, channel, starttime, delta=60.0):
-- 
GitLab


From a92374167549afc62952e6e8c7f928dce85bfc6f Mon Sep 17 00:00:00 2001
From: Jeremy M Fee <jmfee@usgs.gov>
Date: Wed, 7 Oct 2020 19:33:53 +0000
Subject: [PATCH 5/5] Use UTCDateTime.replace method for rounded_usecs.

instead of deprecated microsecond property setter.
---
 geomagio/TimeseriesUtility.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py
index adecbcb06..418bbe9c1 100644
--- a/geomagio/TimeseriesUtility.py
+++ b/geomagio/TimeseriesUtility.py
@@ -557,5 +557,6 @@ def round_usecs(time):
     if rounded_usecs > 999000:
         rounded_usecs = 0
         time += 1
-    time.microsecond = rounded_usecs
+    if rounded_usecs != usecs:
+        time = time.replace(microsecond=rounded_usecs)
     return time
-- 
GitLab