*! 1.0.0 NJC 2 February 2006 program ntimeofday, rclass version 8 syntax varlist(str) [if] [in], /// Generate(string) /// Numeric(str) /// String(string) /// [ /// Type(str) /// Parse(str asis) destring * /// Extremes /// Cend(numlist max=1 >100 <10000) /// am(str asis) pm(str asis) /// ] // the wild-card is to pass extra options to -split-, but // undocumented at present // check numeric() and string() // either could say nothing: unlikely, but check if trim("`numeric'") == "" { di as err "invalid numeric() option" exit 198 } if trim("`string'") == "" { di as err "invalid string() option" exit 198 } // numeric() has to signal one of days hours hrs minutes seconds local numeric = lower(trim("`numeric'")) local len = length("`numeric'") foreach ok in days hours hrs minutes seconds { if "`numeric'" == substr("`ok'",1,`len') { local unit = cond("`ok'" == "hrs", "hours", "`ok'") } } if "`unit'" == "" { di as err /// "numeric() option should specify one of days, hours, minutes, seconds" exit 198 } // string() may signal // // days dmy dym // hours hrs // mdy minutes months myd // seconds // ydm years ymd yrs local OK days dmy dym hours hrs mdy minutes months myd seconds local OK `OK' ydm years ymd yrs local OK2 dmy dym mdy myd ydm ymd foreach s of local string { local s = lower("`s'") local len = length("`s'") foreach ok of local OK { local min = /// 2 - inlist("`ok'", "seconds", "hours", "hrs") local min = max(`min', `len') if "`s'" == substr("`ok'",1,`min') { local String `String' `ok' } } } // just "h" could mean "hrs" or "hours", and just "y" could mean "yrs" // or "years": not ambiguous, but don't double count local String : subinstr local String "hrs" "hours" local String : subinstr local String "yrs" "years" // no duplicates allowed, and every component must be valid local String : list uniq String if `: word count `string'' != `: word count `String'' { di as err "invalid string() option" exit 198 } // check for information specifying days local String : subinstr local String "years" "years", count(local y) local string : subinstr local String "months" "months", count(local m) local string : subinstr local string "days" "days", count(local d) /* inputs: y invalid y m invalid y m d valid (days should be 1 ... 28/31) y + m + d = 3 m d invalid m invalid d valid y + m + d = 1 y d valid (days should be 1 ... 365/366) y + m + d = 2 (none) valid */ // exit if invalid if inlist("`string'", "dmy", "dym", "mdy", "myd", "ydm", "ymd") /// & inlist("`string'", "days", "months", "years") { di as err "string() invalid" exit 498 } local both : list OK2 & string if `: list sizeof both' > 1 { di as err "choose at most one from `OK2'" exit 498 } if `y' == 1 & `d' == 0 { di as err "string() should also specify days?" exit 498 } if `m' == 1 & `d' == 0 & `y' == 0 { di as err "string() should also specify days and years?" exit 498 } if `m' == 1 & `d' == 1 & `y' == 0 { di as err "string() should also specify years?" exit 498 } // need for later processing local listtype = `y' + `m' + `d' // process varlist tokenize `varlist' local nvars : word count `varlist' local oldvarlist "`varlist'" // do before second -syntax- marksample touse, novarlist qui count if `touse' if r(N) == 0 error 2000 // new varlist valid? local 0 "`generate'" syntax newvarlist // do existing and new varlists match one-to-one? if `nvars' != `: word count `varlist'' { di as err "number of new variables not equal to" _c di as err " number of existing variables" exit 198 } // default parse strings if `"`parse'"' == "" local parse `" : " " "' // initial blank line if check for extremes if "`extremes'" != "" di // for each variable in original varlist quietly forval i = 1 / `nvars' { tempvar work touse2 ispm tempname stub // markout separately for each variable gen byte `touse2' = `touse' markout `touse2' ``i'', strok // am or pm: first strip text indicators // keep a note of which times are pm gen byte `ispm' = 0 local I "``i''" if `"`am'`pm'"' != "" { tempvar copyi gen `copyi' = ``i'' local `i' "`copyi'" } if `"`am'"' != "" { foreach s of local am { replace `copyi' = subinstr(`copyi', "`s'","",.) } } if `"`pm'"' != "" { foreach s of local pm { replace `ispm' = max(`ispm', index(`copyi', "`s'") > 0) replace `copyi' = subinstr(`copyi', "`s'","",.) } } // split is main engine here split ``i'', gen(`stub') parse(`parse') destring `options' if `r(nvars)' != `: word count `string'' { di as err "`I' does not appear to be `string'" exit 498 } local split "`r(varlist)'" // initialise if "`type'" == "" local type "double" gen `type' `work' = 0 local nd = 0 // for y + m + d local quiet // for each component forval j = 1/`: word count `string'' { local comp : word `j' of `string' local cont : word `j' of `split' if "`comp'" == "years" { capture confirm numeric variable `cont' if _rc { di as err "years component of `I' is string" exit _rc } local Y "`cont'" local ++nd } else if "`comp'" == "months" { capture confirm numeric variable `cont' if _rc { tempvar tostring gen `tostring' = /// month(date(`cont' + " 1 1960", "mdy")) if `touse2' local cont "`tostring'" } local M "`cont'" local ++nd } else if "`comp'" == "days" { capture confirm numeric variable `cont' if _rc { di as err "days component of `I' is string" exit _rc } local D "`cont'" local ++nd } else if "`comp'" == "hours" { capture confirm numeric variable `cont' if _rc { di as err "hours component of `I' is string" exit _rc } // pm adjustment replace `cont' = `cont' + 12 * (`cont' < 12) * `ispm' if "`unit'" == "days" { replace `work' = `work' + `cont' / 24 } else if "`unit'" == "hours" { replace `work' = `work' + `cont' } else if "`unit'" == "minutes" { replace `work' = `work' + 60 * `cont' } else if "`unit'" == "seconds" { replace `work' = `work' + 3600 * `cont' } } else if "`comp'" == "minutes" { capture confirm numeric variable `cont' if _rc { di as err "minutes component of `I' is string" exit _rc } if "`unit'" == "days" { replace `work' = `work' + `cont' / 1440 } else if "`unit'" == "hours" { replace `work' = `work' + `cont' / 60 } else if "`unit'" == "minutes" { replace `work' = `work' + `cont' } else if "`unit'" == "seconds" { replace `work' = `work' + 60 * `cont' } } else if "`comp'" == "seconds" { capture confirm numeric variable `cont' if _rc { di as err "seconds component of `I' is string" exit _rc } if "`unit'" == "days" { replace `work' = `work' + `cont' / 86400 } else if "`unit'" == "hours" { replace `work' = `work' + `cont' / 3600 } else if "`unit'" == "minutes" { replace `work' = `work' + `cont' / 60 } else if "`unit'" == "seconds" { replace `work' = `work' + `cont' } } else if inlist("`comp'", "dmy", "dym", "mdy", "myd", "ydm", "ymd") { tempvar todate capture confirm string var `cont' if _rc { di as err "`comp' element of `I' is numeric" exit 498 } if "`cend'" != "" local cend ", `cend'" capture gen double `todate' = date(`cont', "`comp'" `cend') if `touse2' if _rc { di as err "`comp' element of `I' is problematic" exit _rc } local cont "`todate'" if "`unit'" == "days" { replace `work' = `work' + `cont' } else if "`unit'" == "hours" { replace `work' = `work' + 24 * `cont' } else if "`unit'" == "minutes" { replace `work' = `work' + 1440 * `cont' } else if "`unit'" == "seconds" { replace `work' = `work' + 86400 * `cont' } } if "`extremes'" != "" { `quiet' noi di as res abbrev("`I'", 21) /// as txt "{col 23}min max" `quiet' noi di as txt "{hline 32}" local quiet "*" su `cont' if `touse2', meanonly noi di as txt "{col 5}`comp'{col 19}" /// as res %7.0g r(min) %7.0g r(max) } if `nd' > 0 & `nd' == `listtype' { if `nd' == 2 { // years and days both to hand capture assert /// inrange(`D',1,doy(mdy(12,31,`Y'))) if `touse2' if _rc { di as err /// "day element of `I' outside (1, 365 or 366)" exit 498 } local cont "mdy(1,1,`Y') + `D' - 1" } else if `nd' == 3 { // years, months, days to hand local cont "mdy(`M',`D',`Y')" } else if `nd' == 1 { // days to hand // OK } if "`unit'" == "days" { replace `work' = `work' + `cont' } else if "`unit'" == "hours" { replace `work' = `work' + 24 * `cont' } else if "`unit'" == "minutes" { replace `work' = `work' + 1440 * `cont' } else if "`unit'" == "seconds" { replace `work' = `work' + 86400 * `cont' } // don't do this more than once local nd = 0 } } if "`extremes'" != "" noi di local togenerate "`togenerate' `work'" drop `touse2' drop `ispm' capture drop `tostring' capture drop `todate' drop `stub'* } // generate new variables iff all are OK tokenize "`togenerate'" local i = 1 quietly { foreach v of local varlist { gen `type' `v' = ``i'' local V : word `i' of `oldvarlist' label var `v' "`V' (`unit')" local ++i } } d `varlist' return local varlist "`varlist'" end