Ruby on Rails - Get date - given is year, month, weeknumber and day - ruby-on-rails

I have some troubles with the following task:
Given are the following parameters:
Year: 2016
Month: 2
Week of Month: 2
Day of week: Monday
The result should be 2016-02-08
Is there a method to get this?
Thanks in advance

A way to do this would be
weekday_index = {monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 7}
day_in_a_week = 7
year = year_param # 2016
month = month_param # 8
week_number = 2
day_of_month = (week_number * days_in_a_week) - (days_in_a_week - weekday_index[:monday])
Date.new(year, month, day_of_month).to_s

You can use this code. Values used were supplied in the question month = 2, week_number = 2 and year = 2016
month = 2
year = 2016
week_number = 2
FIRST_DAY = 1 #first day of every month
day = { monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 7}
first_wk_of_month = Date.new(year, month, FIRST_DAY).cweek #=> 05
date_week_num = first_wk_of_month + (week_number - 1) #=> 06
date = Date.commercial(year, date_week_num, day[:monday]).to_s # 2016-02-08
There are 52 weeks in a year and a month can have as low as 1 day in a week if it starts on Sunday. Taking that into consideration,
first_wk_of_month finds the week number in the year (between 1 and 52) on which the month in question starts.
date_week_num finds the actual week number in the year the date falls on. In this scenario which is 2nd week of February it returns 6 representing the 6th week of the year
Passing the date_week_num and the day to the Date.commercial allows us to find the date
Tested it with a couple of days and works fine. You could probably change the variable names. Hope this solves your challenge

Related

How to sort a table in Lua based on current date

I need an array which will start with today's date as the first element and sort all of the others in that order.
self.dayw=tonumber(os.date("%w")) --today's date
this is the array I have already implemented
self.dayArray[1]=response["monday"]
self.dayArray[2]=response["tuesday"]
self.dayArray[3]=response["wednesday"]
self.dayArray[4]=response["thursday"]
self.dayArray[5]=response["friday"]
self.dayArray[6]=response["saturday"]
self.dayArray[7]=response["sunday"]
So if today is Friday, I need that array to start from Friday as the first element.
I have created sortArray={} and tried to fill it with elements depending on the day, but the code is too "hectic" and there is probably a smarter solution to this. If you can please help.
Each day of the week is assigned a number, starting from Sunday at 1 and ending with Saturday at 7. In order to find this numerical representation of the current day, call os.date("*t"), which returns a table filled with information about the current day, month, year, etc. The field within this table that corresponds to the current day's number is called wday.
for k, v in pairs(os.date("*t")) do
print(k, v)
end
Output:
year 2020
wday 6
month 8
isdst true
hour 21
day 28
sec 13
yday 241
min 48
Here, the value associated with key wday is 6, which corresponds to Friday.
With this number, you can reorder your table of days by popping elements before the current day and then reinserting them at the end.
-- Local references to table functions.
local tblins = table.insert
local tblrmv = table.remove
local days = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
}
local function reorder(days, wday)
for i = wday - 1, 1, -1 do
-- Pop element days[1] and then append it.
tblins(days, tblrmv(days, 1))
end
return
end
local date_table = os.date("*t")
reorder(days, date_table.wday)
for i, day in ipairs(days) do
print(i, day)
end
Output:
1 Friday
2 Saturday
3 Sunday
4 Monday
5 Tuesday
6 Wednesday
7 Thursday
If you wanted an easily restartable version, here is an expansion of my existing solution:
-- Local references to table functions.
local tblins = table.insert
local tblrmv = table.remove
local tblsrt = table.sort
-- The field `day' refers to the string representation; `num' refers to the
-- given day's original position in the table, which is used to restore the
-- table to its starting order.
local days = {
{day = "Sunday", num = 1},
{day = "Monday", num = 2},
{day = "Tuesday", num = 3},
{day = "Wednesday", num = 4},
{day = "Thursday", num = 5},
{day = "Friday", num = 6},
{day = "Sunday", num = 7}
}
local function sort_days(left_day, right_day)
return left_day.num < right_day.num
end
-- The reorder function remains the same
-- To put the days table back in its original order, call the following:
tblsrt(days, sort_days)
for i, day in ipairs(days) do
print(i, day.day, day.num)
end
Output:
1 Sunday 1
2 Monday 2
3 Tuesday 3
4 Wednesday 4
5 Thursday 5
6 Friday 6
7 Saturday 7

Find days of week based on weekdays mask value

I have a weekdays mask number which represents the sum of some days of the week:
SUNDAY = ((2**0))
MONDAY = ((2**1))
TUESDAY = ((2**2))
weekdays_mask = SUNDAY + MONDAY + TUESDAY
So if I have the sum of these days stored in DB how can I do the reverse process and find what days of week I will have to display based on the weekdays_mask value. Does anyone know a method for doing this? Thank you!
+ is a bit uncommon. You'd usually use bitwise OR to combine the values:
SUNDAY | MONDAY | TUESDAY #=> 7
SUNDAY | MONDAY #=> 3
and bitwise AND to check for a specific value:
days = SUNDAY | MONDAY
days & SUNDAY #=> 1
days & MONDAY #=> 2
days & TUESDAY #=> 0 <- zero indicates bit not set
You could initialise them by the 0b0000-like pattern and instead of +/- then use binary arithmetics instead;
SUNDAY = 0b000000
MONDAY = 0b000001
TUESDAY = 0b000010
WEDNESDAY = 0b000100
THURSDAY = 0b001000
FRIDAY = 0b010000
SATURDAY = 0b100000
weekend = SATURDAY | SUNDAY
workday = MONDAY | TUESDAY | WEDNESDAY | THURSDAY | FRIDAY
is_friday_weekend = weekend & FRIDAY
# => 0
is_friday_workday = workday & FRIDAY
# => 16 (Which is not 0; so yes)
I'm sure there is a cleaner way, bit here my 2 cents, based on three days, but works also adding more days:
SUNDAY = ((2**0))
MONDAY = ((2**1))
TUESDAY = ((2**2))
weekdays_mask_7 = SUNDAY | MONDAY | TUESDAY
weekdays_mask_6 = MONDAY | TUESDAY
weekdays_mask_5 = SUNDAY | TUESDAY
weekdays_mask_4 = TUESDAY
weekdays_mask_3 = SUNDAY | MONDAY
weekdays_mask_2 = MONDAY
weekdays_mask_1 = SUNDAY
days = %w[SUNDAY MONDAY TUESDAY]
weekdays_mask = weekdays_mask_7 # just to play
day_array = (weekdays_mask.to_s(2).split("").map(&:to_i).reverse).zip(days)
day_array.select{|e| p e[1] if e[0] == 1}

Ruby on rails get hours, minutes, seconds from two dates and times

I need the number of hours, minutes, seconds between two dates and times.I'm able to get the number of days, hours, minutes, seconds but I don't want no.of days instead of it, I need hours, minutes, seconds only enough.
Here my code,
start_time is Wed, 13 Dec 2017 20:35:19 -0800 and end_time is today datetime
def time_diff(end_time, start_time)
diff = end_time - start_time
mm, ss = diff.divmod(60)
hh, mm = mm.divmod(60)
dd, hh = hh.divmod(24)
time = "%d h, %d m, %d s" % [hh, mm, ss]
return time
end
I need output like this "35 h, 29 m, 12 s"
Thanks for your help.
Just out of curiosity, a pure [almost] functional solution, without intermediate local variables:
start_time = DateTime.parse 'Wed, 13 Dec 2017 23:00:00 UTC'
end_time = DateTime.parse 'Wed, 15 Dec 2017 23:30:20 UTC'
sec, min, hrs = [60, 60, 1].
map.
with_object([[[end_time, start_time].
map(&:to_time).
map(&:to_i).
reduce(:-), nil]]) do |div, obj|
obj << obj.last.first.divmod(div)
obj[-2].rotate!
end.
map(&:first).
compact
#⇒ [20, 30, 48]
You've already got the answer - just don't divide by 24!
If the start_time and end_time are DateTime value you can use the following
difference = end_time - start_time
hours = (difference * 24).to_i
minutes = (difference * 24 * 60).to_i
seconds = (difference * 24 * 60 * 60).to_i

Ruby: Calculate time difference between 2 times

I want to calculate the difference between 2 times.
start_time: 22:00 (Rails interprets this as 2015-12-31 22:00:00 +0100)
second_time: 02:00 (Rails interprets this as 2015-12-31 02:00:00 +0100). The second time is 4 hours later, so in the next day. Is there a way to calculate this difference?
I can not simply do this: second_time - first_time, because this gives me a difference of 22 hours instead of 4 hours.
Edit:
Some background information:
A job is starting at 22:00 and ending the next day at 02:00. Because i fill in the form of this job only times, this times for the above 2 values are 2015-12-31 22:00:00 +0100 and 2015-12-31 02:00:00 +0100. I don't want the user to fill in the time including the date. The real difference between the times should be 4 hours.
So what i actually want is calculate the difference between 22:00 and 02:00 (in the next day).
I don't understand why you think it should return 4 hours or why it does return 22 hours. 20 hours would be correct for your example:
require 'time'
a = Time.parse('2015-12-31 22:00:00 +0100')
b = Time.parse('2015-12-31 02:00:00 +0100')
a - b
#=> 72000.0 # difference in seconds
(a - b) / 3600
#=> 20.0 # difference in hours
Update: It seems like you are dealing only with the time portion and not with the actual date. And I assume the maximum difference you will have to deal with is 24 hours:
def time_difference(time_a, time_b)
difference = time_b - time_a
if difference > 0
difference
else
24 * 3600 + difference
end
end
a = Time.parse('2015-12-31 22:00:00 +0100')
b = Time.parse('2015-12-31 02:00:00 +0100')
time_difference(a, b) / 3600
# => 4 # hours
a = Time.parse('2015-12-31 02:00:00 +0100')
b = Time.parse('2015-12-31 22:00:00 +0100')
time_difference(a, b) / 3600
# => 20 # hours
Old question but I did a nice method to deal with it:
def time(start,ending)
if start != ending
medidas=["year","month","day","hour","minute","second"]
array=[1970,1,1,0,0,0]
text = ""
Time.at(ending-start).utc.to_a.take(6).reverse.each_with_index do |k,i|
text = "#{text} #{I18n.translate medidas[i].to_sym, count: k-array[i]}"
end
text = text.strip.squish
pos = text.rindex(" ",(text.rindex(" ")-1))
unless pos.nil?
text = text.insert(pos," and")
end
text = text.strip.squish #This shouldn't be needed but just in case
else
"0 seconds"
end
end
Then in config/locales/en.yml I added:
en:
año:
zero: ''
one: '1 year'
other: '%{count} years'
mes:
zero: ''
one: '1 month'
other: '%{count} months'
dia:
zero: ''
one: '1 day'
other: '%{count} days'
hora:
zero: ''
one: '1 hour'
other: '%{count} hours'
minuto:
zero: ''
one: '1 minute'
other: '%{count} minutes'
segundo:
zero: ''
one: '1 second'
other: '%{count} seconds'
So for example when you call:
start = Time.now
ending = start + (60*60)
time(start,ending)
=> "1 hour"
ending = start + (60*60*28)
time(start,ending)
=> "1 day and 4 hours"
ending = start + (53*60*5874)
time(start,ending)
=> "7 months 4 days 4 hours and 42 minutes"
Hope it's useful
I'd write it thusly (before adding data checks), in an attempt to make it self-documenting:
require 'time'
DT_FMT = '%Y-%m-%d %H:%M:%S %z'
SECONDS_PER_DAY = 24*60*60
def hours_elapsed(start_str, finish_str)
start = DateTime.strptime(start_str, DT_FMT).to_time
finish = DateTime.strptime(finish_str, DT_FMT).to_time
finish = same_time_tomorrow(finish) if finish < start
(finish-start)/3600
end
def same_time_tomorrow(time)
time + SECONDS_PER_DAY
end
hours_elapsed '2015-12-31 22:00:00 +0100',
'2015-12-31 02:00:00 +0100'
#=> 4.0
hours_elapsed '2015-12-31 02:00:00 +0100',
'2015-12-31 22:00:00 +0100'
#=> 20.0
It may be better for the arguments of hours_elapsed to be strings containing hours and minutes only, in which case we might rename the method as well. time_elapsed("18:00", "2:30") is an example of how this method might be invoked.
MINUTES_PER_DAY = 24*60
def time_elapsed(start_str, finish_str)
start_mins = time_str_to_minutes(start_str)
finish_mins = time_str_to_minutes(finish_str)
finish_mins += MINUTES_PER_DAY if
finish_mins < start_mins
(finish_mins-start_mins).divmod(60)
end
def time_str_to_minutes(str)
hrs, mins = str.split(':').map(&:to_i)
60 * hrs + mins
end
time_elapsed("8:00", "17:30")
#=> [9, 30]
time_elapsed("18:00", "2:30")
#=> [8, 30]

NSDateComponents returns wrong weekday

If you'll run this code:
// January 16th 2015 10:20 AM in Amsterdam
var date = NSDate(timeIntervalSince1970: 1421400000)
var formatter = NSDateFormatter()
formatter.dateFormat = "dd-MMM"
let calendar = NSCalendar.currentCalendar()
calendar.firstWeekday = 2 // default when region == Netherlands
let units = NSCalendarUnit.YearCalendarUnit | NSCalendarUnit.MonthCalendarUnit
| NSCalendarUnit.WeekOfMonthCalendarUnit | NSCalendarUnit.DayCalendarUnit
| NSCalendarUnit.WeekdayCalendarUnit
// Loop days in January
for day in 1...14 {
// select day in month
var components = calendar.components(units, fromDate: date)
components.day = day
// get day and components
let _date = calendar.dateFromComponents(components)!
var _components = calendar.components(units, fromDate: _date)
// retrieve characteristics
let weekOfMonth = _components.weekOfMonth
let dayOfWeek = _components.weekday
let month = _components.month
println("\(formatter.stringFromDate(_date)) is day \(dayOfWeek) of week \(weekOfMonth) of month \(month) \n")
}
You'll probably get back:
01-Jan is day 5 of week 1 of month 1
02-Jan is day 6 of week 1 of month 1
03-Jan is day 7 of week 1 of month 1
04-Jan is day 1 of week 1 of month 1
05-Jan is day 2 of week 2 of month 1
06-Jan is day 3 of week 2 of month 1
07-Jan is day 4 of week 2 of month 1
....
Those weekdays are wrong.
Not only should the the 1st of january be the 4th day (a thursday), it's also strange that the 3rd of january seems to be on day 7 of week 1 and the 4th of january seems to be day 1 of that same week.
Obviously I'm doing something wrong here, who could help me out?
When you remove the calendar.firstWeekday = 2 line you'll get:
01-Jan is day 5 of week 1 of month 1
02-Jan is day 6 of week 1 of month 1
03-Jan is day 7 of week 1 of month 1
04-Jan is day 1 of week 2 of month 1
05-Jan is day 2 of week 2 of month 1
06-Jan is day 3 of week 2 of month 1
07-Jan is day 4 of week 2 of month 1
....
That makes more sense, but I really need the first day of the week to be a monday here..
I've set up a demo project for you to test this behaviour yourself. https://github.com/tiemevanveen/NSDateComponentsTest/tree/master
As sha points out, components.weekday does not change if your week does not start on a sSnday. Strange that components.weekOfMonth does change when your week starts on Monday..
Solution to my problem
An answer to another question let me to a way to find the desired weekday if the calendar's week does not start with a Sunday.
dayOfWeek = calendar.ordinalityOfUnit(.WeekdayCalendarUnit, inUnit: .WeekCalendarUnit, forDate: _date)
That code could also be used to find the week of the month or the month itself:
dayOfWeek = calendar.ordinalityOfUnit(.WeekdayCalendarUnit, inUnit: .WeekCalendarUnit, forDate: _date)
month = calendar.ordinalityOfUnit(.MonthCalendarUnit, inUnit: .YearCalendarUnit, forDate: _date)
It's all correct. If you look at Apple Documentation.
you can see that 1 is Sunday, 2 - is Monday and so forth. So 5 is Thursday as expected.

Resources