@@ -3871,7 +3871,7 @@ def wfdbdesc(record_name, pn_dir=None):
3871
3871
if (pn_dir is not None ) and ('.' not in pn_dir ):
3872
3872
dir_list = pn_dir .split ('/' )
3873
3873
pn_dir = posixpath .join (dir_list [0 ], get_version (dir_list [0 ]),
3874
- * dir_list [1 :])
3874
+ * dir_list [1 :])
3875
3875
3876
3876
record = rdheader (record_name , pn_dir = pn_dir )
3877
3877
if type (record ) is MultiRecord :
@@ -3922,6 +3922,193 @@ def wfdbdesc(record_name, pn_dir=None):
3922
3922
print (f' Checksum: { record .checksum [i ]} ' )
3923
3923
3924
3924
3925
+ def wfdbtime (record_name , input_times , pn_dir = None ):
3926
+ """
3927
+ Use the specified record as a reference for determining the length of a
3928
+ sample interval and the absolute time represented by sample number 0. This
3929
+ program accepts one or more time arguments (in WFDB standard time format)
3930
+ and produces one line on the standard output for each such argument. In
3931
+ each output line, the corresponding time is written as a sample number (in
3932
+ the form sNNN), as an elapsed time interval in hours, minutes, and seconds
3933
+ from the beginning of the record (in the form hh:mm:ss.sss), and as an
3934
+ absolute time and date (in the form [hh:mm:ss.sss DD/MM/YYYY]). If the
3935
+ base time for the record is undefined, the absolute time cannot be
3936
+ calculated, and in this case the elapsed time appears twice instead.
3937
+
3938
+ Parameters
3939
+ ----------
3940
+ record_name : str
3941
+ The name of the WFDB record to be read, without any file
3942
+ extensions. If the argument contains any path delimiter
3943
+ characters, the argument will be interpreted as PATH/BASE_RECORD.
3944
+ Both relative and absolute paths are accepted. If the `pn_dir`
3945
+ parameter is set, this parameter should contain just the base
3946
+ record name, and the files fill be searched for remotely.
3947
+ Otherwise, the data files will be searched for in the local path.
3948
+ input_times : str, list
3949
+ The desired times (or samples) to be converted and displayed for the
3950
+ chosen record. This can either be a single string, or a list of
3951
+ strings depending on how many results the user would like. The string
3952
+ must either be an integer (in which case '1' will be interpreted as 1
3953
+ second), a datetime format (hh:mm:ss.sss DD/MM/YYYY), or prefixed with
3954
+ the letter 's' if the time at a selected sample is desired (i.e. 's10'
3955
+ for sample time). For the datetime format, 0's do not have be filled
3956
+ for all values (i.e. '5::' will be interpreted as '05:00:00.000',
3957
+ ':5:' will be '00:05:00.000', etc.). The string 'e' can be used to
3958
+ represent the end of the record.
3959
+ pn_dir : str, optional
3960
+ Option used to stream data from Physionet. The Physionet
3961
+ database directory from which to find the required record files.
3962
+ eg. For record '100' in 'http://physionet.org/content/mitdb'
3963
+ pn_dir='mitdb'.
3964
+
3965
+ Returns
3966
+ -------
3967
+ N/A
3968
+
3969
+ Examples
3970
+ --------
3971
+ * Note, the use of a single string instead of a list.
3972
+ >>> wfdb.wfdbtime('sample-data/100', 's250')
3973
+ s250 00:00:00.694 00:00:00.694
3974
+
3975
+ * Note, the elapsed time and date fields are the same since no start time
3976
+ or date was provided.
3977
+ >>> wfdb.wfdbtime('sample-data/100', ['s10',':5:','2'])
3978
+ s10 00:00:00.028 00:00:00.028
3979
+ s108000 00:05:00.000 00:05:00.000
3980
+ s720 00:00:02.000 00:00:02.000
3981
+
3982
+ * Note, '01/01/0001' represents a start time was provided but not a date.
3983
+ >>> wfdb.wfdbtime('sample-data/3000003_0003', ['s1','0:0:05','e'])
3984
+ s1 00:00:00.008 [19:46:25.765000 01/01/0001]
3985
+ s625 00:00:05.000 [19:46:30.757000 01/01/0001]
3986
+ s1028 00:00:08.224 [19:46:33.981000 01/01/0001]
3987
+
3988
+ * Note, the final argument results in the same date field as the argument
3989
+ but a different value for the elapsed time.
3990
+ >>> wfdb.wfdbtime('sample-data/3000003_0003', ['s1','::5','e','19:46:34.981 01/01/0001'])
3991
+ s1 00:00:00.008 [19:46:25.765000 01/01/0001]
3992
+ s625 00:00:05.000 [19:46:30.757000 01/01/0001]
3993
+ s1028 00:00:08.224 [19:46:33.981000 01/01/0001]
3994
+ s1153 00:00:09.224 [19:46:34.981000 01/01/0001]
3995
+
3996
+ """
3997
+ if (pn_dir is not None ) and ('.' not in pn_dir ):
3998
+ dir_list = pn_dir .split ('/' )
3999
+ pn_dir = posixpath .join (dir_list [0 ], get_version (dir_list [0 ]),
4000
+ * dir_list [1 :])
4001
+
4002
+ record = rdheader (record_name , pn_dir = pn_dir )
4003
+ try :
4004
+ start_time = datetime .datetime .combine (record .base_date , record .base_time )
4005
+ except TypeError :
4006
+ try :
4007
+ start_time = record .base_time
4008
+ except AttributeError :
4009
+ start_time = None
4010
+
4011
+ if type (input_times ) is str :
4012
+ input_times = [input_times ]
4013
+
4014
+ for times in input_times :
4015
+ if times == 'e' :
4016
+ sample_num = record .sig_len
4017
+ sample_time = float (sample_num / record .fs )
4018
+ seconds = float (sample_time )% 60
4019
+ minutes = int (float (sample_time )// 60 )
4020
+ hours = int (float (sample_time )// 60 // 60 )
4021
+ else :
4022
+ if times .startswith ('s' ):
4023
+ sample_num = int (times [1 :])
4024
+ new_times = float (sample_num / record .fs )
4025
+ times_split = [f'{ new_times } ' ]
4026
+ elif '/' in times :
4027
+ out_date = datetime .datetime .strptime (times , '%H:%M:%S.%f %d/%m/%Y' )
4028
+ try :
4029
+ start_time = datetime .datetime .combine (datetime .date .min , start_time )
4030
+ except TypeError :
4031
+ start_time = start_time
4032
+ try :
4033
+ elapsed_time = out_date - start_time
4034
+ except TypeError :
4035
+ raise Exception ('No start date or time provided in the record.' )
4036
+ total_seconds = elapsed_time .total_seconds ()
4037
+ sample_num = 's' + str (int (elapsed_time .total_seconds () * record .fs ))
4038
+ hours = int (total_seconds // 60 // 60 )
4039
+ minutes = int (total_seconds // 60 )
4040
+ out_time = f'{ hours :02} :{ minutes :02} :{ total_seconds :06.3f} '
4041
+ out_date = f'[{ out_date .strftime ("%H:%M:%S.%f %d/%m/%Y" )} ]'
4042
+ print (f'{ sample_num :>12} { out_time :>24} { out_date :>32} ' )
4043
+ break
4044
+ else :
4045
+ new_times = times
4046
+ times_split = [t if t != '' else '0' for t in times .split (':' )]
4047
+ if len (times_split ) == 1 :
4048
+ seconds = float (new_times )% 60
4049
+ minutes = int (float (new_times )// 60 )
4050
+ hours = int (float (new_times )// 60 // 60 )
4051
+ elif len (times_split ) == 2 :
4052
+ seconds = float (times_split [1 ])
4053
+ minutes = int (times_split [0 ])
4054
+ hours = 0
4055
+ elif len (times_split ) == 3 :
4056
+ seconds = float (times_split [2 ])
4057
+ minutes = int (times_split [1 ])
4058
+ hours = int (times_split [0 ])
4059
+ if seconds >= 60 :
4060
+ raise Exception ('Seconds not in correct format' )
4061
+ if minutes >= 60 :
4062
+ raise Exception ('Minutes not in correct format' )
4063
+ out_time = f'{ hours :02} :{ minutes :02} :{ seconds :06.3f} '
4064
+ out_date = _get_date_from_time (start_time , hours , minutes , seconds ,
4065
+ out_time )
4066
+ if not times .startswith ('s' ):
4067
+ sample_num = int (sum (x * 60 ** i for i ,x in enumerate ([seconds ,minutes ,hours ])) * record .fs )
4068
+ sample_num = 's' + str (sample_num )
4069
+ print (f'{ sample_num :>12} { out_time :>24} { out_date :>32} ' )
4070
+
4071
+
4072
+ def _get_date_from_time (start_time , hours , minutes , seconds , out_time ):
4073
+ """
4074
+ Convert a starting time to a date using the elapsed time.
4075
+
4076
+ Parameters
4077
+ ----------
4078
+ start_time : datetime object
4079
+ The time the record start if available.
4080
+ hours : int
4081
+ The number of hours elapsed.
4082
+ minutes : int
4083
+ The number of minutes elapsed.
4084
+ seconds : int
4085
+ The number of seconds elapsed.
4086
+ out_time : str
4087
+ The string formatted time elapsed for a desired sample, if available.
4088
+
4089
+ Returns
4090
+ -------
4091
+ out_date : datetime object
4092
+ The time the record ends after the caculcated elapsed time.
4093
+
4094
+ """
4095
+ if start_time :
4096
+ try :
4097
+ start_time = datetime .datetime .combine (datetime .date .min , start_time ) - \
4098
+ datetime .datetime .min
4099
+ except TypeError :
4100
+ start_time = start_time - datetime .datetime .min
4101
+ microseconds = int (1000000 * (seconds % 1 ))
4102
+ elapsed_time = datetime .timedelta (hours = hours , minutes = minutes ,
4103
+ seconds = int (seconds ),
4104
+ microseconds = microseconds )
4105
+ out_date = start_time + elapsed_time
4106
+ out_date = f'[{ (datetime .datetime .min + out_date ).strftime ("%H:%M:%S.%f %d/%m/%Y" )} ]'
4107
+ else :
4108
+ out_date = out_time
4109
+ return out_date
4110
+
4111
+
3925
4112
def _get_wanted_channels (wanted_sig_names , record_sig_names , pad = False ):
3926
4113
"""
3927
4114
Given some wanted signal names, and the signal names contained in a
0 commit comments