Find the specific day of a given month and given year - perl-module

is there a Perl module which could give me for input month and year, let say, 06-2005, what the last day of this month for this year is? For this example, it is easy, because June always has 30 days, so the last day will be 30-06-2005. But it is not the case for February. So, if I have 02-1997, I would like to know whether to return 28-02-1997 or 29-02-1997. Thanks in advance.

Yes, there is a subroutine Days_in_Month in the Date::Calc module.
use strict;
use warnings;
use Date::Calc qw/Days_in_Month/;
for my $m (1 .. 12) {
print Days_in_Month(2005, $m), "\n";
}
OUTPUT
31
28
31
30
31
30
31
31
30
31
30
31

The last day in a given month is the "0th" day of the following month. mktime() takes a 0-based month and 1900-based year number, and returns an epoch timestamp.
use POSIX qw( mktime strftime );
sub last_day {
my ( $year, $mon ) = #_;
return mktime(0,0,0, 0, $mon, $year-1900);
}
You can pass that to localtime or strftime.
say scalar localtime last_day(2005, 5)
'Tue May 31 00:00:00 2005'
say scalar localtime last_day(2005, 6)
'Thu Jun 30 00:00:00 2005'
say scalar localtime last_day(1997, 2)
'Fri Feb 28 00:00:00 1997'
say scalar localtime last_day(2012, 2)
'Wed Feb 29 00:00:00 2012'
say (localtime last_day(1997, 2))[3]
'28'
say strftime "%d", localtime last_day(1997, 2)
'28'

As others said, use DateTime:
use DateTime;
my $dt = DateTime->last_day_of_month('year'=>2000, 'month'=>2);
print $dt->ymd; # '2000-02-29'
While DateTime may not be the fastest module for handling dates/times, it's definitely the most complete one. It's also the only one that handles the various quirks of date/time math correctly.

Related

Time ago in words convert into system date-time

Trying to convert strings like 9 weeks ago, 1 year, 6 months ago, 20 hours ago into a ruby time object like Tue, 10 Mar 2015 12:06:15 PDT -07:00.
I've been doing this:
eval("10 days ago".gsub(' ', '.'))
This works fine, but for strings like 1 year, 6 months ago blows up.
I just need to do comparisons like:
eval("10 days ago".gsub(' ', '.')) < (Time.now - 7.days)
I'm using sinatra so no fancy rails helpers.
Please never use eval in production code..
Converting from timeago notation would be quite complex and resource intensive.
However, this way seems the least error prone: It will convert a string like "5 seconds ago" to "5S" and use mapping to find what it means in time, after which it will subtract that time from the current time.
The parse string is dynamically built so it can accomodate most every timeago notation.
require('date')
mapping = {"D"=> "%d","W"=>"%U","H"=>"%T","Y"=>"%Y","M"=>"%m","S"=>"%S"}
timerel = "1 year, 6 months ago".split(",").map { |n| n.gsub(/\s+/, "").upcase()[0,2].split('')}
Date.strptime(
timerel.map {|n| n[0]}.join(" "),
timerel.map {|n| mapping[n[1]]}.join(" ")
)
date = Date.new(0) + (Date.today - Date.strptime(timerel.map {|n| n[0]}.join(" "), timerel.map {|n| mapping[n[1]]}.join(" ")))
=> #<Date: 2014-10-10 ((2456941j,0s,0n),+0s,2299161j)>
It goes without saying that is very error prone. Use at your own risk:
def parse(date:)
eval(date.gsub(/ ?(,|and) ?/, '+').tr(' ', '.').gsub(/^(.*)(\.ago)$/, '(\1)\2'))
end
parse(date: '1 year, 6 months ago') # => Wed, 10 Sep 2014 21:29:11 BST +01:00
parse(date: '1 year, 6 months, 3 weeks, 6 days, 9 hours and 12 seconds ago')
# => Thu, 14 Aug 2014 12:33:07 BST +01:00
The idea is to convert the original string to:
'(1.year+6.months).ago'

How to interpret RFC3339 UTC timestamp

How should I interpret all aspects of the following timestamps? Where is the time based and how do timezones apply?
2015-11-15T14:45:28Z
2015-11-15T14:45:28.9694Z
2015-11-15T14:45:28.969412345Z
Below is my thoughts...
Date: 2015-11-15
???: T
Hours: 14
Minutes: 45
Seconds: 28 OR 28.9694 OR 28.969412345
???: Z
Most of your values are attributed correctly. The date portion (2015-11-15) is in the order YYYY-MM-DD, time in HH:MM:SS.ffff.
T indicates the start of the time portion of the date time.
Z indicates the time zone is UTC. Next to Z, you could have a format like Z+02:00, which indicates the time zone is UTC + 2 hours.

Rails 4 parse a date in a different language

I have a text_field :birthday_line in my user form, that I need to parse into the user's birthday attribute.
So I'm doing something like this in my User class.
attr_accessor :birthday_line
before_save :set_birthday
def set_birthday
self.birthday = Date.strptime(birthday_line, I18n.translate("date.formats.default")
end
But the problem is that for some reason it gives me an error saying Invalid date when I try to pass in a string 27 января 1987 г. wich should be parsed to 1987-01-27.
The format and month names in my config/locales/ru.yml
ru:
date:
formats:
default: "%d %B %Y г."
month_names: [~, января, февраля, марта, апреля, мая, июня, июля, августа, сентября, октября, ноября, декабря]
seem to be correct.
Date.parse also doesn't help, it just parses the day number (27) and puts the month and year to todays date (so it'll be September 27 2013 instead of January 27 1987).
I had the same problem and what I can suggest:
string_with_cyrillic_date = '27 Января 1987'
1)create array of arrays like this
months = [["января", "Jan"], ["февраля", "Feb"], ["марта", "Mar"], ["апреля", "Apr"], ["мая", "May"], ["июня", "Jun"], ["июля", "Jul"], ["августа", "Aug"], ["сентября", "Sep"], ["октября", "Oct"], ["ноября", "Nov"], ["декабря", "Dec"]]
2) Now you can iterate this and find your cyrillic month:
months.each do |cyrillic_month, latin_month|
if string_with_cyrillic_date.match cyrillic_month
DateTime.parse string_with_cyrillic_date.gsub!(/#{cyrillic_month}/, latin_month)
end
end
And now you will receive the date that you expect
27 Jan 1987

How to check day is last date of month in ruby

I want to check if a day is the last day of the month and if it is, for a function to return true, otherwise return false.
For example, if I pass in an argument of "Sun, 30 Jun 2013", the function is to return true, because it is the last day of the month, however if I pass in the argument "Mon, 03 Jun 2013" the function is to return false.
How can this be accomplished using Ruby.
If you're using Rails, you can always do this as well:
date == date.end_of_month
or to check the end of this month:
date == Date.today.end_of_month
I would do something like this
def is_last_day(mydate)
mydate.month != mydate.next_day.month
end
Parse the date with DateTime.parse. DateTime.parse has built-in support for many date formats (including those in your example), but you can always use DateTime.strptime for more complex formats.
See if the next day is 1 (first day of next month) by using Date#+.
require 'date'
def last_day?(date_string)
date = DateTime.parse(date_string)
(date + 1).day == 1
end
puts last_day?('Sun, 30 Jun 2013') # true
puts last_day?('Mon, 03 Jun 2013') # false

Rails 3.2.8 - How do I get the week number from Rails?

I would like to know how to get the current week number from Rails and how do I manipulate it:
Translate the week number into date.
Make an interval based on week number.
Thanks.
Use strftime:
%U - Week number of the year. The week starts with Sunday. (00..53)
%W - Week number of the year. The week starts with Monday. (00..53)
Time.now.strftime("%U").to_i # 43
# Or...
Date.today.strftime("%U").to_i # 43
If you want to add 43 weeks (or days,years,minutes, etc...) to a date, you can use 43.weeks, provided by ActiveSupport:
irb(main):001:0> 43.weeks
=> 301 days
irb(main):002:0> Date.today + 43.weeks
=> Thu, 22 Aug 2013
irb(main):003:0> Date.today + 10.days
=> Sun, 04 Nov 2012
irb(main):004:0> Date.today + 1.years # or 1.year
=> Fri, 25 Oct 2013
irb(main):005:0> Date.today + 5.months
=> Mon, 25 Mar 2013
You are going to want to stay away from strftime("%U") and "%W".
Instead, use Date.cweek.
The problem is, if you ever want to take a week number and convert it to a date, strftime won't give you a value that you can pass back to Date.commercial.
Date.commercial expects a range of values that are 1 based.
Date.strftime("%U|%W") returns a value that is 0 based. You would think you could just +1 it and it would be fine. The problem will hit you at the end of a year when there are 53 weeks. (Like what just happened...)
For example, let's look at the end of Dec 2015 and the results from your two options for getting a week number:
Date.parse("2015-12-31").strftime("%W") = 52
Date.parse("2015-12-31").cweek = 53
Now, let's look at converting that week number to a date...
Date.commercial(2015, 52, 1) = Mon, 21 Dec 2015
Date.commercial(2015, 53, 1) = Mon, 28 Dec 2015
If you blindly just +1 the value you pass to Date.commercial, you'll end up with an invalid date in other situations:
For example, December 2014:
Date.commercial(2014, 53, 1) = ArgumentError: invalid date
If you ever have to convert that week number back to a date, the only surefire way is to use Date.cweek.
date.commercial([cwyear=-4712[, cweek=1[, cwday=1[, start=Date::ITALY]]]]) → date
Creates a date object denoting the given week date.
The week and the day of week should be a negative
or a positive number (as a relative week/day from the end of year/week when negative).
They should not be zero.
For the interval
require 'date'
def week_dates( week_num )
year = Time.now.year
week_start = Date.commercial( year, week_num, 1 )
week_end = Date.commercial( year, week_num, 7 )
week_start.strftime( "%m/%d/%y" ) + ' - ' + week_end.strftime("%m/%d/%y" )
end
puts week_dates(22)
EG: Input (Week Number): 22
Output: 06/12/08 - 06/19/08
credit: Siep Korteling http://www.ruby-forum.com/topic/125140
Date#cweek seems to get the ISO-8601 week number (a Monday-based week) like %V in strftime (mentioned by #Robban in a comment).
For example, the Monday and the Sunday of the week I'm writing this:
[ Date.new(2015, 7, 13), Date.new(2015, 7, 19) ].map { |date|
date.strftime("U: %U - W: %W - V: %V - cweek: #{date.cweek}")
}
# => ["U: 28 - W: 28 - V: 29 - cweek: 29", "U: 29 - W: 28 - V: 29 - cweek: 29"]

Resources