Ruby - new_offset losing a second - ruby

I'm not sure if this behaviour is intended, but it seems a bit weird to me. I'm using the code from How do you get DateTime.parse to return a time in your time zone?
require 'date'
estHoursOffset = +10 # Brisbane/Australia
estOffset = Rational(estHoursOffset, 24)
With some times, the DateTime that's returned is a second earlier:
(DateTime.parse("2012-07-15 16:56:00") - (estHoursOffset/24.0)).new_offset(estOffset)
=> #<DateTime: 2012-07-15T16:55:59+10:00 (2456123.788888889,5/12,2299161)>
But with other times, it seems correct:
(DateTime.parse("2012-07-15 16:16:00") - (estHoursOffset/24.0)).new_offset(estOffset)
=> #<DateTime: 2012-07-15T16:16:00+10:00 (2456123.7611111114,5/12,2299161)>
The program I'm writing only cares about the minutes, which means I'm getting back 16:55 when I want 16:56.
So my questions are;
Is this intentional? (If so, is it documented somewhere - I haven't been able to find anything.)
Is there a simple way of fixing this programmatically? Since I don't care about seconds,I suppose I could "round up" the DateTimes returned, but it'd be good to know if this could bring up any other problems in edge cases.

This is probably because floating point numbers are imprecise - the 10/24.0 you are subtracting cannot be represented exactly.
If instead of subtracting that float you subtracted a rational, ie Rational(estHoursOffset, 24) then you should be ok

I tried both times ("2012-07-15 16:56:00" & "2012-07-15 16:16:00") and Ruby was always yielding the times parsed initially. I don't know mate how you managed to get 1 sec less; it is a miracle!! Only joking :)
If this still is giving you a hard time try getting the date (& time) - simpler like this..:
require 'date'
$date = Time.now #current date/time
puts $date
puts $date.min #if you want to use only the minutes
$date="2012-07-15 16:56:00" #if you want to parse it yourself
Moving on to your questions:
-No this is not international and it could be intermittent as well. I've tested your code above (+10h Australia) & from my location London, England (+1h). ALWAYS GOT the time parsed; never a second less or more.
Now if you need to round up the seconds so you will be 100% sure that each & every time you are getting the same results..:
def round_up(seconds)
divisor = 10**Math.log10(seconds).floor
i = seconds / divisor
remainder = seconds % divisor
if remainder == 0
i * divisor
else
(i + 1) * divisor
end
end
I cannot see why the rounding will cause problems in boundary conditions; as long as you always keep rounding everything! Hope this helps! Good luck mate :)

Related

Get the most precise time in Ruby

I want to get the most possible precis time using Ruby. For example:
3.times.map do
Thread.new do
# Expect 3 differnt results from each thread
p Time.now.precis_time
end
end.each(&:join)
However, even using the strftime, I still can not achieve the goal. So is there any other way to get this?
The most precise timer available to Ruby is Process::clock_gettime. To avoid losing precision to float rounding, use :nanosecond unit:
3.times { p Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond) }
# => 1491185078101717000
# => 1491185078101741000
# => 1491185078101747000
EDIT: This is the same time that is available by Time.now. On Linux, the two have nanosecond precision. However, there is another clock that has nanosecond precision even on OSX: CLOCK_MONOTONIC. This clock does not track time from epoch, but time from "some event", this event normally being your computer's boot time. To get the most precise time, one can take the difference between CLOCK_REALTIME and CLOCK_MONOTONIC and apply it later:
clock_diff = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond) -
Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
3.times {
nsec = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) + clock_diff
time = Time.at(nsec / 1_000_000_000, nsec % 1_000_000_000 / 1_000.0)
p time.strftime("%Y-%m-%d %H:%i:%s.%N")
}
On Linux, I think the most precise time is just Time.now. The to_r method "is intended to be used to get an accurate value representing the nanoseconds since the Epoch" (from the docs).
t = Time.now
p t.to_r # =>(1491206113721862629/1000000000)
p [t.to_i, t.nsec] # =>[1491206113, 721862629]
On JRuby, you can use java.lang.System.nano_time :
java.lang.System.nano_time - java.lang.System.nano_time
# -15607
to get nanoseconds since a fixed but arbitrary origin. From the documentation :
This method can only be used to measure elapsed time and is not
related to any other notion of system or wall-clock time. The value
returned represents nanoseconds since some fixed but arbitrary origin
time (perhaps in the future, so values may be negative). The same
origin is used by all invocations of this method in an instance of a
Java virtual machine; other virtual machine instances are likely to
use a different origin.
If you want a precise Time with Java < 9, you could use currentTimeMillis :
java.lang.System.current_time_millis
#=> 1491214503112
But then, you wouldn't get more information than from Time.now :
Time.now.to_f
#=> 1491214592.562
So Time.now might be your best bet : it will work on any Ruby version on any system. Note that nanoseconds precision doesn't mean nanoseconds accuracy.
I dare say you could ignore any digit related to a shorter time than milliseconds. You could output the distance between NYC and Los-Angeles in micrometers, it doesn't mean it would be useful though.

Calculating days in Ruby

I have an issue where, I'm trying to work out if a certain alert on a webpage is calculating sums correctly. I'm using Capybara and Cucumber.
I have an alert that calculates records that expire within 30 days. When selecting this alert, the records are listed in a table and the date is presented in the following format, "1 feb 2016"
What I want to do is somehow take today's date, compare it to the date returned in the table and ensure that it's >= 30 days from the date in the alert.
I'm able to set today's date as the same format using Time.strftime etc.
When I try things like:
And(/^I can see record "([\d]*)" MOT is calculated due within 30 days$/) do |selection1|
today = Time.now.strftime('%l %b %Y')
thirty_days = (today + 30)
first_30day_mot = first('#clickable-rows > tbody > tr:nth-child(' + selection1 + ') > td:nth-child(3)')
if today + first_30day_mot <= thirty_days
puts 'alert correct'
else
(error handler here)
end
end
As you can see, this is quite a mess.
I keep getting the error TypeError: no implicit conversion of Fixnum into String
If anyone can think of a neater way to do this, please put me out of my misery.
Thanks
There are at least a couple of things wrong with your attempt.
You're converting dates to strings and then trying to compare lengths of time with strings. You should be converting strings to dates and then comparing them
#first returns the element in the page not the contents of the element
It's not 100% clear from your code what you're trying to do, but from the test naming I think you just want to make sure the date in the 3rd td cell (which is in the 1 feb 2016 format) of a given row is less than 30 days from now. If so the following should do what you want
mot_element = first("#clickable-rows > tbody > tr:nth-child(#{selection1}) > td:nth-child(3)")
date_of_mot = Date.parse(mot_element.text)
if (date_of_mot - Date.today) < 30
puts 'alert correct'
else
#error handler
end
Beyond that, I'm not sure why you're using #first with that selector since it seems like it should only ever match one element on the page, so you might want to swap that to #find instead, which would get you the benefits of Capybaras waiting behavior. If you do actually need #first, you might consider passing the minimum: 1 option to make sure it waits a bit for the matching element to appear on the page (if this is the first step after clicking a button to go to a new page for instance)
Convert selection1 to the string explicitly (or, better, use string interpolation):
first_30day_mot = first("#clickable-rows > tbody > tr:nth-child(#{selection1}) > td:nth-child(3)")
Also, I suspect that one line below it should be converted to integer to add it to today:
first_30day_mot.to_i <= 30
UPD OK, I finally got time to take a more thorough look at. You do not need all these voodoo magic with days calculus:
# today = Time.now.strftime('%l %b %Y') # today will be a string " 3 Feb 2016"
# thirty_days = (today + 30) this was causing an error
# correct:
# today = DateTime.now # correct, but not needed
# plus_30_days = today + 30.days # correct, but not needed
first_30day_mot = first("#clickable-rows > tbody > tr:nth-child(#{selection1}) > td:nth-child(3)")
if 30 > first_30day_mot.to_i
...
Hope it helps.
I'd strongly recommend not using Cucumber to do this sort of test. You'll find its:
Quite hard to set up
Has a high runtime cost
Doesn't give enough benefit to justify the setup/runtime costs
Instead consider writing a unit test of the thing that provides the date. Generally a good unit test can easily run 10 to 100 times faster than a scenario.
Whilst with a single scenario you won't experience that much pain, once you have alot of scenarios like this the pain will accumulate. Part of the art of using Cucumber is to get plenty of bang for each scenario you write.

Python Birthday paradox math not working

it run corectly but it should have around 500 matches but it only has around 50 and I dont know why!
This is a probelm for my comsci class that I am having isues with
we had to make a function that checks a list for duplication I got that part but then we had to apply it to the birthday paradox( more info here http://en.wikipedia.org/wiki/Birthday_problem) thats where I am runing into problem because my teacher said that the total number of times should be around 500 or 50% but for me its only going around 50-70 times or 5%
duplicateNumber=0
import random
def has_duplicates(listToCheck):
for i in listToCheck:
x=listToCheck.index(i)
del listToCheck[x]
if i in listToCheck:
return True
else:
return False
listA=[1,2,3,4]
listB=[1,2,3,1]
#print has_duplicates(listA)
#print has_duplicates(listB)
for i in range(0,1000):
birthdayList=[]
for i in range(0,23):
birthday=random.randint(1,365)
birthdayList.append(birthday)
x= has_duplicates(birthdayList)
if x==True:
duplicateNumber+=1
else:
pass
print "after 1000 simulations with 23 students there were", duplicateNumber,"simulations with atleast one match. The approximate probibilatiy is", round(((duplicateNumber/1000)*100),3),"%"
This code gave me a result in line with what you were expecting:
import random
duplicateNumber=0
def has_duplicates(listToCheck):
number_set = set(listToCheck)
if len(number_set) is not len(listToCheck):
return True
else:
return False
for i in range(0,1000):
birthdayList=[]
for j in range(0,23):
birthday=random.randint(1,365)
birthdayList.append(birthday)
x = has_duplicates(birthdayList)
if x==True:
duplicateNumber+=1
print "after 1000 simulations with 23 students there were", duplicateNumber,"simulations with atleast one match. The approximate probibilatiy is", round(((duplicateNumber/1000.0)*100),3),"%"
The first change I made was tidying up the indices you were using in those nested for loops. You'll see I changed the second one to j, as they were previously bot i.
The big one, though, was to the has_duplicates function. The basic principle here is that creating a set out of the incoming list gets the unique values in the list. By comparing the number of items in the number_set to the number in listToCheck we can judge whether there are any duplicates or not.
Here is what you are looking for. As this is not standard practice (to just throw code at a new user), I apologize if this offends any other users. However, I believe showing the OP a correct way to write a program should be could all do us a favor if said user keeps the lack of documentation further on in his career.
Thus, please take a careful look at the code, and fill in the blanks. Look up the python doumentation (as dry as it is), and try to understand the things that you don't get right away. Even if you understand something just by the name, it would still be wise to see what is actually happening when some built-in method is being used.
Last, but not least, take a look at this code, and take a look at your code. Note the differences, and keep trying to write your code from scratch (without looking at mine), and if it messes up, see where you went wrong, and start over. This sort of practice is key if you wish to succeed later on in programming!
def same_birthdays():
import random
'''
This is a program that does ________. It is really important
that we tell readers of this code what it does, so that the
reader doesn't have to piece all of the puzzles together,
while the key is right there, in the mind of the programmer.
'''
count = 0
#Count is going to store the number of times that we have the same birthdays
timesToRun = 1000 #timesToRun should probably be in a parameter
#timesToRun is clearly defined in its name as well. Further elaboration
#on its purpose is not necessary.
for i in range(0,timesToRun):
birthdayList = []
for j in range(0,23):
random_birthday = random.randint(1,365)
birthdayList.append(random_birthday)
birthdayList = sorted(birthdayList) #sorting for easier matching
#If we really want to, we could provide a check in the above nester
#for loop to check right away if there is a duplicate.
#But again, we are here
for j in range(0, len(birthdayList)-1):
if (birthdayList[j] == birthdayList[j+1]):
count+=1
break #leaving this nested for-loop
return count
If you wish to find the percent, then get rid of the above return statement and add:
return (count/timesToRun)
Here's a solution that doesn't use set(). It also takes a different approach with the array so that each index represents a day of the year. I also removed the hasDuplicate() function.
import random
sim_total=0
birthdayList=[]
#initialize an array of 0's representing each calendar day
for i in range(365):
birthdayList.append(0)
for i in range(0,1000):
first_dup=True
for n in range(365):
birthdayList[n]=0
for b in range(0, 23):
r = random.randint(0,364)
birthdayList[r]+=1
if (birthdayList[r] > 1) and (first_dup==True):
sim_total+=1
first_dup=False
avg = float(sim_total) / 1000 * 100
print "after 1000 simulations with 23 students there were", sim_total,"simulations with atleast one duplicate. The approximate problibility is", round(avg,3),"%"

Get the count of elements in a ruby range made of Time objects

How would I be able to get the size or count of a range made up of Time objects?
Something that would achieve the same result as my pseudo Ruby code, which doesn't work:
((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).size == 30
currently doing the above gives an error:
NoMethodError: undefined method `size' for 2012-05-18 23:59:59 -0400..2012-06-17 23:59:59 -0400:Range
and trying to turn it into array (range).to_a :
can't iterate from Time
update
Interesting, Just tried to do
((Date.today.end_of_day - 31.days)..(Date.today.end_of_day - 1.day)).count
Users/.../gems/ruby/1.9.1/gems/activesupport-3.0.15/lib/active_support/time_with_zone.rb:322: warning: Time#succ is obsolete; use time + 1
However
((Date.today - 31.days)..(Date.today - 1.day)).count == 31
I would be willing to settle for that?
Also ((Date.today - 31.days)..(Date.yesterday)).count == 31
update 2
On the other hand, taking Mu's hint we can do:
(((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).first.to_date..((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).last.to_date).count == 31
There's no such method as Range#size, try Range#count (as suggested by Andrew Marshall), though it still won't work for a range of Time objects.
If you want to perform number-of-days computations, you're better off using Date objects, either by instantiating them directly (Date.today - 31, for example), or by calling #to_date on your Time objects.
Date objects can be used for iteration too:
((Date.today - 2)..(Date.today)).to_a
=> [#<Date: 2012-06-17 ((2456096j,0s,0n),+0s,2299161j)>,
#<Date: 2012-06-18 ((2456097j,0s,0n),+0s,2299161j)>,
#<Date: 2012-06-19 ((2456098j,0s,0n),+0s,2299161j)>]
((Date.today - 2)..(Date.today)).map(&:to_s)
=> ["2012-06-17", "2012-06-18", "2012-06-19"]
It's because a size for a date range doesn't make senseā€”it doesn't know if you want to view it as days, minutes, seconds, months, or something else. The reason the error mentions iterating is that in order to determine the size of the range, it must know how to iterate over them so that it may count the number of elements.
Since what you want is the difference in days, just do that:
date_one = Time.now.end_of_day - 31.days
date_two = Time.now.end_of_day - 1.day
((date_one - date_two) / 1.day).abs
#=> 30.0
You must divide by 1.day since a difference of Times returns seconds.
To have any chance of your code working you should wrap everything before .size in parentheses.
Instead of using a range, maybe you can just subtract one time object from another?
I know you make ranges out of Date objects so you could convert to that.

using probability for rounding decimals

What might be a simple Ruby way to round numbers using probability, i.e., based on how close the value is to one boundary or the other (floor or ceiling)?
For example, given a current price value of 28.33, I need to add 0.014.
Equivalent to starting with 28.34 and needing to add 0.004, but the final value must be rounded to two decimal places(which can be provided as parameter, or fixed for now).
The final value should therefore be:
28.34 with 60% chance, since it is that much closer, OR
28.35 with 40% random chance
The reason it occured to me this could serve best is that the application is stateless and independent across runs, but still needs to approximate the net effect of accumulating the less significant digits normally rounded into oblivion (eg. micropenny values that do have an impact over time). For example, reducing a stop-loss by some variable increment every day (subtraction like -0.014 above instead).
It would be useful to extend this method to the Float class directly.
How about:
rand(lower..upper) < current ? lower.round(2) : upper.round(2)
EDIT:
The above will only work if you use Ruby 1.9.3 (due to earlier versions not supporting rand in a range).
Else
random_number = rand * (upper-lower) + lower
random_number < current ? lower.round(2) : upper.round(2)
Wound up using this method:
class Float
def roundProb(delta, prec=2)
ivalue=self
chance = rand # range 0..1, nominally averaged at 0.5
# puts lower=((ivalue + delta)*10**prec -0.5).round/10.0**prec # aka floor
# puts upper=((ivalue + delta)*10**prec +0.5).round/10.0**prec # ceiling
ovalue=((ivalue + delta)*10**prec +chance-0.5).round/10.0**prec # proportional probability
return ovalue
rescue
puts $#, $!
end
end
28.33.roundProb(0.0533)
=> 28.39
Maybe not the most elegant approach but seems to work for the general case of any precision, default 2. Even works on Ruby 1.8.7 I'm stuck with in one case, which lacks a precision parameter to round().

Resources