Adding times in ruby on rails - ruby-on-rails

I currently have a model called Job which has an attribute called CPU. This corresponds to the CPU time that a job was running. I would like to add all the time attributes of all the jobs for a specific date. This column is in the time format 00:00:00. Therefore I thought this would work:
def self.cpu_time
sum(:cpu)
end
Which returns the following "Sat Jan 01 00:00:00 UTC 2000".
For my test data, I used the following cpu times:
00:00:46
00:26:46
Any help would be appreciated
This solved my problem, although it doesnt seem to be the rails way:
def self.cput
#times = find(:all,
:select => 'cput')
#total_time =0
for time in #times do
#total_time += time.cput.to_i - 946684800
end
#total_time
end

You don't state what data type you're using for the cpu column. Personally I would store it in seconds and then convert to hours, minutes and seconds after summing it.

Related

Time datatype in Rails

I created a database using rails with stime attribute (referring to the starting time). Its type is Time. When I added a record with:
stime = "10:10"
it has been added as:
"stime":"2000-0101T10:10:00.000Z"
I only want the time, not the date. I thought it's the difference between Time and DateTime. Could anyone tell me what the problem is?
As the documentation states, it is not.
Time is an abstraction of dates and times. Time is stored internally
as the number of seconds with fraction since the Epoch, January 1,
1970 00:00 UTC.
There are several things you can do:
Store it as a string and do regex validation.
If you are using Postgres and don't mind depending on it, you can use time type. This would allow you do things like: YourModel.where("stime > '13:00:00'").
Store the time of the day in seconds since midnight. This will require you to add some helper methods for creating and scopes for querying data. You might find ActiveSupport's extension on numeric values helpful.
One way of handling this issue, to exemplify Uzbekjon's third point, is storing the time as seconds since midnight in an integer column:
stime = Time.now
# => 2016-04-13 10:58:13 -0700
seconds = stime.seconds_since_midnight.to_i
# => 39493
You can then retrieve this value and parse it into the time of day when needed:
time = Time.at(seconds).utc
# => 1970-01-01 10:58:13 UTC
result = time.strftime("%I:%M")
# => "10:58"
Hope it helps!

How to quickly convert a date into seconds for comparison in Ruby

I am writing a method to return how many seconds old a user is. I'm having a lot of issues with an error that I don't understand. It's as follows:
(eval):1: (eval):1: uninitialized constant Date (NameError)
I've created a class Age < Date so that I can use all the methods in the Date module as well. The issue I keep running into is with my in_seconds method as follows:
def in_seconds
current = Time.now
bday = Date.new(year, month, day) # this is the birthday of the user
age = (current - bday).to_s
return "You are #{age} years old."
end
I'm considering revising the initialize method to include the parameters (YYYY, MM, DD) so that I can keep a running birthday date for each user, as follows:
John = Age.new(1985, 04, 27)
# sets #age to 910,993,128 seconds
Angela = Age.new(1991, 03, 15)
# sets #age to 725,405,928 seconds
My biggest issue is that I can't seem to get rid of that error up there. Ruby keeps throwing back the fact that there's an uninitialized constant. What constant? After a few hours of research, I've drawn a complete blank. How can I remedy that error?
I'm not sure you can subtract a Date from a Time. I'd suggest using Time.new to get your bday variable. That will also get rid of your error - #MarkThomas is right about the need to require that library.
It's not clear why Age needs to be a subclass of Date (or of Time, for that matter) to do what you want to do. If you add this method to any object, it will calculate the Time in seconds between a date and now:
def in_seconds(year, month, day)
back_then = Time.new(year, month, day)
seconds_since_then = Time.now - back_then
end
I've figured out a way to get rid of the issues thanks to Steve Rowley's suggestion that I can't compare a Date object to a Time object. I decided to only use Time objects to get ages converted to integers as follows:
require 'time'
class Age
def initialize(year, month, day)
# the parameters are assumed to be integers
#age = Time.parse("#{year}-#{month}-#{day}").to_i
end
def in_sec
return Time.now.to_i - #age
end
end
So far, I'm not having any issues. It seems like the biggest problem was coming from using the Date module at all.
umm
date.to_time.to_i
converts it into seconds after Jan 1 1970

Rails. How to store time of day (for schedule)?

I'm writing an app that keeps track of school classes.
I need to store the schedule. For example: Monday-Friday from 8:am-11am.
I was thinking about using a simple string column but I'm going to need to make time calculations later.
For example, I need to store a representation of 8am, such as start_at:8am end_at:11am
So how should I store the time? What datatype should I use? Should I store start time and number of seconds or minutes and then calculate from there? or is there an easier way?
I use MySQL for production and SQLite for development.
I made an app recently that had to tackle this problem. I decided to store open_at and closed_at in seconds from midnight in a simple business hour model. ActiveSupport includes this handy helper for finding out the time in seconds since midnight:
Time.now.seconds_since_midnight
This way I can do a simple query to find out if a venue is open:
BusinessHour.where("open_at > ? and close_at < ?", Time.now.seconds_since_midnight, Time.now.seconds_since_midnight)
Any tips for making this better would be appreciated =)
If you're using Postgresql you can use a time column type which is just the time of day and no date. You can then query
Event.where("start_time > '10:00:00' and end_time < '12:00:00'")
Maybe MySQL has something similar
Check out the gem 'tod' for Rails 4 or Time_of_Day for Rails 3. They both solve the problem of storing time in a database while using an an Active Record model.
SQL has a time data type but Ruby does not. Active Record addresses this difference by representing time attributes using Ruby’s Time class on the canonical date 2000-01-01. All Time attributes are arbitrarily assigned the same dates. While the attributes can be compared with one another without an issue, (the dates are the same), errors arise when you attempt to compare them with other Time instances. Simply using Time.parse on a string like ”10:05” adds today’s date to the output.
Lailson Bandeira created a created solution for this problem, the Time_of_Day gem for Rails 3. Unfortunately the gem is no longer maintained. Use Jack Christensen’s ‘tod’ gem instead. It works like a charm.
This ruby gem converts time of day to seconds since midnight and back. The seconds value is stored in the database and can be used for calculations and validations.
Define the time of day attributes:
class BusinessHour < ActiveRecord::Base
time_of_day_attr :opening, :closing
end
Converts time of day to seconds since midnight when a string was set:
business_hour = BusinessHour.new(opening: '9:00', closing: '17:00')
business_hour.opening
=> 32400
business_hour.closing
=> 61200
To convert back to time of day:
TimeOfDayAttr.l(business_hour.opening)
=> '9:00'
TimeOfDayAttr.l(business_hour.closing)
=> '17:00'
You could also omit minutes at full hour:
TimeOfDayAttr.l(business_hour.opening, omit_minutes_at_full_hour: true)
=> '9'
I would store the starting hour and the duration within the database, using two integer columns.
By retrieving both values, you could convert the starting hour as in (assuming that you know the day already:
# assuming date is the date of the day, datetime will hold the start time
datetime = date.change({:hour => your_stored_hour_value , :min => 0 , :sec => 0 })
# calculating the end time
end_time = datetime + your_stored_duration.seconds
Otherwise, hava a look at Chronic. The gem makes handling time a little bit easier. Note that the changemethod is part of rails, and not available in plain Ruby.
The documentation on DateTime for plain Ruby can be found here.
Also, whatever you do, don't start storing your dates/time in 12-hour format, you can use I18nin Rails to convert the time:
I18n.l Time.now, :format => "%I.%m %p", :locale => :"en"
I18n.l Time.now + 12.hours, :format => "%I.%m %p", :locale => :"en"
You can also get from this notation, that you can store you duration in hours, if you want, you can then convert them rather easily by:
your_stored_value.hours
if stored as an integer, that is.
Suggestion:
Don’t worry about a specific datatype for that. A simple solution would be:
In the database, add an integer type column for start_time and another for end_time. Each will store the number of minutes since midnight.
Ex: 8:30am would be stored as 510 (8*60+30)
In the form, create a select field (dropdown) that displays all available times in time format:Ex.: 10am, 10:30am and so on.
But the actual field values that get saved in the database are their integer equivalents:
Ex: 600, 630 and so on (following the example above)
I assume you are using some kind of database for this. If you are using MySQL or Postgresql, you can use the datetime column type, which Ruby/Rails will automatically convert to/from a Time object when reading/writing to the database. I'm not sure if sqlite has something similar, but I imagine it probably does.
From the SQLite 3 website,
"SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
Applications can chose to store dates and times in any of these formats and freely convert between formats using the built-in date and time functions."
You can then manipulate the values using the Date and Time functions outlined here.

Why do my time-related unit tests break every day at 4pm?

For a long time I've been having this issue that at a certain time of the day, a TON of my tests break. I have a lot of tests that are doing simple date comparisons and everything runs fine from midnight to like 4:00 in the afternoon. Any idea why this is happening? I've set my timezone in my environment file too.
It seems like some of my calls like 5.days.from_now.to_date are adding an extra day.
Edit
For instance, this test fails:
# Widget that creates items for how many days the trip is gone.
def test_should_create_correct_amount_of_days_for_trip
w = DayWidget.create(:trip => trips(:hawaii))
assert_equal w.days.size, 5
end
# Code in trip model that calculates amount of days
def number_of_days
(self.return_date.to_date - self.depart_date.to_date).to_i + 1
end
# Test fixture yaml for Hawaii
hawaii:
depart_date: <%= Time.now.tomorrow.to_s(:db) %>
return_date: <%= 5.days.from_now.to_s(:db) %>
After 4:00 pm, the test above fails and says it created 6 days instead of 5. :(
You're probably in the Pacific time zone, 8 hours behind UTC (which is why at 4:00p they start breaking, since that's when it hits midnight UTC).
Without seeing your test/comparison code, all I could say is to make sure you're comparing dates/times with the same location (UTC to UTC or localtime to localtime).
Update: Ok, it looks like Time.now returns a Time object, whereas using XXX.days.from_now returns an ActiveSupport::TimeWithZone object, resulting in different handling of timezones:
ruby-1.9.2-p136 :009 > (Time.now+5.days).to_s(:db)
=> "2011-02-08 19:40:24"
ruby-1.9.2-p136 :010 > 5.days.from_now.to_s(:db)
=> "2011-02-09 03:40:29"
My recommendation would be to either call .utc on your times in your fixtures like so:
ruby-1.9.2-p136 :017 > 5.days.from_now.utc.to_s(:db)
=> "2011-02-09 03:42:39"
ruby-1.9.2-p136 :018 > (Time.now+5.days).utc.to_s(:db)
=> "2011-02-09 03:42:39"
or to just switch to using 1.day.from_now instead of Time.now.tomorrow to keep the types consistent.
You can also use Time.current instead of Time.now.
Time.current returns ActiveSupport::TimeWithZone just like #ago and #from_now, so you can safely compare dates returned by them.
If you are truly dealing with just dates, be sure to set the hours, minutes and seconds to 0, otherwise you are at the mercy of the time of day you create the dates.

Inconsistent behavior of Time.now in Ruby/Rails app

I have a Ruby on Rails app that has a scheduled job I execute using cron and script/runner.
The purpose of the job is to find any updates that are past due ('stale', see below) and (after executing the job) update the 'due date' of the job a pre-defined number of minutes into the future.
The problem I'm having is that when I set the 'due date', the "GMT" time is used. However, when I search for stale jobs, the 'local' time seems to be used. The local time of the server is -7 hours offset from GMT, so the system goes 7 extra hours between updates.
Example: The due date of the job is 06:00 on a certain day. The system finds that job at 06:00 west coast time (-7 GMT). If the job is due again in 15 mins, it sets the 'due date' to 06:15 which gets changed to 13:15 when the object is saved (15 mins and 7 hours into the future).
I'll insert the code below. If it makes any difference, 'mark_updated' is part of a model and self.stale is a class-level function in a module that is included in the same model.
BTW...bonus question - how to get catch the SQL that is being executed from script/runner job in Production? That would make this easier to debug. I'm guessing a little at what is happening.
def mark_updated
self.next_refresh_due_at = Time.now + update_interval_in_minutes.minutes
save
end
def self.stale(max = 3)
news_sources = NewsSource.find(:all,
:conditions => ["next_refresh_due_at < ?", Time.now],
:order => 'next_refresh_due_at ASC',
:limit => max)
return news_sources
end
I'm assuming you have the time zone specified in your environment.rb config file. In that case you should use Time.zone.now instead of Time.now.
See this answer for a full explanation.

Resources