In ruby how can i count number of months between two months using their names
Examples:
Feb to Oct => 9
Dec to Mar => 4
Apr to Aug => 5
How can i achieve this ?
def months_between( start_month, end_month)
month_names = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
(12 + month_names.index( end_month ) - month_names.index( start_month ) ) % 12 + 1
end
You could define an array of 12 elements, containing your possible month names. Then, when you need to find the number of months between month1 and month2, you need to find their indexes, possibly using a hash, like this:
#let month1 and month2 be the values
array = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
hash = Hash[array.map.with_index.to_a] # => {"a"=>0, "b"=>1, "c"=>2}
#(hash[month2] + 12 - hash[month1]) % 12 should yield the desired result
However, the solution above does not deal with years. If month1 is 'Jan' and month2 is 'Feb', then the result will be 1, regardless of the year of month1 and the year of month2.
I am not fluent in Ruby, so my code might have errors in its syntax.
Use can use DateTime::strptime to get number represented the order of the month in a year. From there it should be easy
require 'date'
def distance(start_month, end_month)
distance = DateTime.strptime(end_month,"%b").month - DateTime.strptime(start_month,"%b").month + 1
distance < 0 ? distance + 12 : distance
end
If you have the month name then you can parse that month and get the sequence number of that month, like this:
require 'date'
month1 = Date.parse("Feb").month
month1 = Date.parse("Apr").month
Or You can use array of 12 month to find their sequence number.
And for counts between months:
result = ((month2 > month1) ? (month2 - month1) : (month1 - (month1 - month2)) + 1)
This will work for the sequence of the months. If the month1 id 'Dec' and month2 is 'Mar', then it will return count 4, not 9.
Related
I have a measure [Total] reported by a custom week-ending date field 'Date'[CustWEndingDate] from Date table, based on Sat - Fri weeks (so each week ends on a Fri), plus an associated 'Date'[WeekNum] and 'Date'[Year] to that. Data looks like this:
[CustWEndingDate], [Year], [WeekNum], [Total]
3/29/2019, 2019, 13, 400
4/5/2019, 2019, 14, 350
4/12/2019, 2019, 15, 420
4/19/2019, 2019, 16, 390
...
3/27/2020, 2020, 13, 315
4/3/2020, 2020, 14, 325
4/10/2020, 2020, 15, 405
4/17/2020, 2020, 16, 375
My question is this: How do I create DAX measure to calculate last 3 weeks this year OVER same last 3 weeks last year? For example, week 14, 15 and 16 this year (325+405+375) vs same week 14, 15 and 16 last year (350+420+390)?
Thank you in advance for any help you can provide!
I would use the following approach:
Calculate scalar with today's date
Calculate scalar with year of today's date
Calculate scalar with WeekNum associated to today's date
Calculate scalar with CY value for last 3 weeks this year
Calculate scalar with PY value for (same) last 3 weeks previous year
Calculate ratio
Here is the technical implementation:
Joel's Measure:=
VAR _TODAY = TODAY()
VAR _YEAR = YEAR(_TODAY)
VAR _WEEKNUM = CALCULATE(MIN('Date'[WeekNum]), 'Date'[Date] = _TODAY)
VAR _CY = CALCULATE([MEASURE], 'Date'[Year] = _YEAR, 'Date'[WeekNum] IN {_WEEKNUM, _WEEKNUM - 1, _WEEKNUM - 2})
VAR _PY = CALCULATE([MEASURE], 'Date'[Year] = _YEAR - 1, 'Date'[WeekNum] IN {_WEEKNUM, _WEEKNUM - 1, _WEEKNUM - 2})
RETURN
DIVIDE(_CY - _PY, ABS(_PY))
This should solve your problem. If not, please share a feedback with the result.
I’m using Rails 4.2.7. I have two pairs of numbers …
month1 # A number between 1 and 12
year1 # a four digit year
month2 # A number between 1 and 12
year2 # A four digit year
How do I write a comparison expression to determine if the “month2-year2” combination is greater than or equal to the “month1-year1” combination? For instance if month2 = 1 and year2 = 2017 and month1 = 12 and year1 = 2016, the month2-year2 combination is greater than the month1-year1 combination.
month1, month2, year1, year2 = 12, 1, 2016, 2017
=> [12, 1, 2016, 2017]
Time.new(year1, month1) >= Time.new(year2, month2)
=> false
Time.new(year2, month2) >= Time.new(year1, month1)
=> true
reference: https://ruby-doc.org/core-2.2.0/Time.html#class-Time-label-Creating+a+new+Time+instance
It's pretty easy and there's no need to create date or time objects.
def first_smaller?(ym1, ym2)
(ym1 <=> ym2) == -1
end
first_smaller? [2016,12], [2017,1]
#=> true
first_smaller? [2017,1], [2016,12]
#=> false
first_smaller? [2017,1], [2017,1]
#=> false
See the third paragraph of the doc for Array#<=> to see how Ruby orders arrays.
If you also wish to know if the two arrays are equal, you could write something like the following:
def ordering(ym1, ym2)
case ym1 <=> ym2
end
which returns -1 if ym1 is smaller, +1 if ym2 is smaller or 0 if the arrays are equal.
I have a Time object T. What's a reasonable way to add N days to T?
The best I've come up with feels somewhat tortured:
require 'date'
def add_days(time, days)
time.to_date.next_day(days).to_time
end
P.S.: If you are in the US, a correct answer must satisfy:
add_days(Time.new(2013, 3, 10, 0), 1) == Time.new(2013, 3, 11, 0)
and if you are in the EU, a correct answer must satisfy:
add_days(Time.new(2013, 3, 31, 0), 1) == Time.new(2013, 4, 1, 0)
P.P.S: This is a Ruby question, not a Rails question.
Time has a + method which accepts seconds.
N = 3
t = Time.now + N * 86400 # 24 * 60 * 60
Or, if you bring ActiveSupport in, it's easier
require 'active_support/core_ext'
t = Time.now + N.days
You can obviously make your own helper
class Fixnum
def days
self * 86400
end
end
t = Time.now # => 2013-01-31 16:06:31 +0700
t + 3.days # => 2013-02-03 16:06:31 +0700
ActiveSupport::TimeWithZone seems to handle this well
> t1 = ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse('2013-03-10')
=> Sun, 10 Mar 2013 00:00:00 EST -05:00
Notice the class type below:
> t1.class
=> ActiveSupport::TimeWithZone
Notice the change from EST above to EDT below:
> t1 + 1.day
=> Mon, 11 Mar 2013 00:00:00 EDT -04:00
As appears to have become my style, I am answering my own question.
Since the transition across DST / ST is rather rare (and in many parts of the world, nonexistent), a more efficient approach is to first add (n_days * 24 * 60 * 60) seconds and then check if the UTC offset has changed. If it has, then create a corrected time object.
Like this:
def add_days(time, n_days)
t2 = time + (n_days * 24 * 60 * 60)
utc_delta = time.utc_offset - t2.utc_offset
(utc_delta == 0) ? t2 : t2 + utc_delta
end
This approach and avoids a lot of extra object creation, and handles transitions across Daylight Savings properly (at least in my current time zone, Pacific Time):
>> t1 = Time.new(2013, 3, 10, 0, 0, 0)
=> 2013-03-10 00:00:00 -0800 # midnight Mar 3, 2013 Pacific Standard Time
>> t2 = add_days(t1, 1)
=> 2013-03-11 00:00:00 -0700 # midnight Mar 4, 2013 Pacific Daylight Time
>> t2 - t1
=> 82800.0 # a shorter than usual day
>> u1 = Time.new(2013, 11, 3, 0, 0, 0)
=> 2013-11-03 00:00:00 -0700 # midnight Nov 3, 2013 Pacific Daylight Time
>> u2 = add_days(u1, 1)
=> 2013-11-04 00:00:00 -0800 # midnight Nov 4, 2013 Pacific Standard Time
>> u2 - u1
=> 90000.0 # a longer than usual day
This is somewhat of a lateral answer but because in your original question you weren't concerned about the HMS section of Time, wouldn't you be better off using Date objects instead?
require 'date'
t=Time.now
d=Date.parse(t.to_s)
puts d+1 # => gives you tomorrow's day (YMD)
Edit: Added require 'date' to improve answer comprehensiveness, as pointed out in the comments section.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have deviced a procedure to find nth working day without using loops.
Please bring around your suggesstions over this -
Algorithm to manipulate working days -
Problem: Find the date of nth working day from any particular day.
Solution:
Normalize to closest Monday -
If today(or the initial day) happens to be something other than monday, bring the day to the closest monday by simple addition or subtraction.
eg: Initial Day - 17, Oct. This happens to be wednesday. So normalize this no monday by going 2 dates down.
Now name this 2 dates, the initial normalization factor.
Add the number of working days + week ends that fall in these weeks.
eg: to add 10 working days, we need to add 12 days. Since 10 days has 1 week that includes only 1 saturday and 1 sunday.
this is because, we are normalizing to nearest monday.
Amortizing back -
Now from the end date add the initial normalization factor (for negative initial normalization) and another constant factor (say, k).
Or add 1 if the initial normalization is obtained from a Friday, which happens to be +3.
If start date falls on Saturday and sunday , treat as monday. so no amortization required at this step.
eg: Say if initial normalization is from wednesday, the intial normalization factor is -2. Hence add 2 to the end date and a constant k.
The constant k is either 2 or 0.
Constant definition -
If initial normalization factor is -3, then add 2 to the resulting date if the day before amortization is (wed,thu,fri)
If initial normalization factor is -2, then add 2 to the resulting date if the day before amortization is (thu,fri)
If initial normalization factor is -1, then add 2 to the resulting date if the day before amortization is (fri)
Example -
Find the 15th working day from Oct,17 (wednesday).
Step 1 -
initial normalization = -2
now start date is Oct,15 (monday).
Step 2 -
add 15 working days -
15 days => 2 weeks
weekends = 2 (2 sat, 2 sun)
so add 15 + 4 = 19 days to Oct, 15 monday.
end_date = 2, nov, Friday
Step 3a -
end_date = end_date + initial normalization = 4, nov sunday
Step 3b -
end_date = end_date + constant_factor = 4, nov, sunday + 2 = 6, nov (Tuesday)
Cross Verfication -
Add 15th working day to Oct, 17 wednesday
Oct,17 + 3 (Oct 17,18,19) + 5 (Oct 22-26) + 5 (Oct 29 - Nov 2) + 2 (Nov 5, Nov 6)
Now the answer is 6, Nov, Tuesday.
I have verified with a few cases. Please share your suggesstions.
Larsen.
To start with, its a nice algorithm, i have doubts about boundary conditions though: for example, what if i need to find the 0th working day from today's date:
Step 1 -
initial normalization = -2 now start date is Oct,15 (monday).
Step 2 -
add 0 working days -
0 days => 0 weeks
weekends = 0
so add 0 + 0 = 0 days to Oct, 15 monday.
end_date = 15, oct, monday
Step 3a -
end_date = end_date + initial normalization = 17, oct wednesday
Step 3b -
end_date = end_date + constant_factor = 17, Oct wednesday or 19,oct friday based on whether constant factor is 0 or 2 as it be only one of these values.
Now lets repeat the steps for finding the 1st working day from today:
Step 1 -
initial normalization = -2 now start date is Oct,15 (monday).
Step 2 -
add 1 working days -
1 days => 0 weeks
weekends = 0
so add 1 + 0 = 1 days to Oct, 15 monday.
end_date = 15, oct, monday
Step 3a -
end_date = end_date + initial normalization = 17, oct wednesday
Step 3b -
end_date = end_date + constant_factor = 17, Oct wednesday or 19,oct friday based on whether constant factor is 0 or 2 as it be only one of these values.
Did you notice, algorithm gives the same end result for 0 and 1. May be thats not an issue if t defined beforehand that 0 working days and 1 working days are considered as same scenario, but ideally they should be giving different results.
I would also suggest you to consider the negative test cases, like what if i need to find -6th working day from today, will your alforithm give me a date in past rightfully?
Lets consider 0th working day from today (17/10, wed).
Step 1 -
start_date = 17/10 wed
normalized date = 15/10 mon
Step 2 -
end_date = normalized date + working days
= 15/10 mon + 0 = 15/10 mon
Step 3 -
amortized_back = end_date_before_amortization + normalization factor
= 15/10 + (+2) = 17/10 wed
since the end_date_before_amortization falls on monday and initial normalization is 2, constant factor = 0.
hence, end_date = 17/10 wed.
now case 2, 1st working day from today.
Step 1 -
start_date = 17/10 wed
normalized date = 15/10 mon
Step 2 -
end_date = normalized date + working days
= 15/10 mon + 1 = 16/10 tue
Step 3 -
amortized_back = end_date_before_amortization + normalization factor
= 16/10 + (+2) = 18/10 thu.
since the end_date_before_amortization falls on tuesday and initial normalization is 2, constant factor = 0.
hence, end_date = 18/10 thu.
Looks to be working for 0th and 1st WD.
c should be inner period of 2 periods. How to get it in most elegant way?
a1=Date.current
a2=Date.current + 2.months
b1=Date.current + 1.month
b2=Date.current + 3.months
c=???
c.should_be [Date.current + 1.month, Date.current + 2.months]
Hurried implementation:
xs = (a1..a2).to_a & (b1..b2).to_a
(xs.first..xs.last)
# => Sun, 24 Jun 2012..Tue, 24 Jul 2012
There is nothing special about a range of dates. So search "range intersection" to do it more efficiently (for example here). Now you can write:
(a1..a2) & (b1..b2)
d= [a1, a2, b1, b2]
[*1..d.length/ 2].map do |dt|
d.shift(2)
end.map do |dx|
Date.current+ (dx[1]- dx[0])
end
[Sun, 24 Jun 2012, Tue, 24 Jul 2012]