distance_of_time_in_words, smarter formatting? - ruby-on-rails

Currently I'm using the following:
distance_of_time_in_words(tweet.created_at, Time.now + 30.minutes, include_seconds: false)
This works fine in most scenarios, but I'd like certain exceptions to be formatted differently, and the documentation is rather confusing for this scenario. (I also couldn't find any questions on here about it)
As an example, currently the function converts the time to this before 1 day has passed.
5 hours and 17 minutes ago
I'm using it for tweets posted and times such as
Tweeted 4 days, 8 hours, and 3 minutes
Seem a little long winded and unnecessarily specific.
Ideally I'd be like to be able to display:
less than a minute = the number of seconds ago
less than 1 hour = the amount of minutes
less than a day = the amount of hours
more than a day = the number of days
Is there a way of doing this?

Perhaps you want to use the distance_of_time_in_words helper:
0 <-> 29 secs # => less than a minute
30 secs <-> 1 min, 29 secs # => 1 minute
1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
and so on...
You can use that helper with just one argument. Something like this in your view should do the job:
Tweeted <%= time_ago_in_words(tweet.created_at) ago
Assuming tweet.created_at is about 29 hours ago it would render to:
Tweeted 1 day ago
see: http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-distance_of_time_in_words

Here's a non-Rails solution, though it appears you are better off using Rails' distance_of_time_in_words helper. Looking through the documentation, the latter seems to offer tremendous flexibility.
require 'time'
SECS_PER_MIN = 60
SECS_PER_HOUR = 60 * SECS_PER_MIN
SECS_PER_DAY = 24 * SECS_PER_HOUR
SECS_PER_WEEK = 7 * SECS_PER_DAY
SECS_PER_YEAR = 365 * SECS_PER_DAY
SECS_PER_CENT = 100 * SECS_PER_YEAR
SECS_PER_MLNM = 10 * SECS_PER_CENT
SECS_PER_EON = 1_000_000 * SECS_PER_MLNM
def message(secs)
case secs
when (0...SECS_PER_MIN)
"#{secs} seconds"
when (SECS_PER_MIN...SECS_PER_HOUR)
"#{secs/SECS_PER_MIN} minutes"
when (SECS_PER_HOUR...SECS_PER_DAY)
"#{secs/SECS_PER_HOUR} hours"
when (SECS_PER_DAY...SECS_PER_WEEK)
"#{secs/SECS_PER_DAY} days"
when (SECS_PER_WEEK...SECS_PER_YEAR)
"#{secs/SECS_PER_WEEK} weeks"
when (SECS_PER_YEAR...SECS_PER_CENT)
"#{secs/SECS_PER_YEAR} years"
when (SECS_PER_CENT...SECS_PER_MLNM)
"#{secs/SECS_PER_CENT} centuries"
when (SECS_PER_MLNM...SECS_PER_EON)
"#{secs/SECS_PER_MLNM} millenia"
else
"#{secs/SECS_PER_EON} eons"
end << " ago"
end
You would call message with something like:
message(Time.now-tweet_time)
Let's try it.
message(10) #=> "10 seconds ago"
message(1_000) #=> "16 minutes ago"
message(20_000) #=> "5 hours ago"
message(300_000) #=> "3 days ago"
message(10_000_000) #=> "16 weeks ago"
message(1_000_000_000) #=> "31 years ago"
message(25_000_000_000) #=> "7 centuries ago"
message(1_300_000_000_000) #=> "41 millenia ago"
message(100_000_000_000_000_000) #=> "3 eons ago"
You could prettify it by changing, for example,
when (SECS_PER_DAY...SECS_PER_WEEK)
"#{secs/SECS_PER_DAY} days"
to
when (SECS_PER_DAY...SECS_PER_WEEK)
n = secs/SECS_PER_DAY
n == 1 ? "1 day" : "#{n} days"

The output of distance_of_time_in_words (which is just an alias for time_ago_in_words) should look like this:
0 <-> 29 secs # => less than a minute
30 secs <-> 1 min, 29 secs # => 1 minute
1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month
44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months
59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
1 yr <-> 1 yr, 3 months # => about 1 year
1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
2 yrs <-> max time or date # => (same rules as 1 yr)
http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-distance_of_time_in_words
That looks like what you want. Something must be overriding distance_of_time_in_words? I'm not sure why anything would do that, but that seems to be the case.

Related

google real time data of rolling 30 mins at interval of 5 min

I have google real time data of rolling 30 mins at interval of 5 min.
how can i get data of 5 min each, share logic for the same thanks
i have data like
01 05 am.............
10 00 am 20 user(data of 30 mins i.e 9 30 am to 10 00 am)
10 05 am 25 user(data of 30 mins i.e 9 35 am to 10 05 am)
10 10 am 19 user(data of 30 mins i.e 9 45 am to 10 15 am)
10 15 am 26 user(data of 30 mins i.e 9 50 am to 10 20 am)
10 20 am 17 user(data of 30 mins i.e 9 55 am to 10 25 am)
and so on on interval of 5 mins.
now i have figure out user which were there from 10 05 am to 10 10 am i.e of 5 mins but i have data of 30 mins which is refreshed on 5 mins interval.
Answer: The realtime.get method does not accept a start and end date it returns what there is.
The Google Analytics real time api gives you information about the current activity on your website. This data is like all other google analytics data. It is anonymous there is no way of knowing who these users were.
My experience its the data for around the last 15 minutes. I suggest you try adding the rt:minutesAgo dimension in order to see how long ago these users where online.
You need to remember this is a Beta API it is not a full fully functional api.

Case,Substring and concat within a cognos Data Item Expression

I'm scratching my head trying to work with time functions within Cognos 10.2.1 (Report Studio), using an Informix db as a data source.
My time field is stored as a smallint, 4 digits, representing the 24 hour clock. I am trying to get the time to display as 6:00pm, 11:30am, 3:00pm, etc. I have a separate data expression that calculates the string 'AM' or 'PM' depending on the hour value, but I'm running into some errors when doing the overall concat/substring function.
case when char_length([Query1].[beg_tm]) = 4
then (substring(cast([StartTime], char(5)), 1, 2)) || ':' || (substring (cast ([StartTime], char(5)), 3, 2)) || ([beg_AMPMcalc])
when char_length([Query1].[beg_tm]) = 3
then (substring(cast([StartTime], char(5)), 1, 1)) || ':' || (substring(cast ([StartTime], char(5)), 3, 2)) || ([beg_AMPMcalc])
else '--'
end
Why not use DATETIME HOUR TO MINUTE; at least you then only have to deal with converting 24 hour clock to 12 hour clock. Is midnight stored as 0 and noon as 1200, and the minute before midnight as 2359? Cognos uses a fairly modern version of Informix, I believe, so you should be able to use the TO_CHAR function:
DROP TABLE IF EXISTS times;
CREATE TEMP TABLE times(p_time SMALLINT);
INSERT INTO times VALUES(0);
INSERT INTO times VALUES(59);
INSERT INTO times VALUES(100);
INSERT INTO times VALUES(845);
INSERT INTO times VALUES(1159);
INSERT INTO times VALUES(1200);
INSERT INTO times VALUES(1259);
INSERT INTO times VALUES(1300);
INSERT INTO times VALUES(1815);
INSERT INTO times VALUES(2359);
SELECT TO_CHAR(CURRENT HOUR TO MINUTE, "%I:%M %p"),
p_time,
DATETIME(00:00) HOUR TO MINUTE + MOD(p_time, 100) UNITS MINUTE + (p_time/100) UNITS HOUR,
TO_CHAR(DATETIME(00:00) HOUR TO MINUTE + MOD(p_time, 100) UNITS MINUTE + (p_time/100) UNITS HOUR, "%I:%M %p")
FROM times;
Output:
03:49 AM 0 00:00 12:00 AM
03:49 AM 59 00:59 12:59 AM
03:49 AM 100 01:00 01:00 AM
03:49 AM 845 08:45 08:45 AM
03:49 AM 1159 11:59 11:59 AM
03:49 AM 1200 12:00 12:00 PM
03:49 AM 1259 12:59 12:59 PM
03:49 AM 1300 13:00 01:00 PM
03:49 AM 1815 18:15 06:15 PM
03:49 AM 2359 23:59 11:59 PM
I'm using a database server that has its local time set to UTC, and I'm in time zone -07:00 (US/Pacific); the current time isn't the middle of the night where I am.

Calculate number of business days between two days

I need to calculate the number of business days between two dates. How can I pull that off using Ruby (or Rails...if there are Rails-specific helpers).
Likewise, I'd like to be able to add business days to a given date.
So if a date fell on a Thursday and I added 3 business days, it would return the next Tuesday.
Take a look at business_time. It can be used for both the things you're asking.
Calculating business days between two dates:
wednesday = Date.parse("October 17, 2018")
monday = Date.parse("October 22, 2018")
wednesday.business_days_until(monday) # => 3
Adding business days to a given date:
4.business_days.from_now
8.business_days.after(some_date)
Historical answer
When this question was originally asked, business_time didn't provide the business_days_until method so the method below was provided to answer the first part of the question.
This could still be useful to someone who didn't need any of the other functionality from business_time and wanted to avoid adding an additional dependency.
def business_days_between(date1, date2)
business_days = 0
date = date2
while date > date1
business_days = business_days + 1 unless date.saturday? or date.sunday?
date = date - 1.day
end
business_days
end
This can also be fine tuned to handle the cases that Tipx mentions in the way that you would like.
We used to use the algorithm suggested in the mikej's answer and discovered that calculating 25,000 ranges of several years each takes 340 seconds.
Here's another algorithm with asymptotic complexity O(1). It does the same calculations in 0.41 seconds.
# Calculates the number of business days in range (start_date, end_date]
#
# #param start_date [Date]
# #param end_date [Date]
#
# #return [Fixnum]
def business_days_between(start_date, end_date)
days_between = (end_date - start_date).to_i
return 0 unless days_between > 0
# Assuming we need to calculate days from 9th to 25th, 10-23 are covered
# by whole weeks, and 24-25 are extra days.
#
# Su Mo Tu We Th Fr Sa # Su Mo Tu We Th Fr Sa
# 1 2 3 4 5 # 1 2 3 4 5
# 6 7 8 9 10 11 12 # 6 7 8 9 ww ww ww
# 13 14 15 16 17 18 19 # ww ww ww ww ww ww ww
# 20 21 22 23 24 25 26 # ww ww ww ww ed ed 26
# 27 28 29 30 31 # 27 28 29 30 31
whole_weeks, extra_days = days_between.divmod(7)
unless extra_days.zero?
# Extra days start from the week day next to start_day,
# and end on end_date's week date. The position of the
# start date in a week can be either before (the left calendar)
# or after (the right one) the end date.
#
# Su Mo Tu We Th Fr Sa # Su Mo Tu We Th Fr Sa
# 1 2 3 4 5 # 1 2 3 4 5
# 6 7 8 9 10 11 12 # 6 7 8 9 10 11 12
# ## ## ## ## 17 18 19 # 13 14 15 16 ## ## ##
# 20 21 22 23 24 25 26 # ## 21 22 23 24 25 26
# 27 28 29 30 31 # 27 28 29 30 31
#
# If some of the extra_days fall on a weekend, they need to be subtracted.
# In the first case only corner days can be days off,
# and in the second case there are indeed two such days.
extra_days -= if start_date.tomorrow.wday <= end_date.wday
[start_date.tomorrow.sunday?, end_date.saturday?].count(true)
else
2
end
end
(whole_weeks * 5) + extra_days
end
business_time has all the functionallity you want.
From the readme:
#you can also calculate business duration between two dates
friday = Date.parse("December 24, 2010")
monday = Date.parse("December 27, 2010")
friday.business_days_until(monday) #=> 1
Adding business days to a given date:
some_date = Date.parse("August 4th, 1969")
8.business_days.after(some_date) #=> 14 Aug 1969
Here is my (non gem and non holiday) weekday count example:
first_date = Date.new(2016,1,5)
second_date = Date.new(2016,1,12)
count = 0
(first_date...second_date).each{|d| count+=1 if (1..5).include?(d.wday)}
count
Take a look at Workpattern. It alows you to specify working and resting periods and can add/subtract durations to/from a date as well as calculate the minutes between two dates.
You can set up workpatterns for different scenarios such as mon-fri working or sun-thu and you can have holidays and whole or part days.
I wrote this as away to learn Ruby. Still need to make it more Ruby-ish.
Based on #mikej's answer. But this also takes into account holidays, and returns a fraction of a day (up to the hour accurancy):
def num_days hi, lo
num_hours = 0
while hi > lo
num_hours += 1 if hi.workday? and !hi.holiday?
hi -= 1.hour
end
num_hours.to_f / 24
end
This uses the holidays and business_time gems.
Simple script to calculate total number of working days
require 'date'
(DateTime.parse('2016-01-01')...DateTime.parse('2017-01-01')).
inject({}) do |s,e|
s[e.month]||=0
if((1..5).include?(e.wday))
s[e.month]+=1
end
s
end
# => {1=>21, 2=>21, 3=>23, 4=>21, 5=>22, 6=>22, 7=>21, 8=>23, 9=>22, 10=>21, 11=>22, 12=>22}
There are two problems with the most popular solutions listed above:
They involve loops to count every single day between each date (meaning that performance gets worse the further apart the dates are.
They are unclear about whether they count from the beginning of the day or the end. If you count from the morning, there is one weekday between Friday and Saturday. If you count from the night, there are zero weekdays between Friday and Saturday.
After stewing over it, I propose this solution that addresses both problems. The below takes a reference date and an other date and calculates the number of weekdays between them (returning a negative number if other is before the reference date). The argument eod_base controls whether counting is done from end of day (eod) or start of day. It could be written more compactly but hopefully it's relatively easy to understand and it doesn't require gems or rails.
require 'date'
def weekdays_between(ref,otr,eod_base=true)
dates = [ref,otr].sort
return 0 if dates[0] == dates[1]
full_weeks = ((dates[1]-dates[0])/7).floor
dates[eod_base ? 0 : 1] += (eod_base ? 1 : -1)
part_week = Range.new(dates[0],dates[1])
.inject(0){|m,v| (v.wday >=1 && v.wday <= 5) ? (m+1) : m }
return (otr <=> ref) * (full_weeks*5 + part_week)
end

Ruby on Rails - Working with times

If in a database (MySQL), I have a datetime column (ex. 1899-12-30 19:00:00), how do I sum 1 day to it?
Following http://corelib.rubyonrails.org/classes/Time.html#M000240
If I want to add 1 day, it actually adds 60*60*24 days (86,400 days)
r=Record.find(:first)
=>Sat, 30 Dec 1899 19:00:00 -0600
r.date + (60*60*24)
=>Fri, 20 Jul 2136 19:00:00 -0600
But if I do this it actually adds 1 day:
t = Time.now
=>Mon Jun 14 10:32:51 -0600 2010
t + (60 * 60 * 24)
=>Tue Jun 15 10:33:21 -0600 2010
I guess it has to do with the format...how do I make this work?
You're actually adding 86,400 seconds (60 seconds * 60 minutes * 24 hours).
ActiveSupport has some built in helper methods for dealing with time:
Time.now + 1.day + 15.hours
In Rails,
its very simple to use times.
r = Record.find(:first)
r.created_at + 1.day # this will give you a day to one day ahead )
r.created_at + 2.days + 15.hours + 30.minutes + 5.seconds
or use Time.now
Also, if you want take a look at the by_star plugin/gem its makes some querying etc very easy.

Why is Rails date comparison not working?

What am I missing here, it's driving me crazy..
>> user.current_login_at.utc > 24.hours.ago.utc
=> false
>> 24.hours.ago.utc
=> Mon May 17 18:46:16 UTC 2010
>> user.current_login_at.utc
=> Mon May 17 15:47:44 UTC 2010
user.current_login_at was 27 hours ago, yet the greater than comparison says it was not greater than 24 hours ago. It leaves me scratching my head..
"Greater than" for a date means "after". 24.hours.ago gives a time, not a length of time. Conceptually, you are asking "did the user log in after the time that was 24 hours ago?", not "did the user log in more than 24 hours ago?". So you want:
user.current_login_at.utc < 24.hours.ago.utc
Which says "did the user log in before 24 hours ago?"
You are using "greater than", which in date comparisons, means "later than"
Is current login later than 24 hours ago?
user.current_login_at.utc > 24.hours.ago.utc
Is current login earlier than 24 hours ago?
user.current_login_at.utc < 24.hours.ago.utc

Resources