Is there a Perl equivalent of SAS' INTNX function or Javascript's Dateadd function? - perl

I'm trying to convert Chinese lunar system to Gregorian using Perl, both for fun and for learning purposes. I thought there was some kind of easy mathematical algorithm to do the job but turned out I was wrong. Anyway, after a little googling around I came aross some ready SAS code that could do the job. Well, it was not too difficult for me to figure out how to rewrite most of the code in Perl. But there is something like this:
Convert2Gregorian = INTNX ('day', conDate, AddDay-1);
Google tells me that INTNX is a function that can return a date after which a certain date interval has been added. But using the keywords "Perl INTNX" gave me nothing useful. I then found a script written in Javascript, the general approach is about the same except that the Javascript uses Dateadd function, something like:
Return DateAdd(DateInterval.Day, AddDay - 1, conDate)
I also tried searching using PPM but was unable to find the module I wanted. Can someone kindly give me some pointers?
Thanks in advance :)
Update
Thanks to #psilva and #hobbs :)
Haha, finally I can translate one programming language to another. It's fun :)
Just for fun and maybe for later reference:
Here's the original SAS code and the Perl code translated by me. Correct me if I'm wrong :)
Note: Data incomplete.
SAS CODE IS AS FOLLOWS:
data DateNtoG (drop = Ngstrings conDate AddMonth AddDay Mrunyue I);
array NGlist{43} $18_TEMPORARY_(
"010110101010170130" /*(1968)*/
"010101101010000217" /*(1969)*/
"100101101101000206" /*(1970)*/
"010010101110150127" /*(1971)*/
);
input tYear tMonth tDay RunYue;
if (tyear >1967 and tyear<2011) then do;
NGstrings = NGlist(tYear - 1967);
conDate = MDY (substr (NGstrings,15,2),(NGstrings, 17,2), tYear);
AddMonth = tMonth;
select (substr(NGstrings, 14, 1));
when ("A" ) Mrunyue=10;
when ("B" ) Mrunyue=11;
when ( "C" ) Mrunyue=12;
otherwise Mrunyue = substr (NGstrings, 14,1);
end;
if ((RunYue=1) and (tMonth=Mrunyue) ANDMrunyue>0)or ((tMonth Mrunyue) AND Mrunyue>0) then
do;
Addmonth = tMonth+1;
end;
AddDay = tDay;
do i = 1 To AddMonth-1;
AddDay = AddDay + 29 + substr(NGstrings,i,1);
end;
dNtoG = INTNX ('day', conDate, AddDay - 1);
put "Transfereddate:" dNtoGdate9.;
end;
TRANSLATED Perl CODE IS AS FOLLOWS:
didn't handle the undefined situations for the moment
(I Changed the original SAS variable names)
#!perl
# A Chinese-Gregorian Calendar System Converter just for Testing
use Date::Calc qw(Add_Delta_Days);
use integer;
use strict;
use warnings;
$_ = shift #ARGV;
if (length == 8) {
$_.=0;
}
my ($Lunar_Year,$Lunar_Month,$Lunar_Day,$Leap_Month) = /(\d\d\d\d)(\d\d)(\d\d)(\d)/;
my %Lunar_Year_Patterns = qw/1968 010110101010170130 1969 010101101010000217 1970 100101101101000206 1971 010010101110150127/;
if (substr ($Lunar_Year_Patterns{$Lunar_Year},13,1) =~ /A/) {
$Leap_Month = 10;
} elsif (substr ($Lunar_Year_Patterns{$Lunar_Year},13,1)=~ /B/){
$Leap_Month = 11;
} elsif (substr ($Lunar_Year_Patterns{$Lunar_Year},13,1)=~ /C/){
$Leap_Month = 12;
} else {
$Leap_Month = substr($Lunar_Year_Patterns{$Lunar_Year},13,1);
}
my $First_Lunar_Day_In_Gregorian_Month = substr($Lunar_Year_Patterns{$Lunar_Year},14,2);
my $First_Lunar_Day_In_Gregorian_Day = substr($Lunar_Year_Patterns{$Lunar_Year},16,2);
my $AddMonth;
if ( (($Leap_Month ==1) && ($Lunar_Month == $Leap_Month) && ($Leap_Month > 0)) || (($Lunar_Month > $Leap_Month) && ($Leap_Month>0) ) ){
$AddMonth = $Lunar_Month +1;
} else {
$AddMonth = $Lunar_Month;
}
my $AddDay;
$AddDay = $Lunar_Day;
for(my $i = 1; $i <= $AddMonth - 1; $i++){
$AddDay = $AddDay +29 + substr($Lunar_Year_Patterns{$Lunar_Year},$i,1);
}
my #Gregorian = Add_Delta_Days($Lunar_Year,$First_Lunar_Day_In_Gregorian_Month,$First_Lunar_Day_In_Gregorian_Day,$AddDay -1);
print #Gregorian;

DateTime is the 800-pound gorilla of date handling. It's quite large, but it's large because it does a lot, and more importantly, it does it right.
With DateTime you would simply construct the DateTime object for the starting date, and then obtain the ending date by adding: $dt->add(days => $add_days).
Also, there's a DateTime::Calendar::Chinese that you can compare your results with, even if you want to reinvent this particular wheel for fun :)

Check out the delta functions in Date::Calc and Date::Calc::Object, and Date::Calc::PP. Specifically, you might want to look at the DateCalc_add_delta_days subroutine in the PP source.
You could also try looking at the source of Calendar::China.

Related

Need to create GPA Calculator to run in command line

Need to write a GPA calculator using the provided dictionary to output the gpa based on the 4 arguments of letter grades. I can get the code to run in google colab or other IDEs, but I get no output in CL. Can someone point me to what I am missing?
'''
import sys
#print (sys.argv[1])
#print (sys.argv[2])
#print (sys.argv[3])
#print (sys.argv[4])
def gpa_calculator():
grades = [sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]]
grades_upper = [each_string.upper() for each_string in grades]
points = 0
grade_chart = {'A':4.0, 'A-':3.66, 'B+':3.33, 'B':3.0, 'B-':2.66,
'C+':2.33, 'C':2.0, 'C-':1.66, 'D+':1.33, 'D':1.00, 'D-':.66, 'F':0.00}
for grade in grades_upper:
points += grade_chart[grade]
gpa = points / len(grades)
rounded_gpa = round(gpa,2)
return rounded_gpa
print (rounded_gpa)
gpa_calculator()'''
return rounded_gpa
print (rounded_gpa)
You seem to be returning the value from the function before you reach the print statement. I'm guessing the value is returned correctly, but you don't do anything with the return value when calling the function, so nothing is output to the screen.
You should move the print(...) call above the return statement, or print out the result when calling the function:
print(gpa_calculate())
That's because you return first before print.
In jupyter notebook like google colab, each cell will print anything the last line returns (if any). That's why in such environment you get output.
Corrected code:
import sys
#print (sys.argv[1])
#print (sys.argv[2])
#print (sys.argv[3])
#print (sys.argv[4])
def gpa_calculator():
grades = [sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]]
grades_upper = [each_string.upper() for each_string in grades]
points = 0
grade_chart = {'A':4.0, 'A-':3.66, 'B+':3.33, 'B':3.0, 'B-':2.66,
'C+':2.33, 'C':2.0, 'C-':1.66, 'D+':1.33, 'D':1.00, 'D-':.66, 'F':0.00}
for grade in grades_upper:
points += grade_chart[grade]
gpa = points / len(grades)
rounded_gpa = round(gpa,2)
print(rounded_gpa)
return rounded_gpa
gpa_calculator()
output:
C:\Users\XXXXX\Desktop>python3 a.py A B C D
2.5
C:\Users\XXXXX\Desktop>python3 a.py A A A A
4.0

Matlab Code for weekdays and weekends [duplicate]

This question already has an answer here:
Weekend extraction in Matlab
(1 answer)
Closed 6 years ago.
I were able to successfully made a schedule in which the output is 1 if time is between 7 AM-5PM and otherwise 0, time is based on my computer. However the day Monday-Sunday is based on my computer as well.. I cant find the solution to put an output 1 on Monday-Saturday and output 0 on Sunday. The code I have is below
function y = IsBetween5AMand7PM
coder.extrinsic('clock');
time = zeros(1,6);
time = clock;
current = 3600*time(4) + 60*time(5) + time(6); %seconds passed from the beginning of day until now
morning = 3600*7; %seconds passed from the beginning of day until 7AM
evening = 3600*17; %seconds passed from the beginning of day until 5PM
y = current > morning && current < evening;
end
Now the time here is correct already what I need is for the day (Monday-Sunday) to have my needed output. Also this matlab code is inside a matlab function on Simulink block.
If you use weekday like this, you can generate a 0/1 value as you specified for today's date:
if (weekday(now) > 1)
day_of_week_flag = 1;
else
day_of_week_flag = 0;
or if you like, this one-liner does the same thing, but may not be as easy to read if you're not familiar with the syntax:
day_of_week_flag = ( weekday(now) > 1);
You can also use date-strings like this to convert other dates:
day_of_week_flag = ( weekday('01-Mar-2016') > 1 )
Finally, if you have a numeric array of date/time values, like [2016 3 3 12 0 0], you first need to convert to a serial date using datenum, then use weekday:
time = clock;
day_of_week_flag = ( weekday(datenum(time)) > 1);
An alternate way to check without using weekday is the following:
time = clock;
day_of_week = datestr(time, 8);
if (day_of_week == 'Sun')
day_of_week_flag = 0;
else
day_of_week_flag = 1;

Execute code if it is after 1 of April

I have a simple problem but I can't seem to find an anwser.
I want to execute code:
if current date < 1 April then
do stuff
else
do other stuff
end if
I thought it was pretty easy, since I can get date and format it my way, but problem is when user has different Date format. I did something like this:
Private Sub UserForm_Initialize()
Dim rok As Long, rok_kontrola As Date
rok = Format(Date, "yyyy")
rok_kontrola = Format(Date, "dd-mm-yyyy")
If rok_kontrola < "01-04-" & rok Then
Me.Controls("rok1").Value = True
Else
Me.Controls("rok2").Value = True
End If
End Sub
Try this one:
If Date < DateSerial(Year(Date), 4, 1) Then
Me.Controls("rok1").Value = True
Else
Me.Controls("rok2").Value = True
End If

macro to extract dates from a weeks date range string and add 7 days to print next date range

I am writing a macro that processes an excel with lots of data. One of the rows contains a date range like wkstartdate - wkenddate and I would like to use dateadd function to print next date range every week (like '27-01-14 - 02-02-14' in below case) but unable to do so.
'06-01-14 - 12-01-14'
'13-01-14 - 19-01-14'
'20-01-14 - 26-01-14'
I used below excerpt which fails:
Range("E" & Lastrow).Select
prwk = Split(ActiveCell.Value, "-")
'curr_wkstart = DateAdd("d", 7, prwk(1)) 'error as maybe prwk(1) isnt correct format
'curr_wkend = DateAdd("d", 7, prwk(2)) 'error
Range("E" & Lastrow + 1).Value = curr_wkstart & curr_wkend 'no result
For testing purpose I print, prwk(1) which is 20/01/14 in the above case, in a diff cell and add 7 days, which gives me 1/21/2020 instead of '27/01/14'. I also tried using Cdate function, but still error
Can you please advise??
I think what you want to use here are the Format and DateSerial functions. Here's how I came at it:
Function GetNextWeek(TheStartWeek)
a = Split(TheStartWeek, " - ")
b = Split(a(1), "-")
c = DateSerial(b(2), b(1), b(0)) + 1
d = c + 6
GetNextWeek = Format(c, "dd-mm-yy") & " - " & Format(d, "dd-mm-yy")
End Function
Sub Test()
Debug.Print GetNextWeek("13-01-14 - 19-01-14") 'Givs you "20-01-14 - 26-01-14"
End Sub
Hope this helps.

Perl subroutine running more slowly with each successive call

I have a strange problem where I call the same subroutine several times in succession, processing the same data, and my code takes successively longer with each call. My program is doing some matrix mathematics, but I am asking a more general question here to see if anyone has the same issue, so I don't know if the specifics are important. Below is code at the very bottom of my main program loop.
use Time::HiRes qw (gettimeofday);
($lus2_sec,$lus2_usec) = gettimeofday();
#blah_LU_v2 = invert_LU_v2(#zmatrix);
($lue2_sec,$lue2_usec) = gettimeofday();
$lu2_elapsed = sprintf("%.3f", ($lue2_sec+($lue2_usec/1000000))-($lus2_sec+($lus2_usec/1000000)));
syswrite STDOUT, "$lu2_elapsed seconds\n";
($lus2_sec,$lus2_usec) = gettimeofday();
#blah_LU_v2 = invert_LU_v2(#zmatrix);
($lue2_sec,$lue2_usec) = gettimeofday();
$lu2_elapsed = sprintf("%.3f", ($lue2_sec+($lue2_usec/1000000))-($lus2_sec+($lus2_usec/1000000)));
syswrite STDOUT, "$lu2_elapsed seconds\n";
Each subroutine operates on the same #zmatrix data, which it does not change. I get the exact same answer from each subroutine call (verified earlier), so I know it's not screwing up the input data. The subroutine is also a simple single threaded structure, and this is being run on an idle 12-core workstation with 96GB of RAM. There should be no disk swapping or CPU issue since this machine has more than enough power to handle this relatively small matrix. However, the output of the program results in something like this (running five successive calls of the subroutine, obviously):
96.485 seconds
99.116 seconds
100.036 seconds
100.615 seconds
101.494 seconds
The subroutine keeps getting a little slower for as many tests as I've run. If I terminate and relaunch the program from the command line, it will start out at around 96 seconds and then slow down from there every single time. Why would it be slowing down like this?
The subroutine with the issue is shown below. Note that I've now used NYTProf to nail down the time increase to calls of Math::Complex::_multiply and _minus. Each time I call the invert_LU_v2 subroutine, it makes the same number of calls to Math::Complex, but they take a few % longer on subsequent calls of invert_LU_v2. Please also feel free to critique my code and let me know what else I'm doing wrong. I am a beginner, have no training, and don't really know what I'm doing.
sub invert_LU_v2 {
my(#junk) = (#_);
my #matrix_local;
my #matrix_L;
my #matrix_B;
my #matrix_inverse;
my $tt;
my $row;
my $col;
my $temp;
my $reduced;
my $normalize = 1;
my $multiplier = 1;
my $dimension = #junk - 1;
for($row=1;$row<=$dimension;$row++){
for($col=1;$col<=$dimension;$col++){
$matrix_local[$row][$col]=$junk[$row][$col];
}
}
for($row=1;$row<=$dimension;$row++){
for($col=1;$col<=$dimension;$col++){
if($row==$col){$matrix_L[$row][$col] = 1;$matrix_B[$row][$col] = 1;}
else {$matrix_L[$row][$col] = 0;$matrix_B[$row][$col] = 0;}
}
}
for($row=1;$row<=$dimension;$row++){
$normalize = $matrix_local[$row][$row];
$matrix_L[$row][$row] = $normalize;
for($col=1;$col<=$dimension;$col++){
$matrix_local[$row][$col] /= $normalize;
}
for($temp=$row+1;$temp<=$dimension;$temp++){
if(($temp != $row) && (abs($matrix_local[$temp][$row]) != 0)){
$multiplier = $matrix_local[$temp][$row];
$matrix_L[$temp][$row] = $multiplier;
for($col=$row;$col<=$dimension;$col++){
$reduced = $matrix_local[$temp][$col] - $matrix_local[$row][$col]*$multiplier;
$matrix_local[$temp][$col] = $reduced;
}
}
}
}
my #y_intermediate;
for($col=1;$col<=$dimension;$col++){$y_intermediate[1][$col] = $matrix_B[1][$col]/$matrix_L[1][1]}
for($col=1;$col<=$dimension;$col++){
for($row=2;$row<=$dimension;$row++){
$y_intermediate[$row][$col] = $matrix_B[$row][$col];
for($tt=1;$tt<=($row-1);$tt++){$y_intermediate[$row][$col] -= ($matrix_L[$row][$tt]*$y_intermediate[$tt][$col])}
$y_intermediate[$row][$col] /= $matrix_L[$row][$row];
}
}
for($col=1;$col<=$dimension;$col++){$matrix_inverse[$dimension][$col] = $y_intermediate[$dimension][$col]/$matrix_local[$dimension][$dimension]}
for($col=1;$col<=$dimension;$col++){
for($row=($dimension-1);$row>=1;$row--){
$matrix_inverse[$row][$col] = $y_intermediate[$row][$col];
for($tt=($row+1);$tt<=$dimension;$tt++){$matrix_inverse[$row][$col] -= ($matrix_local[$row][$tt]*$matrix_inverse[$tt][$col])}
$matrix_inverse[$row][$col] /= $matrix_local[$row][$row];
}
}
return(#matrix_inverse);
}
Use Benchmark to do benchmarks :-)
Maybe the problem not in your program but in your measurment?
Use Devel::NYTProf for debug. It will show you a lot of useful info, timings, etc.