# frozen_string_literal: truerequire"active_support/core_ext/object/try"require"active_support/core_ext/date_time/conversions"moduleDateAndTimemoduleCalculationsDAYS_INTO_WEEK={sunday: 0,monday: 1,tuesday: 2,wednesday: 3,thursday: 4,friday: 5,saturday: 6}WEEKEND_DAYS=[6,0]# Returns a new date/time representing yesterday.defyesterdayadvance(days: -1)end# Returns a new date/time representing tomorrow.deftomorrowadvance(days: 1)end# Returns true if the date/time is today.deftoday?to_date==::Date.currentend# Returns true if the date/time is tomorrow.deftomorrow?to_date==::Date.current.tomorrowendalias:next_day?:tomorrow?# Returns true if the date/time is yesterday.defyesterday?to_date==::Date.current.yesterdayendalias:prev_day?:yesterday?# Returns true if the date/time is in the past.defpast?self<self.class.currentend# Returns true if the date/time is in the future.deffuture?self>self.class.currentend# Returns true if the date/time falls on a Saturday or Sunday.defon_weekend?WEEKEND_DAYS.include?(wday)end# Returns true if the date/time does not fall on a Saturday or Sunday.defon_weekday?!WEEKEND_DAYS.include?(wday)end# Returns true if the date/time falls before <tt>date_or_time</tt>.defbefore?(date_or_time)self<date_or_timeend# Returns true if the date/time falls after <tt>date_or_time</tt>.defafter?(date_or_time)self>date_or_timeend# Returns a new date/time the specified number of days ago.defdays_ago(days)advance(days: -days)end# Returns a new date/time the specified number of days in the future.defdays_since(days)advance(days: days)end# Returns a new date/time the specified number of weeks ago.defweeks_ago(weeks)advance(weeks: -weeks)end# Returns a new date/time the specified number of weeks in the future.defweeks_since(weeks)advance(weeks: weeks)end# Returns a new date/time the specified number of months ago.defmonths_ago(months)advance(months: -months)end# Returns a new date/time the specified number of months in the future.defmonths_since(months)advance(months: months)end# Returns a new date/time the specified number of years ago.defyears_ago(years)advance(years: -years)end# Returns a new date/time the specified number of years in the future.defyears_since(years)advance(years: years)end# Returns a new date/time at the start of the month.## today = Date.today # => Thu, 18 Jun 2015# today.beginning_of_month # => Mon, 01 Jun 2015## +DateTime+ objects will have a time set to 0:00.## now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000# now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000defbeginning_of_monthfirst_hour(change(day: 1))endalias:at_beginning_of_month:beginning_of_month# Returns a new date/time at the start of the quarter.## today = Date.today # => Fri, 10 Jul 2015# today.beginning_of_quarter # => Wed, 01 Jul 2015## +DateTime+ objects will have a time set to 0:00.## now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000# now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000defbeginning_of_quarterfirst_quarter_month=month-(2+month)%3beginning_of_month.change(month: first_quarter_month)endalias:at_beginning_of_quarter:beginning_of_quarter# Returns a new date/time at the end of the quarter.## today = Date.today # => Fri, 10 Jul 2015# today.end_of_quarter # => Wed, 30 Sep 2015## +DateTime+ objects will have a time set to 23:59:59.## now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000# now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000defend_of_quarterlast_quarter_month=month+(12-month)%3beginning_of_month.change(month: last_quarter_month).end_of_monthendalias:at_end_of_quarter:end_of_quarter# Returns a new date/time at the beginning of the year.## today = Date.today # => Fri, 10 Jul 2015# today.beginning_of_year # => Thu, 01 Jan 2015## +DateTime+ objects will have a time set to 0:00.## now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000# now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000defbeginning_of_yearchange(month: 1).beginning_of_monthendalias:at_beginning_of_year:beginning_of_year# Returns a new date/time representing the given day in the next week.## today = Date.today # => Thu, 07 May 2015# today.next_week # => Mon, 11 May 2015## The +given_day_in_next_week+ defaults to the beginning of the week# which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+# when set.## today = Date.today # => Thu, 07 May 2015# today.next_week(:friday) # => Fri, 15 May 2015## +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.## now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000# now.next_week # => Mon, 11 May 2015 00:00:00 +0000defnext_week(given_day_in_next_week=Date.beginning_of_week,same_time: false)result=first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))same_time?copy_time_to(result):resultend# Returns a new date/time representing the next weekday.defnext_weekdayifnext_day.on_weekend?next_week(:monday,same_time: true)elsenext_dayendend# Short-hand for <tt>months_since(3)</tt>.defnext_quartermonths_since(3)end# Returns a new date/time representing the given day in the previous week.# Week is assumed to start on +start_day+, default is# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.# DateTime objects have their time set to 0:00 unless +same_time+ is true.defprev_week(start_day=Date.beginning_of_week,same_time: false)result=first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))same_time?copy_time_to(result):resultendalias_method:last_week,:prev_week# Returns a new date/time representing the previous weekday.defprev_weekdayifprev_day.on_weekend?copy_time_to(beginning_of_week(:friday))elseprev_dayendendalias_method:last_weekday,:prev_weekday# Short-hand for <tt>months_ago(1)</tt>.deflast_monthmonths_ago(1)end# Short-hand for <tt>months_ago(3)</tt>.defprev_quartermonths_ago(3)endalias_method:last_quarter,:prev_quarter# Short-hand for <tt>years_ago(1)</tt>.deflast_yearyears_ago(1)end# Returns the number of days to the start of the week on the given day.# Week is assumed to start on +start_day+, default is# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.defdays_to_week_start(start_day=Date.beginning_of_week)start_day_number=DAYS_INTO_WEEK.fetch(start_day)(wday-start_day_number)%7end# Returns a new date/time representing the start of this week on the given day.# Week is assumed to start on +start_day+, default is# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.# +DateTime+ objects have their time set to 0:00.defbeginning_of_week(start_day=Date.beginning_of_week)result=days_ago(days_to_week_start(start_day))acts_like?(:time)?result.midnight:resultendalias:at_beginning_of_week:beginning_of_week# Returns Monday of this week assuming that week starts on Monday.# +DateTime+ objects have their time set to 0:00.defmondaybeginning_of_week(:monday)end# Returns a new date/time representing the end of this week on the given day.# Week is assumed to start on +start_day+, default is# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.# DateTime objects have their time set to 23:59:59.defend_of_week(start_day=Date.beginning_of_week)last_hour(days_since(6-days_to_week_start(start_day)))endalias:at_end_of_week:end_of_week# Returns Sunday of this week assuming that week starts on Monday.# +DateTime+ objects have their time set to 23:59:59.defsundayend_of_week(:monday)end# Returns a new date/time representing the end of the month.# DateTime objects will have a time set to 23:59:59.defend_of_monthlast_day=::Time.days_in_month(month,year)last_hour(days_since(last_day-day))endalias:at_end_of_month:end_of_month# Returns a new date/time representing the end of the year.# DateTime objects will have a time set to 23:59:59.defend_of_yearchange(month: 12).end_of_monthendalias:at_end_of_year:end_of_year# Returns a Range representing the whole day of the current date/time.defall_daybeginning_of_day..end_of_dayend# Returns a Range representing the whole week of the current date/time.# Week starts on start_day, default is <tt>Date.beginning_of_week</tt> or <tt>config.beginning_of_week</tt> when set.defall_week(start_day=Date.beginning_of_week)beginning_of_week(start_day)..end_of_week(start_day)end# Returns a Range representing the whole month of the current date/time.defall_monthbeginning_of_month..end_of_monthend# Returns a Range representing the whole quarter of the current date/time.defall_quarterbeginning_of_quarter..end_of_quarterend# Returns a Range representing the whole year of the current date/time.defall_yearbeginning_of_year..end_of_yearend# Returns a new date/time representing the next occurrence of the specified day of week.## today = Date.today # => Thu, 14 Dec 2017# today.next_occurring(:monday) # => Mon, 18 Dec 2017# today.next_occurring(:thursday) # => Thu, 21 Dec 2017defnext_occurring(day_of_week)from_now=DAYS_INTO_WEEK.fetch(day_of_week)-wdayfrom_now+=7unlessfrom_now>0advance(days: from_now)end# Returns a new date/time representing the previous occurrence of the specified day of week.## today = Date.today # => Thu, 14 Dec 2017# today.prev_occurring(:monday) # => Mon, 11 Dec 2017# today.prev_occurring(:thursday) # => Thu, 07 Dec 2017defprev_occurring(day_of_week)ago=wday-DAYS_INTO_WEEK.fetch(day_of_week)ago+=7unlessago>0advance(days: -ago)endprivatedeffirst_hour(date_or_time)date_or_time.acts_like?(:time)?date_or_time.beginning_of_day:date_or_timeenddeflast_hour(date_or_time)date_or_time.acts_like?(:time)?date_or_time.end_of_day:date_or_timeenddefdays_span(day)(DAYS_INTO_WEEK.fetch(day)-DAYS_INTO_WEEK.fetch(Date.beginning_of_week))%7enddefcopy_time_to(other)other.change(hour: hour,min: min,sec: sec,nsec: try(:nsec))endendend