On 8/23/08, Matthew Moss <matthew.moss / gmail.com> wrote: > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- > ## Uptime Since... (#174) > > Nice and easy one this week. Your task is to write a Ruby script that > reports the date and time of your last reboot, making use of the > `uptime` command. > Here's my Windows solution. I offer it as an object lesson about why you should always check the built-in library documentation first. I knew that Windows could report the dates in several different formats based on user settings. I wanted to be able to handle all of them, so I rolled my own parser based on checking the registry to see what format to expect. Then I discovered Time#parse.... -Adam ########################## ## Windows-uptime.rb ## Ruby Quiz # ## -Adam Shelly ## (The hard way) #find out what form windows is going to report the date in dateform = `reg query "HKCU\\Control Panel\\International" /v sShortDate`.chomp dateform = /REG_SZ\t(.*)$/m.match(dateform)[1].chomp timeform = `reg query "HKCU\\Control Panel\\International" /v sTimeFormat`.chomp timeform = /REG_SZ\t(.*)$/m.match(timeform)[1].chomp timeform.gsub!(/:[sS]+/,'') is_PM = `reg query "HKCU\\Control Panel\\International" /v s2359`.chomp is_PM = /REG_SZ\t(.*)$/m.match(is_PM)[1].chomp #these are the format characters it may use (sorted in descending order by size: year -> minute) $special="yYmMdDhHmMtT" #build a regexp that matches the format string, # record which order the result groups are returned in # Assumes that there is sone separator character # won't work if the format is something like DDMMYY def buildQuery formatstr, indexes idx=1 formatstr.each_byte{|b| if $special.include?(b.chr) indexes[b.chr.upcase]=idx else idx+=1 end } #replace all the format chars with word matchers f = formatstr.gsub(Regexp.new("[#{$special}]"),'\w') #make groups f = f.gsub(/(\\w)+/,'(\w+)') return f end #storage for regexp group indexes dateidx,timeidx={},{} #build regexp to match result regexp = Regexp.new "since "+buildQuery(dateform, dateidx)+' '+buildQuery(timeform, timeidx) #since we are combining 2 regexps, the second set of groups are offset by the number of groups in the first timeidx.each_key{|k| timeidx[k]+=dateidx.values.max} #do the match datematch = regexp.match(`net stats srv`) a=[] idxset=dateidx #build an array with the results of the match, in descending order, $special.upcase.squeeze.each_byte{|b| idxset = timeidx if b==?H #switch to time indexes when we get to Hours a<<datematch[idxset[b.chr]] } #add 12 if string contains this locale's version of 'PM' a[3]=a[3].to_i+12 if datematch[timeidx['T']]==is_PM #convert month names to integers, if needed # ??Question: Does strftime return names in the current locale?? (1..12).each{|m| a[1]=m if datematch[dateidx['M']].include?(Time.utc(0,m).strftime("%b")) } #construct the start time, calculate seconds elapsed until now starttime = Time.local(*a) s=(Time.now-starttime).to_i; #show results print datematch[0].gsub("since", "Last reboot at") puts ".\n #{s} seconds ago." #expand seconds into normalized units. (Is there a library function to do this?) puts 'uptime = '+ [['%d seconds',60],['%d minutes',60],['%d hours',24],['%d days',365],['%d years',1000]].map{|w,v| s,rem=s.divmod v; w%rem if rem>0 }.compact.reverse*' ' #################################### ## Now, the Easy way require 'Time' datematch = /since (.*)$/.match(`net stats srv`) s= Time.now - Time.parse(datematch[1]) print "\n"+datematch[0].gsub("since", "Last reboot at") puts ".\n #{s.to_i} seconds ago." puts "uptime = %.2f days."%[s/(60*60*24.0)]