How to get middle period of 2 date periods in ruby? - ruby

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]

Related

Building a mask out of a selection of days of week (MTWTFSS) to find whether a specific day matches via basic operators only

Given a selection of days of week, I need to know whether a specific day is matching the selection.
For example, given:
enum DayOfWeek {
MON, TUE, WED, THU, FRI, SAT, SUN
}
And the following selection:
List<DayOfWeek> selection = List.of(MON, WED);
If a given day's day of week is MON or WED, then it should match, otherwise it shouldn't.
A straightforward approach:
boolean match = selection.contains(day.getDayOfWeek());
I'm wondering whether there's a way to build a mask out of the selection, and finding whether a given day matches only with basic operators (+ - * / % AND OR NOT EQ LT LTE GT GTE, no bitwise operators allowed).
For instance, given that same selection:
int mask = 1010000; // just an example of a mask, can be any other
Then:
boolean match = fn(mask, day.getDayOfWeek());
Could such a mask exist so that fn can be written with basic operators only?
Instead of a boolean approach, take some prime numbers
MON, TUE, WED, THU, FRI, SAT, SUN
2, 3, 5, 7, 11, 13, 17
The mask is composed of the product of selected day, e.g (MON, WED) -> 2*5 = 10
The dayOfWeek matches iff it divides the mask (idem mask % dayOfWeek === 0)
e.g
TUE -> 3, mask % 3 != 0, not in selection
MON -> 2, mask % 2 === 0, in selection

Calculating which day of the week a date falls on using Gauss's algorithm, ordinal date and modulo arithmetic

After calculating which day of the week the 1st of January falls on using Gauss's algorithm, as well as calculating the ordinal date for a given calendar date, how can the day of the week of the latter date be calculated?
For example, Gauss's algorithm can tell us that, this year, the 1st of January fell on a Sunday, the 7th day of the week. Today is the 22nd of October, with an ordinal day of 295. How can this information be used to calculate that today is a Sunday?
For common years (= non-leap years), 1st of January and 1st of October are on the same day of the week:
Jan 31
Feb 28
Mar 31
Apr 30
May 31
Jun 30
Jul 31
Aug 30
Sep 31
Sum 273 = 39 x 7
See Wikipedia
22nd October is exactly three weeks later than 1st of October.
An approach I've found, which I haven't tested extensively, but seems to work with the dates I've thrown at it, is...
(ordinal day + day of 1st of January - 1) % 7
Where Mon = 1, Tue = 2,..., Sat = 6, Sun = 0.
In the example mentioned in the question:
(295 + 0 - 1) % 7 = 0 (Sunday)

What is the significance of Go's time.Format(layout string) reference time?

What is the significance of Go's time.Format(layout string) reference time, ie:
Mon Jan 2 15:04:05 -0700 MST 2006
This specific time couldn't have been chosen completely randomly, right?
Source: http://golang.org/pkg/time/#Time.Format
Each part of the date is used as an index:
Jan -> 1 -> Month
2 -> 2 -> Day-of-Month
15 = 3PM -> 15/3 -> hour
04 -> 4 -> minute
05 -> 5 -> second
2006 -> 6 -> year
-0700 -> 7 -> time-zone
So according to the doc:
Since MST is GMT-0700, the reference time can be thought of as
01/02 03:04:05PM '06 -0700
This makes it easy for the time.Format method to parse human-readable date format specifications that are visually identical to the desired result.
Compare this to for example the strftime C function that uses hard-to-remember format strings such as "%a, %d %b %y %T %z" which represents a RFC 822-compliant date format.
The Go equivalent is: "Mon, 02 Jan 06 15:04 MST".
The time.Format will tokenize this string and analyze each word.
Mon is recognized litteraly as monday so this is the week day's name
the comma is left untouched
02 is recognized as the integer value 2, representing a day-of-month in the index
Jan is the known english abbreviation for the january month, so this is used for the month part
06 is 6 so this the year part
15 is equivalent to 3 and represent the hour
the ':' character is left untouched
04 is 4, therefore the minute
MST is interpreted litteraly
See https://github.com/golang/go/blob/go1.15/src/time/format.go#L151 for the exact algorithm.
In American date format, it's Mon, 1/2 03:04:05 PM 2006 -0700.
1, 2, 3, 4, 5, 6, 7.

Count number of months using month names in ruby

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.

how do I add N days to time T (accounting for Daylight Savings Time)?

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.

Resources