Skip to content

Commit 2889aa1

Browse files
authored
Merge pull request #187 from NeurodataWithoutBorders/178-shifting-isodatetime
Shifting ISO Datetime
2 parents 5ca3d71 + 8de62ea commit 2889aa1

File tree

6 files changed

+93
-37
lines changed

6 files changed

+93
-37
lines changed

+misc/getWorkspace.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
function path = getWorkspace()
22
%GETWORKSPACE Returns current workspace in MATLAB
33
[path, ~, ~] = fileparts(getenv('WORKSPACE'));
4+
if isempty(path)
5+
path = '.';
6+
end
47
end

+tests/+system/NwbTestInterface.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function setupMethod(testCase)
2222
'session_description', 'a test NWB File', ...
2323
'identifier', 'TEST123', ...
2424
'session_start_time', '2018-12-02T12:57:27.371444-08:00', ...
25-
'file_create_date', datestr([2017, 4, 15, 12, 0, 0], 'yyyy-mm-dd HH:MM:SS'),...
25+
'file_create_date', '2017-04-15T12:00:00.000000-08:00',...
2626
'timestamps_reference_time', '2018-12-02T12:57:27.371444-08:00');
2727
testCase.addContainer(testCase.file);
2828
end

+tests/+system/PyNWBIOTest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import unittest2 as unittest
1+
import unittest
22
from datetime import datetime
33
import os.path
44
import numpy as np

+tests/+unit/multipleShapesTest.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ function testInheritedDtypeDataset(testCase)
5656
%% Convenience
5757
function roundabout(testCase, dataset)
5858
nwb = NwbFile('identifier', 'MSS', 'session_description', 'test',...
59-
'session_start_time', datetime, 'timestamps_reference_time', datetime);
59+
'session_start_time', '2017-04-15T12:00:00.000000-08:00',...
60+
'timestamps_reference_time', '2017-04-15T12:00:00.000000-08:00');
6061
wrapper = types.mss.MultiShapeWrapper('shaped_data', dataset);
6162
nwb.acquisition.set('wrapper', wrapper);
6263
filename = 'multipleShapesTest.nwb';

+tests/+util/verifyContainerEqual.m

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,20 @@ function verifyContainerEqual(testCase, actual, expected)
55
prop = props{i};
66
val1 = actual.(prop);
77
val2 = expected.(prop);
8-
failmsg = ['Values for property ''' prop ''' are not equal'];
8+
failmsg = ['Values for property ''' prop ''' are not equal'];
9+
10+
if isa(val1, 'types.untyped.DataStub')
11+
val1 = val1.load();
12+
end
913

10-
if startsWith(class(val1), 'types.') && ~startsWith(class(val1), 'types.untyped')
14+
if startsWith(class(val2), 'types.') && ~startsWith(class(val2), 'types.untyped')
1115
tests.util.verifyContainerEqual(testCase, val1, val2);
12-
elseif isa(val1, 'types.untyped.Set')
16+
elseif isa(val2, 'types.untyped.Set')
1317
tests.util.verifySetEqual(testCase, val1, val2, failmsg);
14-
elseif isdatetime(val1)
15-
testCase.verifyTrue(isdatetime(val2));
18+
elseif isdatetime(val2)
1619
testCase.verifyEqual(char(val1), char(val2), failmsg);
1720
else
18-
if isa(val1, 'types.untyped.DataStub')
19-
trueval = val1.load();
20-
else
21-
trueval = val1;
22-
end
23-
testCase.verifyEqual(trueval, val2, failmsg);
21+
testCase.verifyEqual(val1, val2, failmsg);
2422
end
2523
end
2624
end

+types/+util/checkDtype.m

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -111,35 +111,19 @@
111111
end
112112
end
113113
elseif strcmp(type, 'isodatetime')
114-
addpath(fullfile(fileparts(which('NwbFile')), 'external_packages', 'datenum8601'));
115-
assert(ischar(val) || iscellstr(val) || isdatetime(val) ||...
116-
(iscell(val) && all(cellfun('isclass', val, 'datetime'))), errid, errmsg);
114+
assert(ischar(val)...
115+
|| iscellstr(val)...
116+
|| isdatetime(val) ...
117+
|| (iscell(val) && all(cellfun('isclass', val, 'datetime'))),...
118+
errid, errmsg);
117119
if ischar(val) || iscellstr(val)
118120
if ischar(val)
119121
val = {val};
120122
end
121123

122124
datevals = cell(size(val));
123-
% one of:
124-
% +-hh:mm
125-
% +-hhmm
126-
% +-hh
127-
% Z
128-
tzre_pattern = '(?:[+-]\d{2}(?::?\d{2})?|Z)$';
129125
for i = 1:length(val)
130-
dnum = datenum8601(val{i});
131-
132-
tzre_match = regexp(val{i}, tzre_pattern, 'once');
133-
if isempty(tzre_match)
134-
tz = 'local';
135-
else
136-
tz = val{i}(tzre_match:end);
137-
if strcmp(tz, 'Z')
138-
tz = 'UTC';
139-
end
140-
end
141-
datevals{i} = ...
142-
datetime(dnum(1), 'TimeZone', tz, 'ConvertFrom', 'datenum');
126+
datevals{i} = datetime8601(val{i});
143127
end
144128
val = datevals;
145129
end
@@ -148,7 +132,7 @@
148132
val = {val};
149133
end
150134

151-
for i=1:length(val)
135+
for i = 1:length(val)
152136
if isempty(val{i}.TimeZone)
153137
val{i}.TimeZone = 'local';
154138
end
@@ -187,4 +171,74 @@
187171
val = truval;
188172
end
189173
end
174+
end
175+
176+
function date_time = datetime8601(datestr)
177+
addpath(fullfile(fileparts(which('NwbFile')), 'external_packages', 'datenum8601'));
178+
[~, ~, format] = datenum8601(datestr);
179+
format = format{1};
180+
has_delimiters = format(1) == '*';
181+
if has_delimiters
182+
format = format(2:end);
183+
end
184+
185+
assert(strncmp(format, 'ymd', 3),...
186+
'MatNWB:Types:Util:CheckDType:DateTime:Unsupported8601',...
187+
'non-ymd formats not supported.');
188+
separator = format(4);
189+
if separator ~= ' '
190+
% non-space digits will error when specifying import format
191+
separator = ['''' separator ''''];
192+
end
193+
194+
has_fractional_sec = isstrprop(format(8:end), 'digit');
195+
if has_fractional_sec
196+
seconds_precision = str2double(format(8:end));
197+
if seconds_precision > 9
198+
warning('MatNWB:Types:Util:CheckDType:DateTime:LossySeconds',...
199+
['Potential loss of time data detected. MATLAB fractional seconds '...
200+
'precision is limited to 1 ns. Extra precision will be truncated.']);
201+
end
202+
end
203+
day_segments = {'yyyy', 'MM', 'dd'};
204+
time_segments = {'HH', 'mm', 'ss'};
205+
206+
if has_delimiters
207+
day_delimiter = '-';
208+
time_delimiter = ':';
209+
else
210+
day_delimiter = '';
211+
time_delimiter = '';
212+
end
213+
214+
day_format = strjoin(day_segments, day_delimiter);
215+
time_format = strjoin(time_segments, time_delimiter);
216+
format = [day_format separator time_format];
217+
if has_fractional_sec
218+
format = sprintf('%s.%s', format, repmat('S', 1, seconds_precision));
219+
end
220+
221+
[datestr, timezone] = derive_timezone(datestr);
222+
date_time = datetime(datestr,...
223+
'InputFormat', format,...
224+
'TimeZone', timezone);
225+
end
226+
227+
function [datestr, timezone] = derive_timezone(datestr)
228+
% one of:
229+
% +-hh:mm
230+
% +-hhmm
231+
% +-hh
232+
% Z
233+
tzre_pattern = '(?:[+-]\d{2}(?::?\d{2})?|Z)$';
234+
tzre_match = regexp(datestr, tzre_pattern, 'once');
235+
if isempty(tzre_match)
236+
timezone = 'local';
237+
else
238+
timezone = datestr(tzre_match:end);
239+
if strcmp(timezone, 'Z')
240+
timezone = 'UTC';
241+
end
242+
datestr = datestr(1:(tzre_match - 1));
243+
end
190244
end

0 commit comments

Comments
 (0)