Getting month from Date.today - ruby

How do you get the month as an integer from the below code
3.2.21#2.1.3 (#<VouchersController:0x007ff453)> t = (Date.today + 5).to_s
=> "2015-12-01"
3.2.21#2.1.3 (#<VouchersController:0x007ff453)> t.to_i
=> 2015
3.2.21#2.1.3 (#<VouchersController:0x007ff453)>
I can get the year. But how do I get the month as an integer so this returns 12?

The reason you're getting the year is only that you're converting the string "2015-12-01" to an integer.
When you use to_i on a Ruby string, it uses only leading digit characters, then throws away the rest of the string. When it reaches the first - character, it stops parsing as an integer and returns what it has so far: 2015.
In order to use the actual functionality of Date, don't use to_s to convert the object into a string.
require 'date'
t = Date.today + 5 # => #<Date: 2015-11-30 ((2457357j,0s,0n),+0s,2299161j)>
t.year # => 2015
t.month # => 11

Related

Regex date format less than operator

I am trying to use regex to verify a date format and I would like to check if the day is less than 32. Similarly, that the month is also less than 12. I have no idea how to about it. Currently, this is what I have;
^[0-1]?[0-9]{1}\-[0-3]?[0-9]{1}\-[0-9]{2,4}$
This regex achieves the format (m)m-(d)d-(yy)yy
TL;DR
Don't use regular expressions for comparison operations. Use a regex to split off values to compare, or use an actual parser.
Use Regular Expressions to Extract Comparables
Date comparisons is a really poor problem for regex to solve. At most, you should use a regular expression to extract your days of the month for a numeric comparison. For example:
date = '01-01-1970'
date.split('-')[1].to_i < 32
#=> true
However, the code above won't really tell you if a given date is valid. For example, what about February 30th or November 31st? Instead, you should attempt to parse the date to determine its validity.
Use a Date Parser
The best way to tell if a given date is valid is to parse it with a date parser, and then report a Boolean result or handle the exception. For example, you could attempt to parse the date with Date#parse.
Boolean Results
If you just want a Boolean result, you can coerce a valid/invalid parse to true or false. For example:
require 'date'
date = '01-33-1970'
!!(Date.parse date rescue nil)
#=> false
Rescuing and Reporting the Exception
Less magically, you would need to rescue ArgumentError from Date#parse. For example:
require 'date'
def valid_date? date_string
true if Date.parse date_string
rescue ArgumentError => e
STDERR.puts "#{e.class}: #{e}: '#{date_string}'"
false
end
valid_date? '11-31-1970'
This will do what you expect, albeit more verbosely. For example, the above example will print the exception to standard error, and then return false as the result.
ArgumentError: invalid date: '11-31-1970'
#=> false
^(?:[0-1][1-2]|[1-9])\-(?:3[0-1]|[0-2][1-9]|[1-9])\-[0-9]{2}(?:[0-9]{2})?$
should do what you're looking for. It will only allow months from 1-12 (either 1-9 or 01-12), days from 1-31 (either 1-9 or 01-31) and years of at least 2 digits with a maximum of four. Tested on regex101.
Basic:
Here is a regex that should do what you want:
^(0[1-9]|1[0-2]|[1-9])-(0[1-9]|[1-2][0-9]|3[0-1]|[1-9])-\d{2}(\d{2})?$
It matches months greater than 0 and less than 13, then -, then days greater than 0 and less than 32, then -, then years (2 digits or 4 digits).
Bonus:
Full regex for matching dates in that format with validation:
^((0?[13578]|10|12)-(([1-9])|(0[1-9])|([12])([0-9]?)|(3[01]?))-((19)([2-9])(\d{1})|(20)([01])(\d{1})|([8901])(\d{1}))|(0?[2469]|11)-(([1-9])|(0[1-9])|([12])([0-9]?)|(3[0]?))-((19)([2-9])(\d{1})|(20)([01])(\d{1})|([8901])(\d{1})))$
If you want to determine the string is a valid date, you'd be better off attempting to convert it. If it won't convert, it's not valid.
def date_valid?(date_string)
format = '%m/%d/' + (date_string.split(-).last.size == 4 ? '%Y' : '%y')
return true if Date.strptime(date_string, format)
rescue ArgumentError
return false
end

ruby Date.month with a leading zero

I have a Date object in Ruby.
When I do myobj.month I get 8. How do I get the date's month with a leading zero such as 08.
Same idea with day.
What I am trying to get at the end is 2015/08/05.
There is the possibility of using a formated string output
Examples:
puts sprintf('%02i', 8)
puts '%02i' % 8
%02i is the format for 2 digits width integer (number) with leading zeros.
Details can be found in the documentation for sprintf
In your specific case with a date, you can just use the Time#strftime od Date#strftime method:
require 'time'
puts Time.new(2015,8,1).strftime("%m")

Convert date to string without applying substraction

I am trying to convert 2015-08-02 into "2015-08-2" with Ruby. If I do:
(2015-08-02).to_s => SyntaxError: (irb):21: Invalid octal digit
2015-08-02.to_s => SyntaxError: (irb):21: Invalid octal digit
Mediate on this:
require 'date'
date = Date.parse('2015-08-02') # => #<Date: 2015-08-02 ((2457237j,0s,0n),+0s,2299161j)>
At this point the string containing the date has been parsed into a Date object.
date.strftime('%Y-%m-%e') # => "2015-08- 2"
date can be returned to a string using strftime, which has a number of ways of representing the various parts of the date. In this case you don't want a zero-padded day. Ruby supports using %e which is a space-padded day, but then you have the space to deal with:
date.strftime('%Y-%m-%e').delete(' ') # => "2015-08-2"

How do I split a string into separate components to parse into a DateTime? [duplicate]

This question already has answers here:
How to split string into 2 parts after certain position
(6 answers)
Closed 9 years ago.
I have a string that looks like:
20130518134721
yyyymmddhhmmss
This is basically the time of day. Knowing the format of the string, and knowing that the year is padded to four digits and all others are padded to two digits, how can I split this string so that I can extract specific information such as the month, or the hour?
You can use DateTime.strptime to convert the String to a DateTime object. You can do something like:
date = DateTime.strptime("20130518134721", "%Y%m%d%H%M%s") → datetime
After that, you can access to different methods of Date object in ruby like hour or mon.
date.hour
#=> 13
date.mon
#=> 5
Also, remember to require 'date' to use DateTime object.
require 'date'
d = DateTime.parse("20130518134721")
p d.hour #=> 13
p d.mon #=> 5
p d.min #=> 47
p d.day #=> 18
p d.year #=> 2013
Dates are best handled by the Date class as others have demonstrated. Splitting up strings into substrings with fixed width in general can be done with the unpack method:
year, month, day, hour, min, sec = "20130518134721".unpack("A4A2A2A2A2A2")
puts day #=> 18

How do I use the Time class on hash values?

I am working through Chris Pine's Ruby book, and I am slightly confused why my code doesn't quite work.
I have a file called birthdays.txt which has around 10 lines of text which resembles:
Andy Rogers, 1987, 02, 03
etc.
My code as follows:
hash = {}
File.open('birthdays.txt', "r+").each_line do |line|
name, date = line.chomp.split( /, */, 2 )
hash[name] = date
end
puts 'whose birthday would you like to know?'
name = gets.chomp
puts hash[name]
puts Time.local(hash[name])
My question is, why does the last line of code, Time.local(hash[name]) produce this output?:
1987-01-01 00:00:00 +0000
instead of:
1987-02-03 00:00:00 +0000
If you look at the documentation for Time.local,
Time.local doesn't parse a string. It expects you to pass a separate parameter for year, month, and date. When you pass a string like "1987, 02, 03", it takes that to be a single parameter, the year. It then tries to coerce that string into an integer - in this case, 1982.
so, basically, you want to slice up that string into the year, month, and day. there's multiple ways to do this. Here's one (it can be made shorter, but this is the most clear way)
year, month, date = date.split(/, */).map {|x| x.to_i}
Time.local(year, month, date)
line = "Andy Rogers, 1987, 02, 03\n"
name, date = line.chomp.split( /, */, 2 ) #split (', ', 2) is less complex.
#(Skipping the hash stuff; it's fine)
p date #=> "1987, 02, 03"
# Time class can't handle one string, it wants: "1987", "02", "03"
# so:
year, month, day = date.split(', ')
p Time.local(year, month, day)
# or do it all at once (google "ruby splat operator"):
p Time.local(*date.split(', '))
hash = {}
File.open('birthdays.txt').each_line do |line|
line = line.chomp
name, date = line.split(',',2)
year, month, day = date.split(/, */).map {|x| x.to_i}
hash[name] = Time.local(year, month, day)
end
puts 'Whose birthday and age do you want to find out?'
name = gets.chomp
if hash[name] == nil
puts ' Ummmmmmmm, dont know that one'
else
age_secs = Time.new - hash[name]
age_in_years = (age_secs/60/60/24/365 + 1)
t = hash[name]
t.strftime("%m/%d/%y")
puts "#{name}, will be, #{age_in_years.to_i} on #{t.strftime("%m/%d/")}"
end
had to move the Time.local call earlier in the program and then bingo, cheers guys!

Resources