;+ ; NAME: ; TimeSplit ; PURPOSE: ; Convert times from string to numerical ; CATEGORY: ; gen/idl/toolbox/time ; CALLING SEQUENCE: FUNCTION TimeSplit, TSS, T0, $ template = template , $ format = format , $ rational = rational , $ fmtspec = fmtspec ; INPUTS: ; TS array; type: string ; times in string form ; T0 array[1]; type: time structure ; time origin ; OPTIONAL INPUTS: ; format=format ; scalar; type: sting; default: none ; string template specifying the time format, e.g. 'YEAR/MN/DD hh:mm:ss.mss' ; Valid components are: ; YEAR for 4-digit year ; YY for 2-digit year (converted using TimeFixYear) ; DOY for the day of year ; MON for 3-char month ('jan') ; MN for 2-digit month (01-12) ; DD for the day of the month ; hh for the hour of the day ; mm for the minutes ; ss for the seconds ; mss for the milliseconds ; OUTPUTS: ; T array; type: time structure ; output times with numerical values for the ; seperate components of the time array ; If an error occurs then T = -1 is returned. ; The function IsTime(T) can be used to check for an ; error condition. ; OPTIONAL OUTPUTS: ; fmtspec=fmtspec ; scalar; type: string ; if format NOT set: format string used to interpret ; the input strings. ; if format SET: specfmt is same as input format ; INCLUDE: @compile_opt.pro ; On error, return to caller ; CALLS: ; InitVar, TimeSet, TimeGet, IsTime, TimeUnit, TagArray, SyncDims ; TimeFixYear, TimeOp ; RESTRICTIONS: ; The first element of the TS array is used to analyze the exact string format. ; This format is used to decompose all string elements in their constituent parts. ; Hence, if TS is a string array, all entries must have exactly the same format!!! ; PROCEDURE: ; The string times in TS do not have to be complete. ; Missing parts are taken from the time origin T0 ; Four types of strings can be entered: ; 1999:300:00:00:00.000 ; 1999/11/20 00:00:00.000 (YMD format) ; SAT JAN 10 00:00:00 1991 (SYS format) ; 19-NOV-1999 23:48:11.00 (VMS format) ; 03-23-2000 23:48:11 (SMEI format) ; 2003_023_234811000 (L1A format) ; 2003-10-11T23:48:11.000 (ISO-8601 format) ; MODIFICATION HISTORY: ; AUG-1998, Paul Hick (UCSD/CASS) ; NOV-1999, Paul Hick (UCSD/CASS) ; Added VMS format, fairly substantial rewrite ; APR-2000, Paul Hick (UCSD/CASS) ; Added SMEI format '03-23-2000 23:48:11' ; MAR-2003, Paul Hick (UCSD/CASS) ; Added processing of strings like 2003_175_123456789 ; OCT-2003, Paul Hick (UCSD/CASS) ; Added processing of strings like 2003_10_02_123456789 ; NOV-2003, Paul Hick (UCSD/CASS) ; Added keyword 'template' ; JUN-2005, Paul Hick (UCSD/CASS) ; Rename keyword 'template' to 'format'. ; SEP-2007, Paul Hick (UCSD/CASS) ; Added some code to handle ISO-8601 compliant times ; OCT-2007, Paul Hick (UCSD/CASS) ; Added output format spec keyword 'fmtspec' ; DEC-2008, Paul Hick (UCSD/CASS; pphick@ucsd.edu) ; Added code to allow input of fractional units in ; day of month, hour, minutes, seconds, e.g. ; '2003/10/10.5' or '2003/10/10 12.5' ;- InitVar, rational, /key InitVar, T0, !TimeZero IF IsType(template,/defined) THEN format = template CASE size(TSS,/n_dim) NE 0 OF 0: TS = TSS 1: TS = reform(TSS, n_elements(TSS)) ENDCASE TS0 = strtrim( strcompress(TS[0]), 2) ; First time in array is used to analyze format CASE 1 OF ; Explicit format specified to split up strings IsType(format,/string): BEGIN fmtspec = format cformat = strlowcase(format) A = strpos(cformat, 'year') ; Check for 4-digit year if A eq -1 then A = strpos(cformat, 'yyyy') ; Check for 4-digit year CASE A EQ -1 OF 0: BEGIN yr = fix(strmid(TS,A,4)) strput, cformat, ' ', A END 1: BEGIN A = strpos(cformat, 'yy') ; Check for 2-digit year CASE A EQ -1 OF 0: BEGIN yr = TimeFixYear(fix(strmid(TS,A,2))) strput, cformat, ' ', A END 1: yr = TimeGet(T0, TimeUnit(/year)) ENDCASE END ENDCASE date_present = 1 A = strpos(cformat, 'doy') ; Check for day of year CASE A EQ -1 OF 0: BEGIN TT = TimeSet(yr=yr, doy=fix(strmid(TS,A,3))) strput, cformat, ' ', A END 1: BEGIN A = strpos(cformat,'mon') ; Check for month as string ('jan') CASE A EQ -1 OF 0: BEGIN month = strmid(TS,A,3) strput, cformat, ' ', A END 1: BEGIN A = strpos(cformat,'mn') ; Check for month as number (1-12) CASE A EQ -1 OF 0: BEGIN month = fix(strmid(TS,A,2)) strput, cformat, ' ', A END ;1: message, 'invalid month entry in format: '+format ;1: date_present = 0 1: BEGIN CASE IsType(yr,/defined) OF 0: date_present = 0 1: month = 1 ENDCASE END ENDCASE END ENDCASE IF date_present THEN BEGIN A = strpos(cformat,'dd') CASE A EQ -1 OF 0: BEGIN TT = TimeSet(yr=yr, month=month, day=fix(strmid(TS,A,2))) strput, cformat, ' ', A END ;1: message, 'invalid day of month entry in format: '+format 1: BEGIN CASE IsType(yr,/defined) OF 0: date_present = 0 1: TT = TimeSet(yr=yr, month=month, day=1) ENDCASE END ENDCASE ENDIF END ENDCASE A = strpos(cformat, 'hh') CASE A EQ -1 OF 0: BEGIN TTH = fix(strmid(TS,A,2)) strput, cformat, ' ', A END 1: TTH = TimeGet(T0,TimeUnit(/hour),/scalar) ENDCASE A = strpos(cformat, 'mm') CASE A EQ -1 OF 0: BEGIN TTM = fix(strmid(TS,A,2)) strput, cformat, ' ', A END 1: TTM = TimeGet(T0,TimeUnit(/minute),/scalar) ENDCASE A = strpos(cformat, 'ss') CASE A EQ -1 OF 0: BEGIN TTS = fix(strmid(TS,A,2)) strput, cformat, ' ', A END 1: TTS = TimeGet(T0,TimeUnit(/sec),/scalar) ENDCASE A = strpos(cformat, 'fff') IF A EQ -1 THEN A = strpos(cformat,'mss') CASE A EQ -1 OF 0: BEGIN TTMS = fix(strmid(TS,A,3)) strput, cformat, ' ', A END 1: TTMS = TimeGet(T0,TimeUnit(/msec),/scalar) ENDCASE CASE date_present OF 0: TT = TimeSet(/diff, hour=TTH,minute=TTM,sec=TTS,msec=TTMS) 1: TT = TimeOp(/add,TT, TimeSet(/diff, hour=TTH,minute=TTM,sec=TTS,msec=TTMS) ) ENDCASE SyncDims, TT, size=size(TSS) ; Impose structure of input array TSS END rational: BEGIN ; No format specified: new, better, but not used yet ; Two rational time specs are accepted ; YYYY/MN/DD hh:mm:ss.sss ; YYYY/DOY hh:mm:ss.sss ; Split first string into components TS = flt_string(TS,/positive_only,fmt=fmt,/list_format) ; 1st piece should be the year. ; Check for 2-digit year CASE TS[0,0] GE 100 OF 0: BEGIN fmtspec = 'YY' yr = TimeFixYear(round(TS[0,*])) END 1: BEGIN fmtspec = 'YYYY' yr = round(TS[0,*]) END ENDCASE ; 2nd piece is 2-digit day or month or 3-digit day of year END ELSE: BEGIN ; No format specified; old, clumsy, but still used A = byte(TS0) SCOR = [where(A EQ (byte('_'))[0],nSCR),n_elements(A)] DASH = where(A EQ (byte('-'))[0],nDSH) SLSH = where(A EQ (byte('/'))[0],nSLS) COLS = where(A EQ (byte(':'))[0],nCOL) COL1 = strpos(TS0,':') COL2 = strpos(TS0,':',COL1+1) NODOT= strpos(TS0,'.') EQ -1 N = strlen(TS0) ; A system time is identified by looking for alphabetical characters ; Currently two formats are identified: ; "SAT APR 13 14:30:15 1991" (SYS format) ; "19-NOV-1999 23:48:11.00" (VMS format) ; The distinction is made by looking for a pair of dashes A = byte(strupcase(TS0)) ; Check for uppercase alphabetical character ; Finds alphabetical months, and T in ISO-8601 IF (where((byte('A'))[0] LE A AND A LE (byte('Z'))[0]))[0] NE -1 THEN BEGIN IF nDSH EQ 2 THEN BEGIN ; Two dashes: '19-NOV-1999 23:48:11.00' (VMS) ; or ; '1999-09-19T23:48:11.00' (ISO-8601) ; Split the first time only. We only want to know the length of the first ; field: an ISO-8601 time starts with a 4-digit year, a VMS time with a ; 2-digit day of the month. TS0 = TagArray(TS[0], ['-',' ',':','.'], /split, /nonull) iso8601 = strlen(TS0[0]) EQ 4 CASE iso8601 OF 0: BEGIN ; VMS TS = TagArray(TS, ['-',' ',':','.'], /split, /nonull) TT = TimeSet(yr=fix(TS[2,*]),month=TS[1,*],day=fix(TS[0,*])) fmtspec = 'DD-MON-YYYY' END 1: BEGIN ; ISO-8601 TS = TagArray(TS, ['-',':','T'], /split, /nonull) n = strpos(TS[2,0],'.') CASE n EQ -1 OF 0: TT = TimeSet(yr=fix(TS[0,*]),month=fix(TS[1,*]),day=double(TS[2,*])) 1: TT = TimeSet(yr=fix(TS[0,*]),month=fix(TS[1,*]),day=fix (TS[2,*])) ENDCASE fmtspec = 'YYYY-MN-DD' IF n NE -1 THEN fmtspec += '.'+strjoin(replicate('d',strlen(TS[2,0])-n-1)) END ENDCASE n = (size(TS))[1] IF n GE 4 THEN BEGIN TS = TS[3:*,*] fmtspec += ([' ','T'])[iso8601] ENDIF ELSE $ TS = -1 ENDIF ELSE BEGIN ; 'SAT JAN 10 00:00:00 1991' (SYS format) TS = TagArray(TS,[' ',':'],/split, /nonull) TT = TimeSet(yr=fix(TS[6,*]),month=TS[1,*],day=fix(TS[2,*])) TS = TS[3:5,*] ; Time of day fmtspec = 'DOW MON DD hh:mm:ss YYYY' ENDELSE tod_sep = ':' ENDIF ELSE IF nSLS EQ 2 OR nSLS EQ 1 THEN BEGIN ; '[1999/]11/20 00:00:00.000' (YMD format) ; '[1999/]11/20.33 TS = TagArray(TS,['/',' ',',',':'], /split, /nonull) CASE nSLS OF 1: BEGIN yr = TimeGet(T0,TimeUnit(/year)) fmtspec = 'MN/DD' END 2: BEGIN yr = fix(TS[0,*]) fmtspec = 'YYYY/MN/DD' END ENDCASE n = strpos(TS[nSLS,0],'.') CASE n EQ -1 OF 0: TT = TimeSet(yr=yr,month=fix(TS[nSLS-1,*]),day=double(TS[nSLS,*])) 1: TT = TimeSet(yr=yr,month=fix(TS[nSLS-1,*]),day=fix (TS[nSLS,*])) ENDCASE IF n NE -1 THEN fmtspec += '.'+strjoin(replicate('d',strlen(TS[nSLS,0])-n-1)) n = (size(TS))[1]-nSLS-1 ; Nr of fields in time of day IF n GT 0 THEN BEGIN ; Time of day present TS = TS[nSLS+1:*,*] fmtspec += ' ' ENDIF ELSE $ TS = -1 tod_sep = ':' ENDIF ELSE IF nCOL EQ 4 OR nCOL EQ 3 THEN BEGIN ; '[1999:]300:00:00:00.000' TS = TagArray(TS,[':',' '], /split, /nonull) CASE strlen(TS[0,0]) EQ 4 OF 0: BEGIN yr = TimeGet(T0,TimeUnit(/year)) fmtspec = 'DOY' ndoy = 0 END 1: BEGIN yr = fix(TS[0,*]) fmtspec = 'YYYY:DOY' ndoy = 1 END ENDCASE n = strpos(TS[ndoy,0],'.') CASE n EQ -1 OF 0: TT = TimeSet(yr=yr,doy=double(TS[ndoy,*])) 1: TT = TimeSet(yr=yr,doy=fix (TS[ndoy,*])) ENDCASE IF n NE -1 THEN fmtspec += '.'+strjoin(replicate('d',strlen(TS[ndoy,0])-n-1)) n = (size(TS))[1] IF n GT ndoy+1 THEN BEGIN TS = TS[ndoy+1:*,*] fmtspec += ':' ENDIF ELSE $ TS = -1 tod_sep = ':' ENDIF ELSE IF COL1 EQ 4 AND (COL2 EQ 8 OR (N EQ 8 AND NODOT)) THEN BEGIN ; COL1=4 and COL2=8: ydoy format YEAR:DOY:..... ; extract Y and DOY ; COL1=4, N=8 and no dots: ydoy format YEAR:DOY ; extract Y and DOY TS = TagArray(TS,[':',' '], /split, /nonull) n = strpos(TS[1,0],'.') CASE n EQ -1 OF 0: TT = TimeSet(yr=fix(TS[0,*]),doy=double(TS[1,*])) 1: TT = TimeSet(yr=fix(TS[0,*]),doy=fix (TS[1,*])) ENDCASE fmtspec = 'YYYY:DOY' IF n NE -1 THEN fmtspec += '.'+strjoin(replicate('d',strlen(TS[1,0])-n-1)) n = (size(TS))[1] IF n GT 2 THEN BEGIN TS = TS[2:*,*] fmtspec += ':' ENDIF ELSE $ TS = -1 tod_sep = '' ENDIF ELSE IF COL1 EQ 3 THEN BEGIN ; COL1=3: ydoy format with year missing year: DOY:.... ; extract DOY ; N=3 and no dots: ydoy format (only day of year) DOY ; extract DOY TS = TagArray(TS,[':',' ','.'], /split, /nonull) TT = TimeSet(yr=TimeGet(T0,TimeUnit(/year)),doy=fix(TS[0,*])) fmtspec = 'DOY' n =(size(TS))[1] IF n GE 2 THEN BEGIN TS = TS[1:*,*] fmtspec += ':' ENDIF ELSE $ TS = -1 tod_sep = '' ENDIF ELSE IF nDSH EQ 2 THEN BEGIN ; Two dashes: '03-23-2000 23:48:11' (SMEI) ; Format used in the B'ham CCD image files TS0 = TagArray(TS[0], ['-',' ',':','.'], /split, /nonull) iso8601 = strlen(TS0[0]) EQ 4 CASE iso8601 OF 0: BEGIN TS = TagArray(TS,['-',' ',':','.'], /split, /nonull) TT = TimeSet(yr=fix(TS[2,*]),month=fix(TS[0,*]),day=fix(TS[1,*])) fmtspec = 'MN-DD-YYYY' END 1: BEGIN TS = TagArray(TS,['-',' ',':'], /split, /nonull) n = strpos(TS[2,0],'.') CASE n EQ -1 OF 0: TT = TimeSet(yr=fix(TS[0,*]),month=fix(TS[1,*]),day=double(TS[2,*])) 1: TT = TimeSet(yr=fix(TS[0,*]),month=fix(TS[1,*]),day=fix (TS[2,*])) ENDCASE fmtspec = 'YYYY-MN-DD' IF n NE -1 THEN fmtspec += '.'+strjoin(replicate('d',strlen(TS[2,0])-n-1)) END ENDCASE n = (size(TS))[1] IF n GT 3 THEN BEGIN TS = TS[3:*,*] fmtspec += ([' ','T'])[iso8601] ENDIF ELSE $ TS = -1 tod_sep = ':' ENDIF ELSE IF nSCR GT 0 THEN BEGIN TS = TagArray(TS,'_', /split, /nonull) CASE SCOR[1]-SCOR[0]-1 OF 2: BEGIN ; Three underscores: '2000_01_23_234811000' n = (size(TS))[1] CASE n EQ 2 OF 0: BEGIN TT = TimeSet(yr=fix(TS[0,*]),month=fix(TS[1,*]), day=fix(TS[2,*])) fmtspec = 'YYYY_MN_DD' END 1: BEGIN TT = TimeSet(yr=fix(TS[0,*]),month=fix(TS[1,*]), day=1) fmtspec = 'YYYY_MN' END ENDCASE CASE n EQ 4 OF 0: TS = -1 1: BEGIN ; Time of day TS = reform(TS[3,*]) n = where(strlen(TS) GT 6) & IF n[0] NE -1 THEN TS[n] = strmid(TS[n],0,6)+'.'+strmid(TS[n],6) n = where(strlen(TS) GT 4) & IF n[0] NE -1 THEN TS[n] = strmid(TS[n],0,4)+':'+strmid(TS[n],4) n = where(strlen(TS) GT 2) & IF n[0] NE -1 THEN TS[n] = strmid(TS[n],0,2)+':'+strmid(TS[n],2) TS = TagArray(TS,[':','.'], /split, /nonull) fmtspec += '_' END ENDCASE END 3: BEGIN ; Two underscores: '2000_023_234811000' TT = TimeSet(yr=fix(TS[0,*]),doy=fix(TS[1,*])) fmtspec = 'YYYY_DOY' n = (size(TS))[1] CASE n EQ 3 OF 0: TS = -1 1: BEGIN ; Time of day TS = reform(TS[2,*]) n = where(strlen(TS) GT 6) & IF n[0] NE -1 THEN TS[n] = strmid(TS[n],0,6)+'.'+strmid(TS[n],6) n = where(strlen(TS) GT 4) & IF n[0] NE -1 THEN TS[n] = strmid(TS[n],0,4)+':'+strmid(TS[n],4) n = where(strlen(TS) GT 2) & IF n[0] NE -1 THEN TS[n] = strmid(TS[n],0,2)+':'+strmid(TS[n],2) TS = TagArray(TS,[':','.'], /split, /nonull) fmtspec += '_' END ENDCASE END ENDCASE tod_sep = '' ENDIF ELSE BEGIN ; Only time of day specified ???? TS = TagArray(TS,[':',' ','.'], /split, /nonull) TT = TimeSet(yr=TimeGet(T0,TimeUnit(/year)),doy= TimeGet(T0,TimeUnit(/days))) fmtspec = '' tod_sep = ':' ENDELSE ; At this point the date has been processed. Now set the time of day TTH = TimeGet(T0,TimeUnit(/hour ),/scalar) TTM = TimeGet(T0,TimeUnit(/minute),/scalar) TTS = TimeGet(T0,TimeUnit(/sec ),/scalar) TTMS = TimeGet(T0,TimeUnit(/msec ),/scalar); Time of day = Time of day of origin IF TS[0] NE -1 THEN BEGIN ; TS should always be an array with the number of time ; tags (h,m,s,ms) as first dimension n = (size(TS))[1] ; Check for a decimal point in the last time-of-day component IF strpos(TS[n-1,0],'.') NE -1 THEN BEGIN ; The exception for n=3 makes sure that fmtspec comes out ; right if there are other than 3 numbers in the decimal ; fraction (representing milli-seconds). CASE n EQ 3 OF 0: BEGIN A = double(TS[n-1,*]) szA = size(A) IF n EQ 1 THEN destroyvar, TS ELSE TS = TS[0:n-2,*] SWITCH n OF 1: BEGIN ; Decimal point in hours tmp = floor(A) A -= tmp A *= 60 ; Remaining minutes tmp = string(tmp,format='(I2.2)') SyncDims, tmp, size=szA TS = tmp END 2: BEGIN ; Decimal point in minutes tmp = floor(A) A -= tmp A *= 60 ; Remaining seconds tmp = string(tmp,format='(I2.2)') SyncDims, tmp, size=szA TS = [TS, tmp ] END 3: BEGIN ; Decimal point in seconds tmp = floor(A) A -= tmp A *= 1000 ; Remaining milli-seconds tmp = string(tmp,format='(I2.2)') SyncDims, tmp, size=szA TS = [TS, tmp] END 4: BEGIN ; Decimal point in milliseconds tmp = round(A) n = strcompress(max(strlen(strcompress(tmp,/rem))),/rem) tmp = string(tmp,format='(I'+n+'.'+n+')') SyncDims, tmp, size=szA TS = [TS, tmp] END ENDSWITCH END 1: TS = [TS[0:n-2,*],TagArray(reform(TS[n-1,*]),'.',/split,/nonull)] ENDCASE n = (size(TS))[1] ENDIF IF strpos(fmtspec,'hh') EQ -1 THEN BEGIN fmtspec += 'hh'+(['',tod_sep+'mm'])[n GE 2]+(['',tod_sep+'ss'])[n GE 3] IF n GE 4 THEN $ fmtspec += '.'+string(replicate((byte('f'))[0],strlen(TS[3]))) ENDIF CASE NODOT OF 0: BEGIN A = n A -= 1 & IF A GE 0 THEN TTMS = fix(TS[A,*])*10^(3-strlen(TS[A,*])) A -= 1 & IF A GE 0 THEN TTS = fix(TS[A,*]) A -= 1 & IF A GE 0 THEN TTM = fix(TS[A,*]) A -= 1 & IF A GE 0 THEN TTH = fix(TS[A,*]) END 1: BEGIN A = 0 & TTH = fix(TS[A,*]) A += 1 & IF A LT n THEN TTM = fix(TS[A,*]) A += 1 & IF A LT n THEN TTS = fix(TS[A,*]) A += 1 & IF A LT n THEN TTMS = fix(TS[A,*])*10^(3-strlen(TS[A,*])) END ENDCASE ENDIF TT = TimeOp(/add,TT, TimeSet(/diff, hour=TTH,minute=TTM,sec=TTS,msec=TTMS) ) SyncDims, TT, size=size(TSS) ; Impose structure of input array TSS END ENDCASE RETURN, TT & END