Yearless Ruby dates? - ruby

Is there a way to represent dates like 12/25 without year information? I'm thinking of just using an array of [month, year] unless there is a better way.

You could use the Date class and hard set the year to a leap year (so that you could represent 2/29 if you wanted). This would be convenient if you needed to perform 'distance' calculations between two dates (assuming that you didn't need to wrap across year boundaries and that you didn't care about the off-by-one day answers you'd get when crossing 2/29 incorrectly for some years).
It might also be convenient because you could use #strftime to display the date as (for example) "Mar-3" if you wanted.
Depending on the usage, though, I think I would probably represent them explicitly, either in a paired array or something like YearlessDate = Struct.new(:month,:day). That way you're not tempted to make mistakes like those mentioned above.
However, I've never had a date that wasn't actually associated with a year. Assuming this is the case for you, then #SeanHill's answer is best: keep the year info but don't display it to the user when it's not appropriate.

You would use the strftime function from the Time class.
time = Time.now
time.strftime("%m/%d")

While #Phrogz answer makes perfect sense, it has a downside:
YearlessDate = Struct.new(:month,:day)
yearless_date = YearlessDate.new(5, 8)
This interface is prone to MM, DD versus DD, MM confusion.
You might want to use Date instead and consider the year 0 as "yearless date" (provided you're not a historian dealing with real dates around bc/ad of course).
The year 0 is a leap year and therefore accommodates every possible day/month duple:
Date.parse("0000-02-29").leap? #=> true
If you want to make this convention air tight, just define your own class around it, here's a minimalistic example:
class YearlessDate < Date
private :year
end

The most "correct" way to represent a date without a year is as a Fixnum between 001 and 365. You can do comparisons on them without having to turn it into a date, and can easily create a date for a given year as needed using Date.ordinal

Related

How to read a timestamp

I have joined in a Data competition for students. They gave me timestamps of users' interaction. Is it useful?
Some of them are:
1615983880510
1615767552552
1615767577100
1616036788631
What you are looking at is a linux timestamp. It's actually a pretty interesting time representation. In short, it's just a number. And it represents the total number of seconds that have passed since January 1st 1970. A date known as "Unix Epoch Time".
Now, if you want to convert that into a readable date there are many ways to do it in basically every programing language. For example in python you might do something like this:
from datetime import datetime
def printdate(unix):
print(datetime.utcfromtimestamp(unix).strftime('%Y-%m-%d %H:%M:%S'))
BUT! It seems like your dates might actually be in miliseconds. Meaning that you might actually want to divide your dates by 1000 before passing them trough the function. So...
def printdatems(unix):
return printdate(unix/1000)
And there you go!
printdatems(1615983880510) #2021-03-17 12:24:40
printdatems(1615767552552) #2021-03-15 00:19:12
printdatems(1615767577100) #2021-03-15 00:19:37
printdatems(1616036788631) #2021-03-18 03:06:28
That's the output for the example dates you provided.
Of course you can find much more information on Wikipedia:
https://en.wikipedia.org/wiki/Unix_time
It's an intresting read!

Find objects that match today's date in Ruby

I have a calendar object that lists a number of object events. Each event contains a start_date. So when I call
event.start_date it gives me "8/6/2017 3:00pm"
I want to be able to find all the events with today's date. I'm using Chronic. So
Chronic.parse('today') would give me "8/6/2017 00:00:00" or something like it
I'm not using Rails just Ruby. Thus, I don't have all the special methods that come with Rails to help me with this.
If I enter a specific date I could find the events with that specific date by using
event.start_date.starts_with?("8/6/2017")
but I haven't figured out how to do it with today's date.
Any help would be appreciated.
time = Chronic.parse('today')
target_date = Chronic.parse('8/6/2017').to_date
puts time.to_date == target_date
Time class (what Chronic returns) holds datetimes. Date only holds dates. If you convert Time to Date, you discard the time-of-day information, and then you can compare just the date itself.
Note that Date.today is the same thing as Chronic.parse('today').to_date (and same as Time.now.to_date). Also note that Time class doesn't have #to_date unless you require 'time', but Chronic does it for you.

How do I get a file's age in days in Ruby?

How would I get the age of a file in days in Ruby?
NOTE that I need a way to accurately get the age of a given file; this means that leap years need to be taken into account.
I need this for a program that removes files after they reach a certain age in days, such as files that are 20 days or older.
And by age, I mean the last access time of a given file, so if a file hasn't been accessed in the past 20 days or more, it gets deleted.
In Perl, I know that you can use date::calc to calculate a date in terms of days since 1 AD, and I used to have a Common-Lisp program that used the Common-Lisp implementation of date::calc, but I don't have that anymore, so I've been looking for an alternative and Ruby seems to have the required capability.
path = '/path/to/file'
(Time.now - File.stat(path).mtime).to_i / 86400.0
#=> 1.001232
Here is the implementation of my above comment, it returns a floating point number expressing the number of days passed.
I know it is an old question, but I needed the same and came up with this solution that might be helpful for others.
As the difference is in days, there is not need to directly deal with seconds.
require 'date'
age_in_days = (Date.today - File.mtime(path).to_date).to_i
if age_in_days > 20
# delete logs
end
If using Rails, you can take advantage of ActiveSupport:
if File.mtime(path) < 20.days.ago
# delete logs
end
If you aren't using Rails, Eduardo's solution above would be my pick.

Any reason to use Date?

There are at least three types which represent time: Time, Date and DateTime(from ActiveSupport).
My problem is, could DateTime totally replace Date? In other words, if I can use DateTime, is there any reason to use Date instead?
require 'date'
d = Date.today
dt = DateTime.now
p Date.public_methods - DateTime.public_methods
#=>[:today]
p DateTime.public_methods - Date.public_methods
#=>[:now]
p d.public_methods - dt.public_methods
#=>[]
p dt.public_methods - d.public_methods
#=>[:hour, :min, :minute, :sec, :second, :sec_fraction, :second_fraction, :offset, :zone, :new_offset]
DateTime is a subclass of Date. Using DateTime, you lose the today Class method and get now in return. You don't lose instance methods.
If you want to store only the date, for example a birthday, or a certain day where an event takes place, then it can be easier to use only date. Then you have no troubles which arise from different time zones and time zone calculations. If you use DateTime, then if you add an offset of -2 hours to 00:00 am, you get 10:00 pm of the previous day.
Date does not store any information about the time, neither with the timezone. So you might get into trouble if at some point you'll need to use time data.
Cf this link, which I found clear about what classes should be used, when, and how.

How to get UTC timestamp in Ruby?

How to get UTC timestamp in Ruby?
You could use: Time.now.to_i.
time = Time.now.getutc
Rationale: In my eyes a timestamp is exactly that: A point in time. This can be accurately represented with an object. If you need anything else, a scalar value, e.g. seconds since the Unix epoch, 100-ns intervals since 1601 or maybe a string for display purposes or storing the timestamp in a database, you can readily get that from the object. But that depends very much on your intended use.
Saying that »a true timestamp is the number of seconds since the Unix epoch« is a little missing the point, as that is one way of representing a point in time, but it also needs additional information to even know that you're dealing with a time and not a number. A Time object solves this problem nicely by representing a point in time and also being explicit about what it is.
The default formatting is not very useful, in my opinion. I prefer ISO8601 as it's sortable, relatively compact and widely recognized:
>> require 'time'
=> true
>> Time.now.utc.iso8601
=> "2011-07-28T23:14:04Z"
if you need a human-readable timestamp (like rails migration has) ex. "20190527141340"
Time.now.utc.to_formatted_s(:number) # using Rails
Time.now.utc.strftime("%Y%m%d%H%M%S") # using Ruby
Usually timestamp has no timezone.
% irb
> Time.now.to_i == Time.now.getutc.to_i
=> true
What good is a timestamp with its granularity given in seconds? I find it much more practical working with Time.now.to_f. Heck, you may even throw a to_s.sub('.','') to get rid of the decimal point, or perform a typecast like this: Integer(1e6*Time.now.to_f).
Time.utc(2010, 05, 17)
time = Time.zone.now()
It will work as
irb> Time.zone.now
=> 2017-12-02 12:06:41 UTC
The proper way is to do a Time.now.getutc.to_i to get the proper timestamp amount as simply displaying the integer need not always be same as the utc timestamp due to time zone differences.

Resources