diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b6a6ed3..66ba77b3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,7 +295,6 @@ target_link_libraries(dorado_lib htslib vbz_hdf_plugin edlib - date::date dorado_utils PRIVATE ${KOI_LIBRARIES} @@ -465,7 +464,6 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") ${POD5_LIBRARIES} ${HDF5_C_LIBRARIES} ${CMAKE_DL_LIBS} - date::date ) if(WIN32) diff --git a/dorado/cli/summary.cpp b/dorado/cli/summary.cpp index 8da957fb..413d2501 100644 --- a/dorado/cli/summary.cpp +++ b/dorado/cli/summary.cpp @@ -2,10 +2,9 @@ #include "read_pipeline/HtsReader.h" #include "utils/bam_utils.h" #include "utils/log_utils.h" +#include "utils/time_utils.h" #include -#include -#include #include #include @@ -16,34 +15,6 @@ namespace dorado { volatile sig_atomic_t interrupt = 0; -// todo: move to time_utils after !273 -double time_difference_seconds(const std::string ×tamp1, const std::string ×tamp2) { - using namespace date; - using namespace std::chrono; - try { - std::istringstream ss1(timestamp1); - std::istringstream ss2(timestamp2); - sys_time time1, time2; - ss1 >> parse("%FT%T%Ez", time1); - ss2 >> parse("%FT%T%Ez", time2); - // If parsing with timezone offset failed, try parsing with 'Z' format - if (ss1.fail()) { - ss1.clear(); - ss1.str(timestamp1); - ss1 >> parse("%FT%TZ", time1); - } - if (ss2.fail()) { - ss2.clear(); - ss2.str(timestamp2); - ss2 >> parse("%FT%TZ", time2); - } - duration diff = time1 - time2; - return diff.count(); - } catch (const std::exception &e) { - throw std::runtime_error("Failed to parse timestamps"); - } -} - int summary(int argc, char *argv[]) { utils::InitLogging(); @@ -154,7 +125,7 @@ int summary(int argc, char *argv[]) { float sample_rate = num_samples / duration; float template_duration = (num_samples - trim_samples) / sample_rate; auto exp_start_dt = read_group_exp_start_time.at(rg_value); - auto start_time = time_difference_seconds(start_time_dt, exp_start_dt); + auto start_time = utils::time_difference_seconds(start_time_dt, exp_start_dt); auto template_start_time = start_time + (duration - template_duration); std::cout << filename << separator << read_id << separator << run_id << separator << channel diff --git a/dorado/utils/CMakeLists.txt b/dorado/utils/CMakeLists.txt index 0b30d4f1..85cc6331 100644 --- a/dorado/utils/CMakeLists.txt +++ b/dorado/utils/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(dorado_utils stats.h tensor_utils.cpp tensor_utils.h + time_utils.cpp time_utils.h torch_utils.h trim.cpp @@ -85,11 +86,11 @@ target_link_libraries(dorado_utils PUBLIC ${TORCH_LIBRARIES} edlib - date::date spdlog::spdlog PRIVATE minimap2 OpenSSL::SSL + date::date htslib ) diff --git a/dorado/utils/time_utils.cpp b/dorado/utils/time_utils.cpp new file mode 100644 index 00000000..45a7590e --- /dev/null +++ b/dorado/utils/time_utils.cpp @@ -0,0 +1,91 @@ +#include "time_utils.h" + +#include +#include + +#include +#include + +namespace dorado::utils { + +std::string get_string_timestamp_from_unix_time(time_t time_stamp_ms) { + auto tp = std::chrono::system_clock::from_time_t(time_stamp_ms / 1000); + tp += std::chrono::milliseconds(time_stamp_ms % 1000); + auto dp = date::floor(tp); + auto time = date::make_time(std::chrono::duration_cast(tp - dp)); + auto ymd = date::year_month_day{dp}; + + std::ostringstream date_time_ss; + date_time_ss << ymd << "T" << time << "+00:00"; + return date_time_ss.str(); +} + +// Expects the time to be encoded like "2017-09-12T09:50:12.456+00:00" or "2017-09-12T09:50:12Z". +// Time stamp can be specified up to microseconds +time_t get_unix_time_from_string_timestamp(const std::string & time_stamp) { + std::istringstream ss(time_stamp); + date::sys_time time_us; + ss >> date::parse("%FT%T%Ez", time_us); + // If parsing with timezone offset failed, try parsing with 'Z' format + if (ss.fail()) { + ss.clear(); + ss.str(time_stamp); + ss >> date::parse("%FT%TZ", time_us); + } + + auto epoch = time_us.time_since_epoch(); + auto value = std::chrono::duration_cast(epoch); + return value.count(); +} + +std::string adjust_time_ms(const std::string & time_stamp, uint64_t offset_ms) { + return get_string_timestamp_from_unix_time(get_unix_time_from_string_timestamp(time_stamp) + + offset_ms); +} + +std::string adjust_time(const std::string & time_stamp, uint32_t offset) { + // Expects the time to be encoded like "2017-09-12T9:50:12Z". + // Adds the offset (in seconds) to the timeStamp. + + date::sys_time time_s; + std::istringstream ss(time_stamp); + ss >> date::parse("%FT%TZ", time_s); + time_s += std::chrono::seconds(offset); + + auto dp = date::floor(time_s); + auto time = date::make_time(time_s - dp); + auto ymd = date::year_month_day{dp}; + + std::ostringstream date_time_ss; + date_time_ss << ymd << "T" << time << "Z"; + return date_time_ss.str(); +} + +double time_difference_seconds(const std::string & timestamp1, const std::string & timestamp2) { + using namespace date; + using namespace std::chrono; + try { + std::istringstream ss1(timestamp1); + std::istringstream ss2(timestamp2); + sys_time time1, time2; + ss1 >> parse("%FT%T%Ez", time1); + ss2 >> parse("%FT%T%Ez", time2); + // If parsing with timezone offset failed, try parsing with 'Z' format + if (ss1.fail()) { + ss1.clear(); + ss1.str(timestamp1); + ss1 >> parse("%FT%TZ", time1); + } + if (ss2.fail()) { + ss2.clear(); + ss2.str(timestamp2); + ss2 >> parse("%FT%TZ", time2); + } + duration diff = time1 - time2; + return diff.count(); + } catch (const std::exception & e) { + throw std::runtime_error("Failed to parse timestamps"); + } +} + +} // namespace dorado::utils diff --git a/dorado/utils/time_utils.h b/dorado/utils/time_utils.h index d031ebdf..eea193b3 100644 --- a/dorado/utils/time_utils.h +++ b/dorado/utils/time_utils.h @@ -1,65 +1,20 @@ #pragma once -#include -#include -#include #include -#include #include namespace dorado::utils { -inline std::string get_string_timestamp_from_unix_time(time_t time_stamp_ms) { - auto tp = std::chrono::system_clock::from_time_t(time_stamp_ms / 1000); - tp += std::chrono::milliseconds(time_stamp_ms % 1000); - auto dp = date::floor(tp); - auto time = date::make_time(std::chrono::duration_cast(tp - dp)); - auto ymd = date::year_month_day{dp}; - - std::ostringstream date_time_ss; - date_time_ss << ymd << "T" << time << "+00:00"; - return date_time_ss.str(); -} +std::string get_string_timestamp_from_unix_time(time_t time_stamp_ms); // Expects the time to be encoded like "2017-09-12T09:50:12.456+00:00" or "2017-09-12T09:50:12Z". // Time stamp can be specified up to microseconds -inline time_t get_unix_time_from_string_timestamp(const std::string& time_stamp) { - std::istringstream ss(time_stamp); - date::sys_time time_us; - ss >> date::parse("%FT%T%Ez", time_us); - // If parsing with timezone offset failed, try parsing with 'Z' format - if (ss.fail()) { - ss.clear(); - ss.str(time_stamp); - ss >> date::parse("%FT%TZ", time_us); - } - - auto epoch = time_us.time_since_epoch(); - auto value = std::chrono::duration_cast(epoch); - return value.count(); -} - -inline std::string adjust_time_ms(const std::string& time_stamp, uint64_t offset_ms) { - return get_string_timestamp_from_unix_time(get_unix_time_from_string_timestamp(time_stamp) + - offset_ms); -} - -inline std::string adjust_time(const std::string& time_stamp, uint32_t offset) { - // Expects the time to be encoded like "2017-09-12T9:50:12Z". - // Adds the offset (in seconds) to the timeStamp. +time_t get_unix_time_from_string_timestamp(const std::string& time_stamp); - date::sys_time time_s; - std::istringstream ss(time_stamp); - ss >> date::parse("%FT%TZ", time_s); - time_s += std::chrono::seconds(offset); +std::string adjust_time_ms(const std::string& time_stamp, uint64_t offset_ms); - auto dp = date::floor(time_s); - auto time = date::make_time(time_s - dp); - auto ymd = date::year_month_day{dp}; +std::string adjust_time(const std::string& time_stamp, uint32_t offset); - std::ostringstream date_time_ss; - date_time_ss << ymd << "T" << time << "Z"; - return date_time_ss.str(); -} +double time_difference_seconds(const std::string& timestamp1, const std::string& timestamp2); } // namespace dorado::utils