*! 1.0.0 NJC 31 January 2006
program stimeofday, rclass
	version 8
	syntax varlist(num) [if] [in], ///
	String(str) Numeric(str) Generate(str) ///
	[ * TIMEonly am(str) pm(str) ///
	DFormat(str) HFormat(str) MFormat(str) SFormat(str) ///
	DSymbol(str) HSymbol(str) MSymbol(str) SSymbol(str) ]

	// 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 hours hrs minutes seconds

	foreach s of local string {
		local s = lower("`s'")
		local len = length("`s'")
		foreach ok in days hours hrs minutes seconds {
			if "`s'" == substr("`ok'",1,`len') {
				local String `String' `ok'
			}
		}
	}

	// no duplicates allowed, and every component must be valid

	local String : subinstr local String "hrs" "hours"
	local String : list uniq String

	if `: word count `string'' != `: word count `String'' {
		di as err "invalid string() option"
		exit 198
	}

	// valid d h m s, d h m, d h, h m s, h m, m s

	local list1 days hours minutes seconds
	local list2 days hours minutes
	local list3 days hours
	local list4 hours minutes seconds
	local list5 hours minutes
	local list6 minutes seconds

	forval i = 1/6 {
		local istype : list String == list`i'
		if `istype' local listtype `i'
	}

	if "`listtype'" == "" {
		di as err "string() option invalid"
		exit 198
	}

	if "`timeonly'" != "" & `listtype' < 4 {
		di as err "timeonly and string(`string') not compatible"
		exit 198
	}

	if "`am'`pm'" != "" {
		if inlist(`listtype', 3, 6) {
			di as err "am/pm and string(`string') not compatible"
			exit 198
		}

		tempvar ispm
		gen byte `ispm' = .
	}

	// 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
	}

	// day, hour, min, second formats
	if "`dformat'" != "" local dformat `", "`dformat'" "'
	if "`hformat'" == "" local hformat "%02.0f"
	if "`mformat'" == "" local mformat "%02.0f"
	if "`sformat'" == "" local sformat "%05.2f"

	// day, hour, min symbols
	if `"`dsymbol'"' == "" local dsymbol " "
	if `"`hsymbol'"' == "" & `listtype' != 3 local hsymbol ":"
	if `"`msymbol'"' == "" & !inlist(`listtype', 2, 5) local msymbol ":"
	// `ssymbol' defaults to empty string

	// initialise
	tempvar rest
	gen double `rest' = 0

	if "`unit'" == "days" {
		quietly forval i = 1 / `nvars' {
			tempvar work touse2

			// markout separately for each variable
			gen byte `touse2' = `touse'
			markout `touse2' ``i''
			gen `work' = ""
			if "`am'`pm'" != "" replace `ispm' = `touse2'
			local I "``i''"

			if "`timeonly'" != "" {
				replace `rest' = ``i'' - floor(``i'')
				local `i' "`rest'"
			}

			if `listtype' == 1 {
				replace `work' = string(floor(``i'') `dformat') + `"`dsymbol'"'
				replace `rest' = ``i'' - floor(``i'')

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (24 * `rest' >= 13)
					replace `rest' = `rest' - 0.5 if `ispm'
				}

				replace `work' = `work' + string(floor(24 * `rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = mod(24 * `rest', 1)
				replace `work' = `work' + string(floor(60 * `rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = mod(60 * `rest', 1)
				replace `work' = `work' + string(60 * `rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 2 {
				replace `work' = string(floor(``i'') `dformat') + `"`dsymbol'"'
				replace `rest' = ``i'' - floor(``i'')

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (24 * `rest' >= 13)
					replace `rest' = `rest' - 0.5 if `ispm'
				}

				replace `work' = `work' + string(floor(24 * `rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = mod(24 * `rest', 1)
				replace `work' = `work' + string(60 * `rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 3 {
				replace `work' = string(floor(``i'') `dformat') + `"`dsymbol'"'
				replace `rest' = ``i'' - floor(``i'')
				replace `work' = `work' + string(24 * `rest', "`hformat'") + `"`hsymbol'"'
			}
			else if `listtype' == 4 {
				replace `rest' = 24 * ``i''
				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 5 {
				replace `rest' = 24 * ``i''

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 6 {
				replace `rest' = 1440 * ``i''
	 			replace `work' = string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}

			replace `work' = "" if `touse2' == 0
			if "`am'`pm'" != "" {
				replace `work' = `work' + cond(`ispm', "`pm'", "`am'")
			}
			local togenerate "`togenerate' `work'"
		}
	}
	else if "`unit'" == "hours" {
		quietly forval i = 1 / `nvars' {
			tempvar work touse2

			// markout separately for each variable
			gen byte `touse2' = `touse'
			markout `touse2' ``i''
			gen `work' = ""
			if "`am'`pm'" != "" replace `ispm' = `touse2'
			local I "``i''"

			if "`timeonly'" != "" {
				replace `rest' = mod(``i'', 24)
				local `i' "`rest'"
			}

			if `listtype' == 1 {
				replace `work' = string(floor(``i'' / 24) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 24)

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = `work' + string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 2 {
				replace `work' = string(floor(``i'' / 24) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 24)

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = `work' + string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 3 {
				replace `work' = string(floor(``i'' / 24) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 24)
				replace `work' = `work' + string(`rest', "`hformat'") + `"`hsymbol'"'
			}
			else if `listtype' == 4 {
				replace `rest' = ``i''

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 5 {
				replace `rest' = ``i''

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 6 {
				replace `rest' = 60 * ``i''
				replace `work' = string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}

			replace `work' = "" if `touse2' == 0
			if "`am'`pm'" != "" {
				replace `work' = `work' + cond(`ispm', "`pm'", "`am'")
			}
			local togenerate "`togenerate' `work'"
		}

	}
	else if "`unit'" == "minutes" {
		quietly forval i = 1 / `nvars' {
			tempvar work touse2

			// markout separately for each variable
			gen byte `touse2' = `touse'
			markout `touse2' ``i''
			gen `work' = ""
			if "`am'`pm'" != "" replace `ispm' = `touse2'
			local I "``i''"

			if "`timeonly'" != "" {
				replace `rest' = mod(``i'', 1440)
				local `i' "`rest'"
			}

			if `listtype' == 1 {
				replace `work' = string(floor(``i'' / 1440) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 1440)

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (`rest' >= 780)
					replace `rest' = `rest' - 720 if `ispm'
				}

				replace `work' = `work' + string(floor(`rest' / 60), "`hformat'") + `"`hsymbol'"'
				replace `rest' = mod(`rest', 60)
				replace `work' = `work' + string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'")  + `"`ssymbol'"'
			}
			else if `listtype' == 2 {
				replace `work' = string(floor(``i'' / 1440) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 1440)

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (`rest' >= 780)
					replace `rest' = `rest' - 720 if `ispm'
				}

				replace `work' = `work' + string(floor(`rest' / 60), "`hformat'") + `"`hsymbol'"'
				replace `rest' = mod(`rest', 60)
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 3 {
				replace `work' = string(floor(``i'' / 1440) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 1440)
				replace `work' = `work' + string(`rest' / 60, "`hformat'") + `"`hsymbol'"'
			}
			else if `listtype' == 4 {
				replace `rest' = ``i'' / 60

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 5 {
				replace `rest' = ``i'' / 60

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 6 {
				replace `work' = string(floor(``i''), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(``i'', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}

			replace `work' = "" if `touse2' == 0
			if "`am'`pm'" != "" {
				replace `work' = `work' + cond(`ispm', "`pm'", "`am'")
			}
			local togenerate "`togenerate' `work'"
		}
	}
	else if "`unit'" == "seconds" {
		quietly forval i = 1 / `nvars' {
			tempvar work touse2

			// markout separately for each variable
			gen byte `touse2' = `touse'
			markout `touse2' ``i''
			gen `work' = ""
			if "`am'`pm'" != "" replace `ispm' = `touse2'
			local I "``i''"

			if "`timeonly'" != "" {
				replace `work' = mod(``i'', 86400)
				local `i' "`rest'"
			}

			if `listtype' == 1 {
				replace `work' = string(floor(``i'' / 86400) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 86400)

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (`rest' >= 46800)
					replace `rest' = `rest' - 43200 if `ispm'
				}

				replace `work' = `work' + string(floor(`rest' / 3600), "`hformat'") + `"`hsymbol'"'
				replace `rest' = mod(`rest', 3600)
				replace `work' = `work' + string(floor(`rest' / 60), "`mformat'") + `"`msymbol'"'
				replace `rest' = mod(`rest', 60)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 2 {
				replace `work' = string(floor(``i'' / 86400) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 86400)

				if "`am'`pm'" != "" {
					replace `ispm' = `ispm' * (`rest' >= 46800)
					replace `rest' = `rest' - 43200 if `ispm'
				}

				replace `work' = `work' + string(floor(`rest' / 3600), "`hformat'") + `"`hsymbol'"'
				replace `rest' = mod(`rest', 3600) / 60
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 3 {
				replace `work' = string(floor(``i'' / 86400) `dformat') + `"`dsymbol'"'
				replace `rest' = mod(``i'', 86400) / 3600
				replace `work' = `work' + string(`rest', "`hformat'") + `"`hsymbol'"'
			}
			else if `listtype' == 4 {
				replace `rest' = ``i'' / 3600

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(floor(`rest'), "`mformat'") + `"`msymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}
			else if `listtype' == 5 {
				replace `rest' = ``i'' / 3600

				if "`am'`pm'" != "" {
					capture assert `rest' < 24 if `touse2'
					if _rc {
						di as err "24 hours or more in `I' not compatible with am/pm"
						exit 498
					}

					replace `ispm' = `ispm' * (`rest' >= 13)
					replace `rest' = `rest' - 12 if `ispm'
				}

				replace `work' = string(floor(`rest'), "`hformat'") + `"`hsymbol'"'
				replace `rest' = 60 * mod(`rest', 1)
				replace `work' = `work' + string(`rest', "`mformat'") + `"`msymbol'"'
			}
			else if `listtype' == 6 {
				replace `work' = string(floor(``i'' / 60), "`mformat'") + `"`msymbol'"'
				replace `rest' = mod(``i'', 60)
				replace `work' = `work' + string(`rest', "`sformat'") + `"`ssymbol'"'
			}

			replace `work' = "" if `touse2' == 0
			if "`am'`pm'" != "" {
				replace `work' = `work' + cond(`ispm', "`pm'", "`am'")
			}

			local togenerate "`togenerate' `work'"
		}
	}

	// generate new variables iff all are OK
	tokenize "`togenerate'"
	local i = 1

	quietly {
		foreach v of local varlist {
			gen `v' = ``i''
			local V : word `i' of `oldvarlist'
			label var `v' "`V' (`String')"
			local ++i
		}
	}

	d `varlist'
	return local varlist "`varlist'"
end