*! 1.3.0 NJC 6 Sept 2005 
* 1.2.1 NJC 29 July 2003 
* 1.2.0 NJC 4 July 2001 
* 1.1.0 NJC 7 June 2001 
program todate
	version 8   
	syntax varlist [if] [in], Pattern(string) Generate(string) ///
	[ Format(string) Cend(numlist int >200 <=10000 max=1) ]

	// existing and new varlists 
	tokenize `varlist' 
	local nvars : word count `varlist'

	// do before second -syntax- 
	marksample touse, novarlist 

	local 0 "`generate'"
	syntax newvarlist 

	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 
	} 
	
	// partial test of format
	if "`format'" != "" { 
		capture display `format' 1 
		if _rc { 
			di as err "invalid format()" 
			exit 120 
		}
	} 

	// parse pattern into m d y h q w elements
	// indulge upper case
	local pattern = lower("`pattern'") 
	local plength = length("`pattern'") 

	forval i = 1 / `plength' { 
		local p = substr("`pattern'",`i',1) 
		
		if "`p'" == "m"      local mlist "`mlist' `i'" 
		else if "`p'" == "d" local dlist "`dlist' `i'" 
		else if "`p'" == "y" local ylist "`ylist' `i'" 
		else if "`p'" == "h" local hlist "`hlist' `i'"
		else if "`p'" == "q" local qlist "`qlist' `i'" 
		else if "`p'" == "w" local wlist "`wlist' `i'" 
		else { 
			di as err "invalid pattern" 
			exit 198
		} 
	} 	

	// allow mdy yh yq ym yw permutations
	foreach i in m d y h q w { 
		if "``i'list'" != "" { 
			local ptype "`ptype'`i'" 
			local pels  "`pels' `i'" 
		}
	} 	

	if !inlist("`ptype'", "yh", "yq", "my", "yw", "mdy") { 
		di as err "invalid pattern type: `ptype'" 
		exit 198 
	} 
	if "`ptype'" == "my" local ptype "ym"

	// contiguous digits will have range == # elements - 1 
	foreach i in `pels' {
		local `i'1 : word 1 of ``i'list' 
		local `i'len : word count ``i'list' 
		local last : word ``i'len' of ``i'list' 
		local range = `last' - ``i'1' 
		local range2 = ``i'len' - 1 
		if `range' != `range2' { 
			di as err "`i' digits not contiguous in pattern" 
			exit 198 
		}
	} 

	// year digits and cend() compatible?  
	if `ylen' != 4 & "`cend'" == "" { 
		di as err "`ylen' digit years: need cend() option?" 
		exit 198 
	} 
	else if `ylen' == 4 & "`cend'" != "" {  
		di as txt "4 digit years: cend() option ignored" 
		local cend 
	} 	
	
	// for each variable in original varlist
	qui forval i = 1 / `nvars' { 
		tempvar strdate datelen touse2 

		// markout separately for each variable 
		gen byte `touse2' = `touse' 
		markout `touse2' ``i'', strok 

		// working string variable copy of date variable 
		capture confirm string variable ``i'' 
		if _rc gen `strdate' = string(``i'',"%12.0g") if `touse2'  
		else   gen `strdate' = trim(``i'') if `touse2' 
		local v "``i''" 
		local `i' "`strdate'" 

		// how long is date variable? 
		gen `datelen' = length(``i'') 
		su `datelen' if `touse2', meanonly 
		local range = r(max) - r(min) 
		local min = r(min) 
		local max = r(max) 
		
		if `max' != `plength' { 
			noi di as res "`v': " ///
			as txt "length does not match pattern" 
			continue 
		} 
		
		// range == 0 is no problem 
		if `range' == 1 { /* leading zero needs to be supplied? */ 
			replace `strdate' = "0" + `strdate' ///
				if `datelen' == `min' & `touse2' 
		} 
		else if `range' >= 2 { /* range of lengths >= 2 => skip this */ 
			noi di as res "`v': " /// 
			as txt "length too variable to handle" 
			continue 
		} 	
		
		// construct month, day, year, half, quarter, week as needed  
		foreach j in `pels' { 
			tempvar `j' 
			gen ``j'' = real(substr(``i'',``j'1',``j'len'))
		} 

		if "`cend'" != "" { 
			local c1 = int(`cend' / 100) 
			local c2 = mod(`cend',100) 
			replace `y' = /// 
			`y' + 100 * cond(`y' <= `c2', `c1', `c1' - 1) 
		} 	

		// generate new variable 
		local newvar : word `i' of `varlist' 
		if "`ptype'" == "mdy" { 
			gen `newvar' = mdy(`m',`d',`y') if `touse2' 
		} 
		else { 
			local o = substr("`ptype'",2,1) 
			gen `newvar' = y`o'(`y',``o'') if `touse2' 
		} 	
		
		if "`format'" != "" format `format' `newvar' 
		else { 
			if index("`ptype'", "d")      format %d  `newvar'
			else if index("`ptype'", "w") format %tw `newvar'
			else if index("`ptype'", "m") format %tm `newvar' 			
			else if index("`ptype'", "q") format %tq `newvar' 		
			else if index("`ptype'", "h") format %th `newvar' 	
		}

		_crcslbl `newvar' ``i'' 
		
		drop `touse2' 
	} 
end