Formatting Time - grails

This may seem like an extremely basic question but it has been eluding me for quite some time. I am trying to setup, in my controller, a way to display the time for a specific day based on that time. The time value for example I would want to display would be 10:00 AM from the value 10:00:00. I cannot seem to format the time correctly so that it can display it in that form. Here is my current code that the value will pass through:
def dayMap = new JSONArray()
daysofWeek.each{last ->
def testjsonObject = new JSONObject()
c.setTime(last.date)
int test = c.get(Calendar.DAY_OF_WEEK)
testjsonObject.put('dayofweekNumber', test)
testjsonObject.put('start_time', last.start_time)
testjsonObject.put('end_time', last.end_time)
dayMap.add(testjsonObject)
}
def weekStartTimeString = ""
def weekEndTimeString = ""
List finalList = []
dayMap.each{numberDay ->
if(numberDay.dayofweekNumber == Calendar.MONDAY){
if(numberDay.start_time.equals("09:00:00")){
weekStartTimeString += "<option value='09:00:00' selected>9:00 AM</option>"
println(numberDay)
}
else{
weekStartTimeString += "<option value='numberDay.start_time' selected>"
}
}
}
I would like to take the value numberDay.start_time and display it in that format. 10:00:00 to 10:00 AM. What is the best way of doing this as I am currently out of ideas.

Parse the time into a date object, then convert it back to a string in the format you want. For example:
def start_date = '10:00:00'
assert Date.parse('HH:mm:ss', start_date).format('hh:mm aa') == '10:00 AM'
start_date = '23:59:59'
assert Date.parse('HH:mm:ss', start_date).format('hh:mm aa') == '11:59 PM'
The date format strings are the same ones used by the Java SimpleDateFormat class.

Related

Finding the months between two dates in rails

I can currently set a time range like so:
start_date: "2018-09-11"
end_date: "2018-11-19"
How can I do this for start to end of months? Examples:
time_range = ["2018-09-11".."2018-09-30"]
time_range = ["2018-10-01".."2018-10-31"]
time_range = ["2018-11-01".."2018-11-19"]
I'm not sure what's exactly your desired outcome but, given start date and end date as Date objects, you can perform
(start_date..end_date).to_a.group_by(&:month).values
and at the end what you get is a three element array, and each element contains an array with all the dates in that range for a month
I do not know if I understand very well what you asked, but I'll try to help you.
The Date class has several methods that will help you to work with dates.
Date < Object
Examples
my_date_range_array = [Date.today.beginning_of_year..Date.today.end_of_year]
my_date_time_range_array = [Time.now.beginning_of_year..Time.now.end_of_year]
my_date_range_array = [6.months.ago..Date.today]
YourModel.where date: Date.today.beginning_of_month..Date.today
YourModel.where date: 6.months.ago..Date.today
If you need every single date in the range, you can use something like this:
(Date.today.beginning_of_year..Date.today.end_of_year).map{ |date| date }
I hope that my answer helps you
This is a pure Ruby solution, but I believe (though I don't know Rails) it can be simplified slightly by replacing my methods first_day_of_month and first_day_of_month with Rails methods beginning_of_month and end_of_month, respectively. I designed the method for efficiency over simplicity.
require 'date'
DATE_FMT = "%Y-%m-%d"
def date_ranges(start_date_str, end_date_str)
start_date = Date.strptime(start_date_str, DATE_FMT)
end_date = Date.strptime(end_date_str, DATE_FMT)
return [start_date_str..end_date_str] if
[start_date.year, start_date.month] == [end_date.year, end_date.month]
d = start_date
ranges = [start_date_str..last_day_of_month(d)]
loop do
d = d >> 1
break if [d.year, d.month] == [end_date.year, end_date.month]
ranges << (first_day_of_month(d)..last_day_of_month(d))
end
ranges << (first_day_of_month(d)..end_date_str)
end
def first_day_of_month(d)
(d - d.day + 1).strftime(DATE_FMT)
end
def last_day_of_month(d)
((d >> 1)-d.day).strftime(DATE_FMT)
end
date_ranges("2018-09-11", "2019-02-11")
#=> ["2018-09-11".."2018-09-30", "2018-10-01".."2018-10-31",
# "2018-11-01".."2018-11-30", "2018-12-01".."2018-12-31",
# "2019-01-01".."2019-01-31", "2019-02-01".."2019-02-11"]
date_ranges("2018-09-08", "2018-09-23")
#=> ["2018-09-08".."2018-09-23"]
With the information provided by the OP, this is what I understand he is looking for.
Given a set range for example:
time_range = "2018-09-11".."2018-09-19"
new_range_min = time_range.min.to_date.beginning_of_month
new_range_max = time_range.max.to_date.end_of_month
new_range = new_range_min..new_range_max

How do I convert a date into a time when parsing an .xls doc using Rails?

I'm using Rails 5. I want to parse an .xls (not to be confused with .xlsx doc) using the code below
book = Roo::Spreadsheet.open(file_location)
sheet = book.sheet(0)
text = sheet.to_csv
csv = CSV.parse(text)
arr_of_arrs = csv
text_content = ""
arr_of_arrs.each do |arr|
arr.map!{|v| v && v.to_f < 1 && v.to_f > 0 ? TimeFormattingHelper.time_as_str(v.to_f * 24 * 3600 * 1000) : v}
text_content = "#{text_content}\n#{arr.join("\t")}"
end
Here is the method I reference above
def time_as_str(time_in_ms)
regex = /^(0*:?)*0*/
Time.at(time_in_ms.to_f/1000).utc.strftime("%H:%M:%S.%1N").sub!(regex, '')
end
One area I'm having trouble is that a cell that appears in my .xls doc as
24:08:00
is processed as
1904-01-02T00:08:00+00:00
with the code above. How do I parse the value I see on the screen? That is, how do I convert the date value into a time value?
As an example from another Excel doc, the cell that appears as
24:02:00
is getting parsed by my code above as
1899-12-31T00:02:00+00:00
It seems your .xls is in the 1904 date system, and Roo is not able to distinguish between what is a Duration and what is a DateTime, so you'll need to subtract the base date 1904-01-01 to the cell value. Weirdly enough, in case of the 1900 date system, you need to subtract the base date 1899-12-30, due to a bug in Lotus 1-2-3 that Microsoft replicated in Excel for compatibility.
Here is a method that converts the DateTime read from the spreadsheet into the duration according to the base date:
def duration_as_str(datetime, base_date)
total_seconds = DateTime.parse(datetime).to_i - base_date.to_i
hours = total_seconds / (60 * 60)
minutes = (total_seconds / 60) % 60
seconds = total_seconds % 60
"%d:%02d:%02d" % [hours, minutes, seconds]
end
Let's test it:
irb(main):019:0> duration_as_str("1904-01-02T00:08:00+00:00", DateTime.new(1904, 1, 1))
=> "24:08:00"
irb(main):020:0> duration_as_str("1899-12-31T00:02:00+00:00", DateTime.new(1899, 12, 30))
=> "24:02:00"
You can use book.workbook.date_base.year to determine the spreadsheet's date system, and then just add another map inside your each loop:
book = Roo::Spreadsheet.open(file_location)
sheet = book.sheet(0)
text = sheet.to_csv
csv = CSV.parse(text)
base_date = book.workbook.date_base.year == 1904 ? DateTime.new(1904, 1, 1) : DateTime.new(1899, 12, 30)
arr_of_arrs = csv
text_content = ""
arr_of_arrs.each do |arr|
arr.map!{|v| v && v.to_f < 1 && v.to_f > 0 ? TimeFormattingHelper.time_as_str(v.to_f * 24 * 3600 * 1000) : v}
arr.map!{|v| v =~ /^(1904|1899)-/ ? duration_as_str(v, base_date) : v}
text_content = "#{text_content}\n#{arr.join("\t")}"
end
You could use something like the below and write a custom parser for that string.
duration = 0
"24:08:01".split(":").each_with_index do |value, i|
if i == 0
duration += value.to_i.hours
elsif i == 1
duration += value.to_i.minutes
else
duration += value.to_i.seconds
end
end
duration.value => 86881 (duration in seconds)
This parser will assume a format of hours:minutes:seconds and return an instance of ActiveSupport::Duration. Then, duration.value will give you the number of seconds.
You need to read the internal value of cell instead of formatted value.
Formatted value gets written to csv when you use to_csv
To read internal value, you would have to use either sheet objects excelx_value method or row object's cell_value method.
These methods return value in float (days). Here is an example using cell_value by iterating over rows, assuming no header and first column with value to be converted.
Using Roo 2.7.1 (similar methods exist in older version)
book = Roo::Spreadsheet.open(file_location)
sheet = book.sheet(0)
formatted_times = []
time_column_index = 0
sheet.each_row_streaming do |row|
time_in_days = row[time_column_index].cell_value
formatted_times << time_as_str(time_in_days.to_f * 24 * 3600)
end
def time_as_str(t)
minutes, seconds = t.divmod(60)
hours, minutes = minutes.divmod(60)
"%02d:%02d:%02d" % [hours, minutes, seconds]
end
# eg: time_in_days = 1.0169444444444444
# formatted_time = "24:24:24"
First, I will try rephrasing what you want to accomplish.
You want to “parse the value you see on the screen”, but I am not sure whether that is 24:08:00 or 1904-01-02T00:08:00+00:00. I assume it is the first.
You want to convert the date value into a time value. I am not sure you actually want the output var to be a Time, a Date, a DateTime, or simply a String. I assume it is ok for you to have it simply as a String, but this is a minor issue.
With this, I assume that what you in general see as HH:MM:SS in Excel, you want to get as “HH:MM:SS” in Rails, regardless of HH being > 23. As an example, 24:08:00 in Excel would turn into “24:08:00” in Rails.
The two seemingly discordant cases you report most likely stem from the two .xls files having different date systems.
To get the desired result you have two options:
Use to_csv, whose result is affected by the date system of the Excel file. In this case, you have to subtract the base_date, as done by Helder Pereira.
Directly get the numeric value from Excel, which is not affected by the date system. In this case, code is simpler, since you only need one conversion (function days2str below).
Code is (modulo minor adjustments)
def days2str(days)
days_int = int(days)
hours = ( days - days_int ) * 24
hours_int = int(hours)
seconds = ( hours - hours_int ) * 3600
seconds_int = int(seconds)
hours_int = hours_int + 24 * days_int
format("%d:%02d:%02d", hours_int, minutes_int, seconds_int)
end
def is_date(v)
# Define the checking function
end
require 'spreadsheet'
Spreadsheet.open('MyTestSheet.xls') do |book|
book.worksheet('Sheet1').each do |row|
break if row[0].nil?
puts row.join(',')
row.map!{|v| is_date(v) ? days2str(v) : v }
text_content = "#{text_content}\n#{arr.join("\t")}"
end
end

RoR, Can't iterate from DateTime/TimeWithZone

I have a simple task where I want to take a starting date and an ending date and loop over the days/dates. This code is being used in my db:seed rake task. Currently, my code has gone through the following attempts.
(someModel.start_date.to_datetime..someModel.end_date.to_datetime).each {
|x| puts x
}
......
(someModel.start_date...someModel.end_date).each { |x| puts x }
In each case, I get an error like this.
can't iterate from ActiveSupport::TimeWithZone
or
can't iterate from DateTime
If anyone has any clue on how to iterate over a range of DateTimes I'd be greatly appreciative.
start = someModel.start_date.to_datetime
finish = someModel.end_date.to_datetime
while(start < finish) do
#bunch of awesome stuff
start += 1.day
end
You must make sure that you are dealing with a Date object (by calling to_date), then everything works as expected:
start_date.to_date.upto(end_date.to_date) {|date| puts date }
Or with a range:
(start_date.to_date..end_date.to_date).to_a
You can't iterate from DateTime. But you can iterate when start and end of
interval are instances of Date. Convert them if possible.
And then look at these Date methods:
step()
upto()
downto()
to use instead of each
If you want to iterate on the range you might consider a loop and step each day using something like someModel.start_date + 1.day.to_i
Here's what we're doing to iterate by minutes over two TimeWithZones:
# allow iteration over a set of TimeWithZones
def self.time_map(range, step: 30.minutes, &block)
result = []
time = range.first
max = range.last
test = range.exclude_end? ? ->(t) { t < max } : ->(t) { t <= max }
value = block_given? ? ->(t) { yield(t) } : ->(t) { t }
while test.call(time) do
result << value.call(time)
time += step
end
result
end

Timed script/function in Lua

good morning friends... I am working on with Lua scripting language for a mobile app and have a requirement as follows -
The application's main aim is to schedule appointments for an individual with a Doctor.
So once a user's appointment is scheduled, for e.g. 8th May # 4:30 PM, the user should receive a "reminder alert" before an hour i.e. # 3:30 PM.
am absolutely having a blank mind on how to get this done.
I can get the user's date-time value and use the logic that a function should invoke just before 60 mins of that date-time. And that function contains my "Alert message".
But how to do this?
Can anyone guide me with a clue?
Please let me know if any other inputs are required...
Thanks in advance.
I would take an approach like this:
1.
Store each appointment's details as a .txt file containing JSON or Lua tabular data something like this:
{
date = "14:30 01/07/2013";
dateBooked = "09:30 23/06/2013";
venue = "31 Dentist Street";
appointmentType = "Routine Teeth Cleaning";
}
2.
You can have a timer class like so
Timer = {}
Timer_mt = { __index = Timer; __add = function(a,b) a:tickBy(b) end ; }
function Timer:new(delayTime,callBack)
local timer = {callBack=callBack}
timer.initTime = os.date() --MM/DD/YY HH:MM:SS
--delayTime = HH:MM:SS
_,_,hour,minute,second = string.find(delayTime,"(%d%d):(%d%d):(%d%d)")
timer.delay = {hour=hour,minute=minute,second=second}
--time the timer started
_,_,hour,minute,second = string.find(timer.initTime,"(%d%d):(%d%d):(%d%d)")
timer.startTime = {hour=hour,minute=minute,second=second}
--time the timer started
timer.initTime = os.date() --MM/DD/YY HH:MM:SS
print(timer.initTime)
_,_,hour,minute,second = string.find(timer.initTime,"(%d%d):(%d%d):(%d%d)")
timer.currentTime = {hour=hour,minute=minute,second=second}
return setmetatable(timer,Timer_mt)
end
function Timer:tick() --returns true if time expired
currTime = os.date() --MM/DD/YY HH:MM:SS
_,_,chour,cminute,csecond = string.find(currTime,"(%d%d):(%d%d):(%d%d)")
if chour - self.startTime.hour >= tonumber(self.delay.hour) and cminute - self.startTime.minute >= tonumber(self.delay.minute) and csecond - self.startTime.second > tonumber(self.delay.second) then
self:callBack()
self.startTime.hour,self.startTime.minute, self.startTime.second = chour,cminute,csecond
--return true
end
--return false
end
t = Timer:new("00:00:02",function () print("DONE") end)
print(t.currentTime.hour,t.currentTime.minute,t.currentTime.second)
while t:tick() or true do
io.read()
end
(I just made this up so I advice you test it but it seems to work for me).
3.
On start-up, or when a new appointment is added create a new timer, and then tick() each one at some point during the main execution, you could even have a timer which is the only one you tick() and it's callback ticks() the others... Anyway set the callback for each timer to display an alarm

Can using Chronic impair your sense of time?

Haha..
I'm using Chronic to parse the time users add in the Calendar. Where the code works and implements the right time, the end result is that, IF a user adds a time, then it has no date, and because it has no date, it will not show in results. Any ideas?
def set_dates
unless self.natural_date.blank? || Chronic.parse(self.natural_date).blank?
# check if we are dealing with a date or a date + time
if time_provided?(self.natural_date)
self.date = nil
self.time = Chronic.parse(self.natural_date)
else
self.date = Chronic.parse(self.natural_date).to_date
self.time = nil
end
end
unless self.natural_end_date.blank? || Chronic.parse(self.natural_end_date).blank?
# check if we are dealing with a date or a date + time
if time_provided?(self.natural_end_date)
self.end_date = nil
self.end_time = Chronic.parse(self.natural_end_date)
else
self.end_date = Chronic.parse(self.natural_end_date).to_date
self.end_time = nil
end
end
end
Edit:
Here is the time_provided? method:
def time_provided?(natural_date_string)
date_span = Chronic.parse(natural_date_string, :guess => false)
(date_span.last - date_span.first).to_i == 1
end
First, I'm not really sure what are you asking about, because it looks like the code intentionally does what you describe... When there's time provided, the date fields are assigned nil. And I don't think that is Chronic is to blame because that's how your code works.
Not knowing your design (why there are separate date & time fields), the types of fields etc., I would suggest starting with a little kludge like this:
if time_provided?(self.natural_date)
self.time = Chronic.parse(self.natural_date)
self.date = self.time.to_date
or:
self.end_date = Chronic.parse(self.natural_date).to_date
if time_provided?(self.natural_date)
self.time = Chronic.parse(self.natural_date)
end
Or maybe the problem is outside the code you provided: in the part that is responsible for the "because it has no date, it will not show in results" behavior? Maybe you should make the conditions more flexible?

Resources