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"
Related
I created a hash out of file that contains date as a string in different formats (like September 1988, the other line would be July 11th 1960, and sometimes year only)
require 'date'
def create_book_hash(book_array)
{
link: book_array[0],
title: book_array[1],
author: book_array[2],
pages: book_array[3].to_i,
date: book_array[4],
rating: book_array[5].to_f,
genre: book_array[6]
}
end
def books_sorted_by_date (books_array)
books_array.sort_by { |key| Date.strptime(key[:date], '%Y, %m') }
end
book_file= File.read("books.txt")
.split("\n")
.map { |line| line.split("|")}
.map { |book_array| create_book_hash(book_array)}
puts books_sorted_by_date(book_file)
I'm trying to sort books by date, so it would be in ascending order by year and since I have different string types, i put a hash key as the first argument in strptime to access all the values in :date . And that gives me \strptime': invalid date (Date::Error).` I don't understand why and what can I do to convert these strings into date objects? (just ruby, no rails)
Handle Both Standard and Custom Date Strings
Date#parse doesn't handle arbitrary strings in all cases. Even when it does, it may not handle them the way you expect. For example:
parse_date "1/1/18"
#=> #<Date: 2001-01-18 ((2451928j,0s,0n),+0s,2299161j)>
While Date#parse handles many date formats automagically, it only successfully parses objects that match its internal expectations. When you have multiple or arbitrary date formats, you have to define your own date specifications using Date#strptime to handle those formats that Date#parse doesn't understand, or that it handles incorrectly. For example:
require 'date'
def parse_date str
Date.parse str
rescue Date::Error
case str
when /\A\d{4}\z/
Date.strptime str, '%Y'
when /\A\d{2}\z/
Date.strptime str, '%y'
else
raise "unexpected date format: #{str}"
end
end
date_samples = ["July 11th 1960", "September 1988", "1776"]
date_samples.map { |date| parse_date(date) }
#=> [#<Date: 1960-07-11 ((2437127j,0s,0n),+0s,2299161j)>, #<Date: 1988-09-01 ((2447406j,0s,0n),+0s,2299161j)>, #<Date: 1776-01-01 ((2369731j,0s,0n),+0s,2299161j)>]
This obviously is not an exhaustive list of potential formats, but you can add more examples to date_samples and update the case statement to include any unambiguous date formats you expect from your data set.
Date.strptime needs two parameters date-string and format of the date. To use strptime you need to know what is the format of the string beforehand.
see some examples here - https://apidock.com/ruby/Date/strptime/class
In your program you don't know exact format of the date on that line when it parses so you need to try something like -
def books_sorted_by_date (books_array)
books_array.sort_by { |key| Date.parse(key[:date]) }
end
Date.parse needs one argument - date string, it then tries to guess the date.
see details - https://apidock.com/ruby/v2_6_3/Date/parse/class
You will still have problems with just year with this approach.
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
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
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")
I have a String 20120119 which represents a date in the format 'YYYYMMDD'.
I want to parse this string into a Ruby object that represents a Date so that I can do some basic date calculation, such as diff against today's date.
I am using version 1.8.6 (requirement).
You could use the Date.strptime method provided in Ruby's Standard Library:
require 'date'
string = "20120723"
date = Date.strptime(string,"%Y%m%d")
Alternately, as suggested in the comments, you could use Date.parse, because the heuristics work correctly in this case:
require 'date'
string = "20120723"
date = Date.parse(string)
Both will raise an ArgumentError if the date is not valid:
require 'date'
Date.strptime('2012-March', '%Y-%m')
#=> ArgumentError: invalid date
Date.parse('2012-Foo') # Note that '2012-March' would actually work here
#=> ArgumentError: invalid date
If you also want to represent hours, minutes, and so on, you should look at DateTime. DateTime also provides a parse method which works like the parse method on Date. The same goes for strptime.