diff --git a/include/core/CRapidJsonWriterBase.h b/include/core/CRapidJsonWriterBase.h index 10978efbba..ae6b3f1bb0 100644 --- a/include/core/CRapidJsonWriterBase.h +++ b/include/core/CRapidJsonWriterBase.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -192,6 +193,11 @@ class CRapidJsonWriterBase : public JSON_WRITERInt64(CTimeUtils::toEpochMs(t)); + } //! Push a constant string into a supplied rapidjson object value //! \p[in] value constant string @@ -400,8 +406,7 @@ class CRapidJsonWriterBase : public JSON_WRITERaddMember(fieldName, v, obj); } @@ -547,7 +552,7 @@ class CRapidJsonWriterBase : public JSON_WRITERpushBack(value * int64_t(1000), array); + this->pushBack(CTimeUtils::toEpochMs(value), array); } this->addMember(fieldName, array, obj); diff --git a/include/core/CTimeUtils.h b/include/core/CTimeUtils.h index 24c898e252..829832836d 100644 --- a/include/core/CTimeUtils.h +++ b/include/core/CTimeUtils.h @@ -64,6 +64,8 @@ class CORE_EXPORT CTimeUtils : private CNonInstantiatable //! E.g. 19:20:30 static std::string toTimeString(core_t::TTime t); + //! Converts an epoch seconds timestamp to epoch millis + static int64_t toEpochMs(core_t::TTime t); //! strptime interface //! NOTE: the time returned here is a UTC value static bool strptime(const std::string &format, diff --git a/lib/api/CJsonOutputWriter.cc b/lib/api/CJsonOutputWriter.cc index 1a5d3f2271..765998578d 100644 --- a/lib/api/CJsonOutputWriter.cc +++ b/lib/api/CJsonOutputWriter.cc @@ -500,7 +500,7 @@ void CJsonOutputWriter::writeBucket(bool isInterim, m_Writer.addIntFieldToObj(DETECTOR_INDEX, detectorIndex, *docPtr); m_Writer.addIntFieldToObj(BUCKET_SPAN, bucketData.s_BucketSpan, *docPtr); m_Writer.addStringFieldCopyToObj(JOB_ID, m_JobId, *docPtr); - m_Writer.addIntFieldToObj(TIMESTAMP, bucketTime * 1000, *docPtr); + m_Writer.addTimeFieldToObj(TIMESTAMP, bucketTime, *docPtr); if (isInterim) { @@ -531,7 +531,7 @@ void CJsonOutputWriter::writeBucket(bool isInterim, } m_Writer.addStringFieldCopyToObj(JOB_ID, m_JobId, *docPtr); - m_Writer.addIntFieldToObj(TIMESTAMP, bucketTime * 1000, *docPtr); + m_Writer.addTimeFieldToObj(TIMESTAMP, bucketTime, *docPtr); if (isInterim) { m_Writer.addBoolFieldToObj(IS_INTERIM, isInterim, *docPtr); @@ -551,7 +551,7 @@ void CJsonOutputWriter::writeBucket(bool isInterim, m_Writer.String(JOB_ID); m_Writer.String(m_JobId); m_Writer.String(TIMESTAMP); - m_Writer.Int64(bucketTime * 1000); + m_Writer.Time(bucketTime); m_Writer.String(ANOMALY_SCORE); m_Writer.Double(bucketData.s_MaxBucketInfluencerNormalizedAnomalyScore); @@ -586,7 +586,7 @@ void CJsonOutputWriter::writeBucket(bool isInterim, m_Writer.addStringFieldCopyToObj(JOB_ID, m_JobId, *docPtr); - m_Writer.addIntFieldToObj(TIMESTAMP, bucketTime * 1000, *docPtr); + m_Writer.addTimeFieldToObj(TIMESTAMP, bucketTime, *docPtr); m_Writer.addIntFieldToObj(BUCKET_SPAN, bucketData.s_BucketSpan, *docPtr); if (isInterim) { @@ -984,7 +984,7 @@ void CJsonOutputWriter::acknowledgeFlush(const std::string &flushId, core_t::TTi m_Writer.String(ID); m_Writer.String(flushId); m_Writer.String(LAST_FINALIZED_BUCKET_END); - m_Writer.Int64(lastFinalizedBucketEnd * 1000); + m_Writer.Time(lastFinalizedBucketEnd); m_Writer.EndObject(); m_Writer.EndObject(); diff --git a/lib/api/CModelPlotDataJsonWriter.cc b/lib/api/CModelPlotDataJsonWriter.cc index e6946655cd..ffd0734ef8 100644 --- a/lib/api/CModelPlotDataJsonWriter.cc +++ b/lib/api/CModelPlotDataJsonWriter.cc @@ -117,7 +117,7 @@ void CModelPlotDataJsonWriter::writeFlatRow(core_t::TTime time, m_Writer.addIntFieldToObj(DETECTOR_INDEX, detectorIndex, doc); m_Writer.addStringFieldCopyToObj(FEATURE, feature, doc, true); // time is in Java format - milliseconds since the epoch - m_Writer.addIntFieldToObj(TIME, time * 1000, doc); + m_Writer.addTimeFieldToObj(TIME, time, doc); m_Writer.addIntFieldToObj(BUCKET_SPAN, bucketSpan, doc); if (!partitionFieldName.empty()) { diff --git a/lib/api/CModelSizeStatsJsonWriter.cc b/lib/api/CModelSizeStatsJsonWriter.cc index a767378407..339da673cf 100644 --- a/lib/api/CModelSizeStatsJsonWriter.cc +++ b/lib/api/CModelSizeStatsJsonWriter.cc @@ -75,10 +75,10 @@ void CModelSizeStatsJsonWriter::write(const std::string &jobId, writer.String(print(results.s_MemoryStatus)); writer.String(TIMESTAMP); - writer.Int64(results.s_BucketStartTime * 1000); + writer.Time(results.s_BucketStartTime); writer.String(LOG_TIME); - writer.Int64(core::CTimeUtils::now() * 1000); + writer.Time(core::CTimeUtils::now()); writer.EndObject(); } diff --git a/lib/api/CModelSnapshotJsonWriter.cc b/lib/api/CModelSnapshotJsonWriter.cc index 74eb7691d4..c1bc63dc92 100644 --- a/lib/api/CModelSnapshotJsonWriter.cc +++ b/lib/api/CModelSnapshotJsonWriter.cc @@ -60,11 +60,8 @@ void CModelSnapshotJsonWriter::write(const SModelSnapshotReport &report) m_Writer.String(SNAPSHOT_DOC_COUNT); m_Writer.Uint64(report.s_NumDocs); - // Write as a Java timestamp - ms since the epoch rather than seconds - int64_t javaTimestamp = int64_t(report.s_SnapshotTimestamp) * 1000; - m_Writer.String(TIMESTAMP); - m_Writer.Int64(javaTimestamp); + m_Writer.Time(report.s_SnapshotTimestamp); m_Writer.String(DESCRIPTION); m_Writer.String(report.s_Description); @@ -73,17 +70,13 @@ void CModelSnapshotJsonWriter::write(const SModelSnapshotReport &report) if (report.s_LatestRecordTime > 0) { - javaTimestamp = int64_t(report.s_LatestRecordTime) * 1000; - m_Writer.String(LATEST_RECORD_TIME); - m_Writer.Int64(javaTimestamp); + m_Writer.Time(report.s_LatestRecordTime); } if (report.s_LatestFinalResultTime > 0) { - javaTimestamp = int64_t(report.s_LatestFinalResultTime) * 1000; - m_Writer.String(LATEST_RESULT_TIME); - m_Writer.Int64(javaTimestamp); + m_Writer.Time(report.s_LatestFinalResultTime); } // write normalizerState here @@ -111,7 +104,7 @@ void CModelSnapshotJsonWriter::writeQuantileState(const std::string &jobId, writer.String(QUANTILE_STATE); writer.String(state); writer.String(TIMESTAMP); - writer.Int64(time * 1000); + writer.Time(time); writer.EndObject(); } diff --git a/lib/core/CTimeUtils.cc b/lib/core/CTimeUtils.cc index 2cb84858b5..510a27ecf6 100644 --- a/lib/core/CTimeUtils.cc +++ b/lib/core/CTimeUtils.cc @@ -61,6 +61,11 @@ std::string CTimeUtils::toTimeString(core_t::TTime t) return result; } +int64_t CTimeUtils::toEpochMs(core_t::TTime t) +{ + return static_cast(t) * 1000; +} + bool CTimeUtils::strptime(const std::string &format, const std::string &dateTime, core_t::TTime &preTime) diff --git a/lib/core/unittest/CRapidJsonWriterBaseTest.cc b/lib/core/unittest/CRapidJsonWriterBaseTest.cc index e433d58c61..8c15b542f9 100644 --- a/lib/core/unittest/CRapidJsonWriterBaseTest.cc +++ b/lib/core/unittest/CRapidJsonWriterBaseTest.cc @@ -50,6 +50,7 @@ const std::string NAN_NAME("nan"); const std::string INFINITY_NAME("infinity"); const std::string BOOL_NAME("bool"); const std::string INT_NAME("int"); +const std::string TIME_NAME("time"); const std::string UINT_NAME("uint"); const std::string STR_ARRAY_NAME("str[]"); const std::string DOUBLE_ARRAY_NAME("double[]"); @@ -75,6 +76,7 @@ void CRapidJsonWriterBaseTest::testAddFields(void) writer.addDoubleFieldToObj(INFINITY_NAME, std::numeric_limits::infinity(), doc); writer.addBoolFieldToObj(BOOL_NAME, false, doc); writer.addIntFieldToObj(INT_NAME, -9, doc); + writer.addTimeFieldToObj(TIME_NAME, ml::core_t::TTime(1521035866), doc); writer.addUIntFieldToObj(UINT_NAME, 999999999999999ull, doc); writer.addStringArrayFieldToObj(STR_ARRAY_NAME, TGenericLineWriter::TStrVec(3, "blah"), doc); writer.addDoubleArrayFieldToObj(DOUBLE_ARRAY_NAME, TGenericLineWriter::TDoubleVec(10, 1.5), doc); @@ -97,6 +99,7 @@ void CRapidJsonWriterBaseTest::testAddFields(void) "\"infinity\":0," "\"bool\":false," "\"int\":-9," + "\"time\":1521035866000," "\"uint\":999999999999999," "\"str[]\":[\"blah\",\"blah\",\"blah\"]," "\"double[]\":[1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5]," diff --git a/lib/core/unittest/CTimeUtilsTest.cc b/lib/core/unittest/CTimeUtilsTest.cc index 9eb9690f5d..a7774cf90f 100644 --- a/lib/core/unittest/CTimeUtilsTest.cc +++ b/lib/core/unittest/CTimeUtilsTest.cc @@ -36,6 +36,9 @@ CppUnit::Test *CTimeUtilsTest::suite() suiteOfTests->addTest( new CppUnit::TestCaller( "CTimeUtilsTest::testToLocal", &CTimeUtilsTest::testToLocal) ); + suiteOfTests->addTest( new CppUnit::TestCaller( + "CTimeUtilsTest::testToEpochMs", + &CTimeUtilsTest::testToEpochMs) ); suiteOfTests->addTest( new CppUnit::TestCaller( "CTimeUtilsTest::testStrptime", &CTimeUtilsTest::testStrptime) ); @@ -114,6 +117,14 @@ void CTimeUtilsTest::testToLocal(void) } } +void CTimeUtilsTest::testToEpochMs(void) +{ + CPPUNIT_ASSERT_EQUAL(int64_t(1000), ml::core::CTimeUtils::toEpochMs(ml::core_t::TTime(1))); + CPPUNIT_ASSERT_EQUAL(int64_t(-1000), ml::core::CTimeUtils::toEpochMs(ml::core_t::TTime(-1))); + CPPUNIT_ASSERT_EQUAL(int64_t(1521035866000), ml::core::CTimeUtils::toEpochMs(ml::core_t::TTime(1521035866))); + CPPUNIT_ASSERT_EQUAL(int64_t(-1521035866000), ml::core::CTimeUtils::toEpochMs(ml::core_t::TTime(-1521035866))); +} + void CTimeUtilsTest::testStrptime(void) { // These tests assume UK time. In case they're ever run outside the UK, diff --git a/lib/core/unittest/CTimeUtilsTest.h b/lib/core/unittest/CTimeUtilsTest.h index f82b0646a9..1e616a8c70 100644 --- a/lib/core/unittest/CTimeUtilsTest.h +++ b/lib/core/unittest/CTimeUtilsTest.h @@ -24,6 +24,7 @@ class CTimeUtilsTest : public CppUnit::TestFixture void testNow(void); void testToIso8601(void); void testToLocal(void); + void testToEpochMs(void); void testStrptime(void); void testTimezone(void); void testDateWords(void);