# What date was so many months and years before

Discussion in 'Perl Misc' started by George Mpouras, Nov 29, 2013.

1. ### George MpourasGuest

#I want the calendar date of any number of years/months before.
# Currently I use the following, but maybe there is a better way.

use strict;
use warnings;
use Time::Local 'timelocal_nocheck';

my \$Months = 21;
my \$Years = 4;

my \$startfrom = \$^T;
\$Years += int \$Months / 12;
\$Months = \$Months % 12;
my @STARTFROM = localtime \$startfrom;
my \$d = \$STARTFROM[3];
my \$m = 0;
my \$y = 0;

if ( \$Months <= \$STARTFROM[4] )
{
\$y = \$STARTFROM[5] - \$Years;
\$m = \$STARTFROM[4] - \$Months
}
else
{
\$m = 12 - \$Months + \$STARTFROM[4];
\$y = \$STARTFROM[5] - \$Years - 1
}

my \$max_month_days = How_many_days_have_a_month(1+\$m, 1900+\$y);
\$d = \$max_month_days if \$d > \$max_month_days;
my \$backtime = Time::Local::timelocal_nocheck(@STARTFROM[0..2],
\$d, \$m, \$y);

print "epoch : \$backtime\n";
print "human : ", scalar(localtime \$backtime) ,"\n";

# How_many_days_have_a_month(MONTH, YEAR)
# MONTH 1 .. 12
# YEAR e.g. 1970
#
sub How_many_days_have_a_month
{
my \$month = \$_[0];
my \$year = \$_[1];
my \$days;
my \$leap_year;

if (\$year % 4)
{
\$leap_year=0
}
elsif (\$year % 100)
{
\$leap_year=1
}
elsif (\$year % 400)
{
\$leap_year=0
}
else
{
\$leap_year=1
}

if (\$month == 1) {\$days = 31}
elsif (\$month == 2) {\$days = \$leap_year ? 29 : 28}
elsif (\$month == 3) {\$days = 31}
elsif (\$month == 4) {\$days = 30}
elsif (\$month == 5) {\$days = 31}
elsif (\$month == 6) {\$days = 30}
elsif (\$month == 7) {\$days = 31}
elsif (\$month == 8) {\$days = 31}
elsif (\$month == 9) {\$days = 30}
elsif (\$month == 10) {\$days = 31}
elsif (\$month == 11) {\$days = 30}
elsif (\$month == 12) {\$days = 31}
\$days
}
George Mpouras, Nov 29, 2013

2. ### Jürgen ExnerGuest

George Mpouras <> wrote:
>#I want the calendar date of any number of years/months before.
># Currently I use the following, but maybe there is a better way.

Is there anything wrong with Date::Calc?

[long, awkward code snipped]

jue
Jürgen Exner, Nov 29, 2013

3. ### George MpourasGuest

Î£Ï„Î¹Ï‚ 29/11/2013 15:10, Î¿/Î· Henry Law Î­Î³ÏÎ±ÏˆÎµ:
> On 29/11/13 11:57, George Mpouras wrote:
>> #I want the calendar date of any number of years/months before.

>
> Here's a useful web site:
>
> http://search.cpan.org/search?query=date calculation

The same with Date::Manip

use Date::Manip;
my \$date = new Date::Manip:ate;
\$date->parse("epoch \$^T");
\$date->parse('-4:-21:0:0:0:0:0');
print \$date->printf('%d %b %Y %H:%M');

: )
George Mpouras, Nov 29, 2013
4. ### George MpourasGuest

Î£Ï„Î¹Ï‚ 29/11/2013 15:51, Î¿/Î· JÃ¼rgen Exner Î­Î³ÏÎ±ÏˆÎµ:
> George Mpouras <> wrote:
>> #I want the calendar date of any number of years/months before.
>> # Currently I use the following, but maybe there is a better way.

>
> Is there anything wrong with Date::Calc?
>
> [long, awkward code snipped]
>
> jue
>

Date::Calc is also fine.
this is happening when you do not take the correct turn early
George Mpouras, Nov 29, 2013
5. ### Rainer WeikusatGuest

Jürgen Exner <> writes:
> George Mpouras <> wrote:
>>#I want the calendar date of any number of years/months before.
>># Currently I use the following, but maybe there is a better way.

>
> Is there anything wrong with Date::Calc?
>
> [long, awkward code snipped]

Replacing a small amount of 'awkward code' with a large amount of
'awkward code' isn't necessarily an improvement.
Rainer Weikusat, Nov 29, 2013
6. ### Rainer WeikusatGuest

George Mpouras <> writes:
> #I want the calendar date of any number of years/months before.
> # Currently I use the following, but maybe there is a better way.

[...]

> # How_many_days_have_a_month(MONTH, YEAR)
> # MONTH 1 .. 12
> # YEAR e.g. 1970
> #
> sub How_many_days_have_a_month
> {
> my \$month = \$_[0];
> my \$year = \$_[1];
> my \$days;
> my \$leap_year;
>
> if (\$year % 4)
> {
> \$leap_year=0
> }
> elsif (\$year % 100)
> {
> \$leap_year=1
> }
> elsif (\$year % 400)
> {
> \$leap_year=0
> }
> else
> {
> \$leap_year=1
> }
>
> if (\$month == 1) {\$days = 31}
> elsif (\$month == 2) {\$days = \$leap_year ? 29 : 28}
> elsif (\$month == 3) {\$days = 31}
> elsif (\$month == 4) {\$days = 30}
> elsif (\$month == 5) {\$days = 31}
> elsif (\$month == 6) {\$days = 30}
> elsif (\$month == 7) {\$days = 31}
> elsif (\$month == 8) {\$days = 31}
> elsif (\$month == 9) {\$days = 30}
> elsif (\$month == 10) {\$days = 31}
> elsif (\$month == 11) {\$days = 30}
> elsif (\$month == 12) {\$days = 31}
> \$days
> }

--------
sub leap_year
{
return 0 if \$_[0] & 3;
return !!(\$_[0] % 100 || !(\$_[0] % 400));
}

sub days_per_month
{
my (\$m, \$y) = @_;

return 28 + leap_year(\$y)
if \$m == 2;

return 30 + ((\$m & 1) ^ (\$m >= 8));
}

print(days_per_month(\$ARGV[0], \$ARGV[1]), "\n");
--------

A long time ago, someone wrote on USENET that 'mathematics has to be
taught to people so that they learn to think'. I'll wonder if the poor
sod ever figures out that 'written tests' teach people how to copy
someone else's solution unnoticed ...
Rainer Weikusat, Nov 29, 2013
7. ### gamoGuest

El 29/11/13 17:29, Rainer Weikusat escribió:
> --------
> sub leap_year
> {
> return 0 if \$_[0] & 3;
> return !!(\$_[0] % 100 || !(\$_[0] % 400));
> }
>
>
> sub days_per_month
> {
> my (\$m, \$y) = @_;
>
> return 28 + leap_year(\$y)
> if \$m == 2;
>
> return 30 + ((\$m & 1) ^ (\$m >= 8));
> }
>
> print(days_per_month(\$ARGV[0], \$ARGV[1]), "\n");
> --------
>

This seems excellent. How would you implement delta_days?

TIA
gamo, Nov 30, 2013
8. ### Jürgen ExnerGuest

gamo <> wrote:
>How would you implement delta_days?

use Data::Calc;
\$Dd = Delta_Days(\$year1,\$month1,\$day1,
\$year2,\$month2,\$day2);

jue
Jürgen Exner, Dec 1, 2013
9. ### gamoGuest

El 01/12/13 01:50, Jürgen Exner escribió:
> gamo <> wrote:
>> How would you implement delta_days?

>
> use Data::Calc;
> \$Dd = Delta_Days(\$year1,\$month1,\$day1,
> \$year2,\$month2,\$day2);
>
>
> jue
>

Yes, thanks, I tryed that before and I encounter
extrange results.

http://www.telecable.es/personales/gamo/price.pl

Particulary with this function and Time_Date.
Maybe the differences are caused by the format
of the time: UTC then, CET now.

Best regards
gamo, Dec 1, 2013
10. ### gamoGuest

El 01/12/13 00:57, gamo escribió:
> El 29/11/13 17:29, Rainer Weikusat escribió:
>> --------
>> sub leap_year
>> {
>> return 0 if \$_[0] & 3;
>> return !!(\$_[0] % 100 || !(\$_[0] % 400));
>> }
>>
>>
>> sub days_per_month
>> {
>> my (\$m, \$y) = @_;
>>
>> return 28 + leap_year(\$y)
>> if \$m == 2;
>>
>> return 30 + ((\$m & 1) ^ (\$m >= 8));
>> }
>>
>> print(days_per_month(\$ARGV[0], \$ARGV[1]), "\n");
>> --------
>>

>
> This seems excellent. How would you implement delta_days?
>
> TIA
>

Here's all what my brain could make, using wour subs:

sub delta_days{
my (\$d1, \$m1, \$y1, \$d2, \$m2, \$y2) = @_;
my \$delta =0;
my \$ystep =1;
\$ystep = -1 if \$y1>\$y2;
for (my \$i=\$y1; \$i!=\$y2; \$i += \$ystep) {
\$delta += (365+leap_year(\$i))*\$ystep;
}
my \$mstep = 1;
\$mstep = -1 if \$m1>\$m2;
for (my \$j=\$m1; \$j!=\$m2; \$j += \$mstep){
\$delta += days_per_month(\$j)*\$mstep;
}
\$delta += (\$d2-\$d1);
return \$delta;
}

TIA
gamo, Dec 1, 2013
11. ### Jürgen ExnerGuest

gamo <> wrote:
>El 01/12/13 01:50, Jürgen Exner escribió:
>> gamo <> wrote:
>>> How would you implement delta_days?

>>
>> use Data::Calc;
>> \$Dd = Delta_Days(\$year1,\$month1,\$day1,
>> \$year2,\$month2,\$day2);

>
>Yes, thanks, I tryed that before and I encounter
>extrange results.
>
>http://www.telecable.es/personales/gamo/price.pl
>
>Particulary with this function and Time_Date.
>Maybe the differences are caused by the format
>of the time: UTC then, CET now.

Well, if your dates are in different time zones then obviously your are
asking for trouble. So standardize to whatever single time zone you
prefer.
And when you do have your dates in some normalized format, then just
convert both into seconds since the epoch, compute the difference, and
divide by 24*60*60.
This is close enough for all practical purposes because when your output
unit is days then you don't care about leap seconds or hours

jue
Jürgen Exner, Dec 1, 2013
12. ### gamoGuest

El 01/12/13 10:00, Jürgen Exner escribió:
> And when you do have your dates in some normalized format, then just
> convert both into seconds since the epoch, compute the difference, and
> divide by 24*60*60.
> This is close enough for all practical purposes because when your output
> unit is days then you don't care about leap seconds or hours
>
> jue

perldoc -f time mentions the DateTime module. Anyway, there is a
problem with the epoch, that is a too recent date. I.e. if I want
to calculate my age in days I think it's better to count over the
actual calendar (gregorian). :-(

Thanks
gamo, Dec 2, 2013
13. ### Charlton WilburGuest

>>>>> "RW" == Rainer Weikusat <> writes:

RW> Replacing a small amount of 'awkward code' with a large amount
RW> of 'awkward code' isn't necessarily an improvement.

Replacing a small amount of incorrect code with a large amount of
correct code, however, is a significant improvement.

Your allergy to code other people have written is puzzling; why are you
not writing directly in x86 assembler?

Charlton

--
Charlton Wilbur
Charlton Wilbur, Dec 2, 2013
14. ### Jürgen ExnerGuest

gamo <> wrote:
>El 01/12/13 10:00, Jürgen Exner escribió:
>> And when you do have your dates in some normalized format, then just
>> convert both into seconds since the epoch, compute the difference, and
>> divide by 24*60*60.
>> This is close enough for all practical purposes because when your output
>> unit is days then you don't care about leap seconds or hours
>>
>> jue

>
>perldoc -f time mentions the DateTime module. Anyway, there is a
>problem with the epoch, that is a too recent date. I.e. if I want
>to calculate my age in days I think it's better to count over the
>actual calendar (gregorian). :-(

If time since epoch is a signed integer then that would be no problem,
either.

jue
Jürgen Exner, Dec 2, 2013
15. ### Jürgen ExnerGuest

Rainer Weikusat <> wrote:
>Jürgen Exner <> writes:
>> George Mpouras <> wrote:
>>>#I want the calendar date of any number of years/months before.
>>># Currently I use the following, but maybe there is a better way.

>>
>> Is there anything wrong with Date::Calc?
>>
>> [long, awkward code snipped]

>
>Replacing a small amount of 'awkward code' with a large amount of
>'awkward code' isn't necessarily an improvement.

In my book a
use Date::Calc;
is significantly shorter than anything the OP wrote. Besides, chances
are much higher that it is correct.

But of course you are welcome to your own believes.

jue
Jürgen Exner, Dec 2, 2013
16. ### Charlton WilburGuest

>>>>> "BM" == Ben Morrow <> writes:

BM> DateTime is the 'big guns' when it comes to date and time
BM> manipulation in Perl. It does everything, and it does it right,
BM> but it's also quite a large chunk of code. IME it's usually
BM> simpler just to use it anyway, though.

Simpler and wiser; the first time your code screws up because the
legislative entity in your country changed the dates for Daylight
Savings Time or British Summer Time or whatever it's called in your
locale, you will have wasted more time than just using DateTime.

Assuming you got the time change right in the first place, that is. I
worked for a place (years ago) that had custom date handling code.
Twice a year we could count on customer service nightmares because no
two systems handled daylight savings time in the same way, never mind
correctly.

Charlton

--
Charlton Wilbur
Charlton Wilbur, Dec 2, 2013
17. ### \$BillGuest

On 12/2/2013 08:03, Jürgen Exner wrote:
> gamo <> wrote:
>
>> perldoc -f time mentions the DateTime module. Anyway, there is a
>> problem with the epoch, that is a too recent date. I.e. if I want
>> to calculate my age in days I think it's better to count over the
>> actual calendar (gregorian). :-(

>
> If time since epoch is a signed integer then that would be no problem,
> either.

Has to be unsigned if it goes to 2038 and you're on a 32 bitter.
\$Bill, Dec 3, 2013
18. ### \$BillGuest

On 12/3/2013 00:28, Ben Morrow wrote:
>
> Quoth "\$Bill" <>:
>>
>> Has to be unsigned if it goes to 2038 and you're on a 32 bitter.

>
> Even 32bit machines have floating point numbers which provide 53 bits of
> integer accuracy. Since 5.12 perl has used its own implementation of the
> time_t functions which uses floats on 32bit machines to handle dates
> beyond 2038.

True, but I was referring to the standard UNIX time functions which
were/are all integer arithmetic - not a Perl workaround.

Remember the Y2K issues - the next issue would have been the 2038
issue, but by then the computers will all be like 248 bitters.

> (And 'dates after 2038' is the problem, not 'dates before 1970'. time_t
> has always been signed.)
\$Bill, Dec 3, 2013
19. ### gamoGuest

El 03/12/13 11:20, \$Bill escribiÃ³:
> Remember the Y2K issues - the next issue would have been the 2038
> issue, but by then the computers will all be like 248 bitters.

256 bits. Maybe. But the clock speed in Ghz seems difficult to
improve, comparing to the number of processor's cores. The branch
of the optical computers seems a dead way. I don't expect to
see what happens that year, but I predict that nobody will be
impressed with computers as we are today. Maybe the key will be
brain-machines interfaces.
gamo, Dec 3, 2013