Convert 12-hour time to 24-hour time with a regex - ruby

I have a bunch of strings with opening hours in this format:
Mon-Fri: AM7:00-PM8:00\nSat-Sun: AM8:00-PM6:00
I can deal with the "AM" part by just removing it, but I'd like to convert the PM by
Removing "PM"
Adding 12 to the number before the ":"
Taking care of the fact that PM is sometimes double-digits (e.g. PM11:00)
There can be zero or more PM times in the string.
I'm not sure how to manipulate the time as a number. I've gotten this far:
opening_hours.sub! /PM([\d]?[\d]):/, "***\1***"
Which outputs things like this:
AM7:15-***\u0001***00
The '\u0001` may be due to Japanese characters in the string.

You can take advantage of the fact that String#gsub accepts a block. Something like this will do for you?
s = "Mon-Fri: AM7:00-PM8:00\nSat-Sun: AM8:00-PM11:00"
s2 = s.gsub('AM', '').gsub(/PM(\d+)/) do |match|
(match.gsub('PM', '').to_i + 12).to_s
end
s2 # => "Mon-Fri: 7:00-20:00\nSat-Sun: 8:00-23:00"

Have a look at this question, ruby has a class called datatime.
Convert 12 hr time to 24 hr format in Ruby

Related

String concatenation of interpolated variables

The code below is longer than the recommended 80 characters.
I've tried to break it up and concatenate it with + operator and << operator, but it doesn't seem to work. I've tried the .to_s method and moved the colons into their own string but to no avail.
What would be the correct way to split this up onto 2 or 3 lines without getting the no method "+#":String error?
string = "#{sprintf("%02i", hours)}:#{sprintf("%02i", minutes)}:#{sprintf("%02i", seconds)}"
You can combine the three sprintf into one:
string = sprintf("%02i:%02i:%02i", hours, minutes, seconds)
To split string into multiline in Ruby you use backslash:
string = "#{sprintf("%02i", hours)}: \
#{sprintf("%02i", minutes)}: \
#{sprintf("%02i", seconds)}"
Notice how there is only one opening and one closing ".
You can also consider moving three calls to sprintf method into one.
I'm guessing that your real question is that, given numbers of seconds or a time object, you want to construct a string with the desired format.
If you are given seconds:
def fmt(seconds)
hours, minutes = seconds.divmod(60)
"%02d, %02d, %02d" % [hours, *minutes.divmod(60)]
end
fmt(223)
#=> "03, 00, 43"
If you are given a Time object:
require 'time'
def fmt(time)
time.strftime('%H hours, %M minutes, %S seconds')
end
time = Time.now
#=> 2015-05-16 14:38:02 -0700
fmt(time)
#=> "14 hours, 38 minutes, 02 seconds"
In this second case I added the words "hours", "minutes" and "seconds" just to illustrate how the format string could be modified.
If you want to print this on multiple lines, simply insert newlines (\n) in the appropriate format string.

What kind of date format is this and how do I transform it?

Making a GET request to a private (no public documentation) API returns data in JSON format.
The value for date looks as follows:
AanmeldDatum: "/Date(1262300400000+0100)/"
There's another variable called AangebodenSindsTekst which means OfferedSinceText and it's value is "8 augustus 2014". So the unknown Date format should get parsed into that specific value.
I'm wondering what kind of date format it is and how can I transform this to something like this 2014-08-08 with Ruby?
I've tried this:
require 'time'
t = '1262300400000+0100'
t2 = Time.parse(t)
# => ArgumentError: no time information in "1262300400000+0100"
Ruby's Time class is your friend, especially the strptime method:
require 'time'
foo = Time.strptime('1262300400000+0100', '%N') # => 2014-08-08 16:57:25 -0700
foo = Time.strptime('1262300400000+0100', '%N%z') # => 2014-08-08 08:57:25 -0700
%N tells Ruby to use nanoseconds. It's throwing away the precision after the 9th digit which is OK if you don't need the rest of the value. Nanosecond accuracy is good enough for most of us.
%z tells Ruby to find the timezone offset, which it then applies to the returned value.
While parse can often figure out how to tear apart an incoming string, it's not bullet-proof, nor is it all-knowing. For speed, I'd recommend learning and relying on strptime if your strings are consistent.
As the Tin Man pointed out in this answer, use the following instead:
Time.strptime('1262300400000+0100', '%Q%z')
it could be milliseconds since epoc, take off the last 3 zeros and plug it into a unix time stamp converter, comes out as Dec 31st 2009
TIME STAMP: 1262300400
DATE (M/D/Y # h:m:s): 12 / 31 / 09 # 11:00:00pm UTC

Ruby: How to extract an hour (or day) from a date-time string

I'm pulling date-time strings from a large CSV file, which look like this:
"11/19/2008 21:56"
I'd like to extract the hour only, so I can build a histogram of all the hours to find the most frequent ones. Similarly, I'd like to extract days of the week (names) from the dates and build a histogram of most frequent days.
I'm new to Ruby, looked up the information, for starters tried various forms of the following, but no luck:
require 'date'
puts DateTime.strptime("11/19/2008 21:56", '%I')
Can you please advise a simple (and clear) way to accomplish the above? Also, any suggestions how to represent the results would be great. I'm thinking one hash array for the hours (24 entries) and one for the days (7 entries)? What would be the neatest algorithm to load them up as I iterate through the date-time strings, and then maybe re-sorting them with most frequent on top? Thanks!!
This is the starting point:
dt = "11/19/2008 21:56"
require 'date'
DateTime.strptime(dt, '%m/%d/%Y %H:%M') # => #<DateTime: 2008-11-19T21:56:00+00:00 ((2454790j,78960s,0n),+0s,2299161j)>
Date formats like "11/19/2008" present a problem when parsing because the default is to use this format:
'%d/%m/%Y'
Date blows up when it sees a month value of 19. '%m/%d/%Y' is not as popular around the world as '%d/%m/%Y', which is why Ruby defaults to it.
Once you have the timestamp parsed, you can easily extract parts from it:
datetime = DateTime.strptime(dt, '%m/%d/%Y %H:%M')
datetime.hour # => 21
datetime.wday # => 3
Notice that wday returns values from 0..6, not 1..7, where 0 = Sunday:
%w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday][datetime.wday]
# => "Wednesday"
Rails' ActiveSupport has a lot of useful methods as part of its Date, DateTime and Time support. Using them is easy, and it's easy to cherry-pick which you want if you decide to add them to plain-ol' Ruby code.
"11/19/2008 21:56".split[1]
=> "21:56"
If can be in other formats, but always the only part with a ":" and two digits on each side, you can use
"11/19/2008 21:56"[/\d{2}:\d{2}/]
=> "21:56"
And for day, something similar
"11/19/2008 21:56"[/\d{2}\/\d{2}\/\d{4}/]
=> "11/19/2008"

Slice the last 4 or otherwise handle this input/output pattern, in Ruby?

print('What is the day and hour (ex., Monday 08AM): ')
appoint = gets.slice[0..-4]
puts(appoint)
is returning this error:
/scope.rb:2:in slice': wrong number of arguments (0 for 1..2) (ArgumentError)
from /scope.rb:2:in'
Also tried slice[appoint.length..-4] and some other things.
From reading other questions, I gathered that this was how such a slice was done. I'm not familiar with the regex pattern. I'd actually like to be able tot return the day of the week, as well, which may mean from -5 back to input or everything up until the space with some kind of regex pattern.
Do you want this ?
appoint = gets.slice(-4,4)
For Monday 08AM it returns:
08AM
You can use slice like this: slice(start, length).
In your case start is -4 and length is 4.
EDIT
Or with only brackets:
appoint = gets[-4..-1]
A string is also an array of characters.
Regex exmaple:
s = "Monday 08AM"
day = /[a-zA-Z]+/
s[day]
=> "Monday"
# \d? to also catch 8AM without 0 at the start
hour = /\d?\d[paPA][mM]/
s[hour]
=> "08AM"
Regex tutorial from Ruby 1.9.3 docs

How to find dates in a csv file using a regular expression and store in an array [using Ruby]?

I have data stored in a csv file that looks like this:
Date,BLOCK,,Wood,Miscellaneous,,Totals,MO
Saturday,4055-RU,4055-AR,4091,1139,1158,,100
11/13/15,C Sort,B,C,iGPS,PECO,,
Starting,758,"3,936",840,0,0,"5,534",
Sorted,656,736,540,162,64,"2,158",
Subtotal 1,"1,414","4,672","1,380",162,64,"7,692",
Shipped,0,"1,152",620,162,64,"1,898",
,"1,414","3,520",860,0,0,"5,794",
Physical,"1,414","3,520",860,0,0,"5,794",
Variance,0,0,0,0,0,0,
Date,BLOCK,,Wood,Miscellaneous,,Totals,MO
Saturday,4055-RU,4055-AR,4091,1139,1158,,100
11/14/15,C Sort,B,C,iGPS,PECO,,
Starting,758,"3,936",840,0,0,"5,534",
Sorted,656,736,540,162,64,"2,158",
Subtotal 1,"1,414","4,672","1,380",162,64,"7,692"
Shipped,0,"1,152",620,162,64,"1,898"
,"1,414","3,520",860,0,0,"5,794"
Physical,"1,414","3,520",860,0,0,"5,794"
Variance,0,0,0,0,0,0
and I need to make an array of all the dates mentioned (in this case, dates = ['11/13/15', '11/14/15'].
I believe it is possible to pull this info out using a regular expression, but I don't really understand how they work/how to go about this. So, how can I extract the dates?
EDIT: I can sort through the data by row using CSV.foreach, but the trouble I am having is to tell the program to pull out anything that matches a date format (ie. 11/13/15). Does that make more sense of my question?
Thank you!
- Sean
The correct one liner is:
File.open('yourfile.csv').read.scan /\d{2}\/\d{2}\/\d{2}/
and by the way \d{2} is so much nicer than \d\d and here's why:
you can see the 2. \d{2} reads like "2 digit number" (once you're
used to it)
if you want to change it to 1 or 2 digits you can do {1,2}
dates = []
File.open('yourfile.csv').each_line do |line|
if m = line.match(/^\d\d\/\d\d\/\d\d/)
dates.push m
end
end
puts dates
BTW. I am sure someone could write this as a one-liner, but this might be a little easier to understand for someone new to Ruby.
I making these assumptions:
All dates are of the format mm/dd/yy.
All dates that you want in the array are at the start of each line.
You don't need to verify that it is a valid date.
You could get a first approximation with this:
dates = CSV.open('x.csv').map{|r| r.select { |x| x =~ /\d\d\/\d\d\/\d\d/ } }.flatten
and then, if needed, scan through the elements of dates to make sure numbers are in the proper ranges (so that you don't accidentally include a date that claims to be Feb 31 2001). If you want to check the format, you could use DateTime.strptime and catch ArgumentErrors:
clean = dates.select do |d|
begin
# I'm guessing on the date format.
DateTime.strptime(d, '%m/%d/%y')
rescue ArgumentError
nil
end
end

Resources