Skip to content

Commit 00a529c

Browse files
oruebelstephprince
andauthored
Allow setting of the session start time and timestamp reference time (#144)
* Fix #8 allow setting of session start time and reference time * Update src/nwb/NWBFile.hpp Co-authored-by: Steph Prince <[email protected]> * Update timestampReferenceTime to timestampsReferenceTime * Use empty default for sessionStartTime and timestampReferenceTime --------- Co-authored-by: Steph Prince <[email protected]>
1 parent c5ac566 commit 00a529c

File tree

5 files changed

+135
-9
lines changed

5 files changed

+135
-9
lines changed

src/Utils.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <cstdint>
77
#include <ctime>
88
#include <iomanip>
9+
#include <regex>
910
#include <sstream>
1011
#include <string>
1112

@@ -61,6 +62,26 @@ static inline std::string getCurrentTime()
6162
return currentTime;
6263
}
6364

65+
/**
66+
* @brief Check that a string is formatted in ISO8601 format
67+
*
68+
* This function only validates the regex pattern but does not check that
69+
* the time values specified are indeed valid.
70+
*
71+
* @return bool indicating whether the string is in ISO8601 form
72+
*/
73+
static inline bool isISO8601Date(const std::string& dateStr)
74+
{
75+
// Define the regex pattern for ISO 8601 extended format with timezone offset
76+
// Allow one or more fractional seconds digits
77+
const std::string iso8601Pattern =
78+
R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{2}:\d{2}$)";
79+
std::regex pattern(iso8601Pattern);
80+
81+
// Check if the date string matches the regex pattern
82+
return std::regex_match(dateStr, pattern);
83+
}
84+
6485
/**
6586
* @brief Factory method to create an IO object.
6687
* @return A pointer to a BaseIO object

src/nwb/NWBFile.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,42 @@ NWBFile::~NWBFile() {}
4949

5050
Status NWBFile::initialize(const std::string& identifierText,
5151
const std::string& description,
52-
const std::string& dataCollection)
52+
const std::string& dataCollection,
53+
const std::string& sessionStartTime,
54+
const std::string& timestampsReferenceTime)
5355
{
5456
if (!m_io->isOpen()) {
5557
return Status::Failure;
5658
}
59+
std::string currentTime = getCurrentTime();
60+
// use the current time if sessionStartTime is empty
61+
std::string useSessionStartTime =
62+
(!sessionStartTime.empty()) ? sessionStartTime : currentTime;
63+
// use the current time if timestampsReferenceTime is empty
64+
std::string useTimestampsReferenceTime = (!timestampsReferenceTime.empty())
65+
? timestampsReferenceTime
66+
: currentTime;
67+
// check that sessionStartTime and timestampsReferenceTime are ISO8601
68+
if (!isISO8601Date(useSessionStartTime)) {
69+
std::cerr << "NWBFile::initialize sessionStartTime not in ISO8601 format: "
70+
<< useSessionStartTime << std::endl;
71+
return Status::Failure;
72+
}
73+
if (!isISO8601Date(useTimestampsReferenceTime)) {
74+
std::cerr
75+
<< "NWBFile::initialize timestampsReferenceTime not in ISO8601 format: "
76+
<< useTimestampsReferenceTime << std::endl;
77+
return Status::Failure;
78+
}
79+
5780
// Check that the file is empty and initialize if it is
5881
bool fileInitialized = isInitialized();
5982
if (!fileInitialized) {
60-
return createFileStructure(identifierText, description, dataCollection);
83+
return createFileStructure(identifierText,
84+
description,
85+
dataCollection,
86+
useSessionStartTime,
87+
useTimestampsReferenceTime);
6188
} else {
6289
return Status::Success; // File is already initialized
6390
}
@@ -102,7 +129,9 @@ Status NWBFile::finalize()
102129

103130
Status NWBFile::createFileStructure(const std::string& identifierText,
104131
const std::string& description,
105-
const std::string& dataCollection)
132+
const std::string& dataCollection,
133+
const std::string& sessionStartTime,
134+
const std::string& timestampsReferenceTime)
106135
{
107136
if (!m_io->canModifyObjects()) {
108137
return Status::Failure;
@@ -133,12 +162,12 @@ Status NWBFile::createFileStructure(const std::string& identifierText,
133162
cacheSpecifications("hdmf-experimental",
134163
AQNWB::SPEC::HDMF_EXPERIMENTAL::version,
135164
AQNWB::SPEC::HDMF_EXPERIMENTAL::specVariables);
136-
std::string time = getCurrentTime();
137-
std::vector<std::string> timeVec = {time};
165+
std::vector<std::string> timeVec = {sessionStartTime};
138166
m_io->createStringDataSet("/file_create_date", timeVec);
139167
m_io->createStringDataSet("/session_description", description);
140-
m_io->createStringDataSet("/session_start_time", time);
141-
m_io->createStringDataSet("/timestamps_reference_time", time);
168+
m_io->createStringDataSet("/session_start_time", sessionStartTime);
169+
m_io->createStringDataSet("/timestamps_reference_time",
170+
timestampsReferenceTime);
142171
m_io->createStringDataSet("/identifier", identifierText);
143172
return Status::Success;
144173
}

src/nwb/NWBFile.hpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,16 @@ class NWBFile : public Container
6767
* @param identifierText The identifier text for the NWBFile.
6868
* @param description A description of the NWBFile session.
6969
* @param dataCollection Information about the data collection methods.
70+
* @param sessionStartTime ISO formatted time string with the session start
71+
* time. If empty (default), then the getCurrentTime() will be used.
72+
* @param timestampsReferenceTime ISO formatted time string with the timestamp
73+
* reference time. If empty (default), then the getCurrentTime() will be used.
7074
*/
7175
Status initialize(const std::string& identifierText,
7276
const std::string& description = "a recording session",
73-
const std::string& dataCollection = "");
77+
const std::string& dataCollection = "",
78+
const std::string& sessionStartTime = "",
79+
const std::string& timestampsReferenceTime = "");
7480

7581
/**
7682
* @brief Check if the NWB file is initialized.
@@ -155,11 +161,17 @@ class NWBFile : public Container
155161
* @param identifierText The identifier text for the NWBFile.
156162
* @param description A description of the NWBFile session.
157163
* @param dataCollection Information about the data collection methods.
164+
* @param sessionStartTime ISO formatted time string with the session start
165+
* time
166+
* @param timestampsReferenceTime ISO formatted time string with the timestamp
167+
* reference time
158168
* @return Status The status of the file structure creation.
159169
*/
160170
Status createFileStructure(const std::string& identifierText,
161171
const std::string& description,
162-
const std::string& dataCollection);
172+
const std::string& dataCollection,
173+
const std::string& sessionStartTime,
174+
const std::string& timestampsReferenceTime);
163175

164176
private:
165177
/**

tests/testNWBFile.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,39 @@ TEST_CASE("saveNWBFile", "[nwb]")
2626
nwbfile.finalize();
2727
}
2828

29+
TEST_CASE("initialize", "[nwb]")
30+
{
31+
std::string filename = getTestFilePath("testInitializeNWBFile.nwb");
32+
33+
// initialize nwbfile object and create base structure
34+
auto io = std::make_shared<IO::HDF5::HDF5IO>(filename);
35+
io->open();
36+
NWB::NWBFile nwbfile(io);
37+
38+
// bad session start time
39+
Status initStatus = nwbfile.initialize(generateUuid(),
40+
"test file",
41+
"no collection",
42+
"bad time",
43+
AQNWB::getCurrentTime());
44+
REQUIRE(initStatus == Status::Failure);
45+
46+
// bad timestamp reference time
47+
initStatus = nwbfile.initialize(generateUuid(),
48+
"test file",
49+
"no collection",
50+
AQNWB::getCurrentTime(),
51+
"bad time");
52+
REQUIRE(initStatus == Status::Failure);
53+
54+
// check that regular init with current times works
55+
initStatus = nwbfile.initialize(generateUuid());
56+
REQUIRE(initStatus == Status::Success);
57+
REQUIRE(nwbfile.isInitialized());
58+
nwbfile.finalize();
59+
io->close();
60+
}
61+
2962
TEST_CASE("createElectricalSeries", "[nwb]")
3063
{
3164
std::string filename = getTestFilePath("createElectricalSeries.nwb");

tests/testUtilsFunctions.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,37 @@
44

55
#include "Utils.hpp"
66

7+
TEST_CASE("isISO8601Date function tests", "[utils]")
8+
{
9+
SECTION("Valid ISO 8601 date strings")
10+
{
11+
REQUIRE(AQNWB::isISO8601Date("2018-09-28T14:43:54.123+02:00"));
12+
REQUIRE(AQNWB::isISO8601Date("2025-01-19T00:40:03.214144-08:00"));
13+
REQUIRE(AQNWB::isISO8601Date("2021-12-31T23:59:59.999999+00:00"));
14+
REQUIRE(AQNWB::isISO8601Date("2000-01-01T00:00:00.0+01:00"));
15+
REQUIRE(AQNWB::isISO8601Date(
16+
"2018-09-28T14:43:54.12345+02:00")); // Allow for too many fractional
17+
// seconds
18+
}
19+
20+
SECTION("Invalid ISO 8601 date strings")
21+
{
22+
REQUIRE_FALSE(AQNWB::isISO8601Date(
23+
"2018-09-28 14:43:54.123+02:00")); // Space instead of 'T'
24+
REQUIRE_FALSE(AQNWB::isISO8601Date(
25+
"2018-09-28T14:43:54+02:00")); // Missing fractional seconds
26+
REQUIRE_FALSE(AQNWB::isISO8601Date(
27+
"2018-09-28T14:43:54.123+0200")); // Missing colon in timezone
28+
REQUIRE_FALSE(AQNWB::isISO8601Date(
29+
"2018-09-28T14:43:54.123Z")); // Missing timezone offset
30+
REQUIRE_FALSE(AQNWB::isISO8601Date(
31+
"2018-09-28T14:43:54.123-0800")); // Incorrect timezone format
32+
REQUIRE_FALSE(
33+
AQNWB::isISO8601Date("2018-09-28T14:43:54.123")); // Missing timezone
34+
REQUIRE_FALSE(AQNWB::isISO8601Date("Random text 1213"));
35+
}
36+
}
37+
738
TEST_CASE("Test UUID generation", "[utils]")
839
{
940
// Test that generated UUIDs are valid

0 commit comments

Comments
 (0)