Skip to main content

Querying GitHub Issues from Google App Script

I'm currently exploring different integrations that can be done between Google App Script and Git. This is part of a bigger attempt to integrate Git data into my project management system. I'll post more on that later. GitHub supports a number of very nice sub-systems. One of them is the GitHub Issues ticket tracking system. It's fairly robust and comes with GitHub - which means it's highly integrated out-of-the-box. Integration with Google's App Script is done via the fetchURL command and JSON object manipulation.  After a good bit of experimentation, I've settled on querying GitHub Issues with an "on open spreadsheet event" to avoid issues with Google's quota system. Essentially, Google prevents you from calling the fetchURL command more than X times (see the quota limits ) per day. That's not great if you have people actively working and changing data in a spreadsheet. Some of my other App Script routines are running thousands of times per d...

Holidays

Anyone else out there ever run into the need for a simple little list of holidays? I do a fair amount of stuff that involves questions like "Are we off on such and such a day?" where you have to figure out, "When is Easter this year anyway?" That's pretty easy if you look it up on a calendar. Now try doing that in code.

At any rate, I wrote a tiny little class to provide some basic holidays out. Help yourself to the code, it's not complicated, just annoying. The biggest trick is finding the information in the first place. This is US-oriented, but I'd be glad to extended it if someone wants some other days in there. It would be helpful if you could provide the holiday information you're interested in though (i.e., My Personal Holiday is the 3rd Tuesday of May or whatever). Oh, one last caveat. If the date doesn't appear (like Inauguration day), the DateTime will be a 0. That's fine for comparisons, but if you just do a dump of the dates, it gives you 1899.



unit holidayDateTime;

interface

uses sysutils;

type
THolidayClass = class
public
YearIs : word;
NewYears : TDateTime;
MLKJr : TDateTime;
Inauguration : TDateTime;
Washington : TDateTime;
ArmedForces : TDateTime;
Memorial : TDateTime;
FlagDay : TDateTime;
Independence : TDateTime;
Labor : TDateTime;
Columbus : TDateTime;
Veterans : TDateTime;
Thanksgiving : TDateTime;
Christmas : TDateTime;
Secretaries : TDateTime;
AprilFools : TDateTime;
Earth : TDateTime;
Fathers : TDateTime;
Groundhog : TDateTime;
Halloween : TDateTime;
Lincoln : TDateTime;
Mothers : TDateTime;
StPatty : TDateTime;
UnitedNations: TDateTime;
Valentines : TDateTime;
Election : TDateTime;
Flag : TDateTime;
Easter : TDateTime;
constructor Create(aYear:word);
procedure changeYear(aYear:word);
function getEaster:TDateTime;
class procedure SafeMonth(var month, y:word);
class function getDOW(year, month, dow, weekNum:word):TDateTime;
class function getLastDOW(year, month, dow:word):TDateTime;
end;

implementation

class procedure THolidayClass.SafeMonth(var month, y:word);
begin
while month>12 do
begin
dec(month,12);
inc(y);
end;
end;

class function THolidayClass.getDOW(year, month, dow, weekNum:word):TDateTime;
var i:word;
begin
result:=EncodeDate(year, month, 1);
for i:=0 to 7 do
if DayOfWeek(result+i)=dow then
begin
result:=result+i;
result:=result+((weeknum-1)*7); //move to the correct week, 1st week is inc of 0
break;
end;
end;

class function THolidayClass.getLastdow(year, month, dow:word):TDateTime;
var i:word;
begin
//Year has to be on the stack, SafeMonth could change year
inc(month);
SafeMonth(month,year);
result:=EncodeDate(year,month,1)-1;
for i:=0 to 7 do
if DayOfWeek(result-i)=dow then
begin
result:=result-i;
break;
end;
end;

constructor THolidayClass.Create(aYear:word);
begin
ChangeYear(aYear);
end;

function THolidayClass.getEaster:TDateTime;
var a,b,c,d,e,f,g,h,i,k,l,m,p : integer;
Easter_Month, Easter_Day : integer;
begin
a:=yearis mod 19;
b:=yearis div 100;
c:=yearis mod 100;
d:=b div 4;
e:=b mod 4;
f:=(b+8) div 25;
g:=(b-f+1) div 3;
h:=(19*a+b-d-g+15) mod 30;
i:=c div 4;
k:=c mod 4;
l:=(32+2*e+2*i-h-k) mod 7;
m:=(a+11*h+22*l) div 451;
Easter_Month :=(h+l-7*m+114) div 31; //[3=March, 4=April]
p:=(h+l-7*m+114) mod 31;
Easter_Day:=p+1; //(day in Easter Month)
result:=EncodeDate(YearIs,Easter_Month,Easter_Day);
end;


procedure THolidayClass.ChangeYear(aYear:word);
const
DayOfWeek_Sunday = 1;
DayOfWeek_Monday = 2;
DayOfWeek_Tuesday = 3;
DayOfWeek_Wednesday = 4;
DayOfWeek_Thursday = 5;
DayOfWeek_Friday = 6;
DayOfWeek_Saturday = 7;

begin
YearIs := aYear;
Easter := getEaster;
NewYears := EncodeDate(YearIs,1,1);
MLKJr := getdow(YearIs, 1, DayOfWeek_Monday, 3);
if (YearIs - 1937) mod 4 = 0 then
Inauguration := EncodeDate(YearIs, 1, 20)
else
Inauguration := 0;
Election := 0;
Flag := EncodeDate(YearIs,6,14);
Washington := getdow(YearIs, 2, DayOfWeek_Monday, 3);
ArmedForces := getdow(YearIs, 5, DayOfWeek_Saturday, 3);
Memorial := getLastdow(YearIs, 5, DayOfWeek_Monday);
FlagDay := EncodeDate(YearIs,6,14);
Independence := EncodeDate(YearIs,7,4);
Labor := getdow(YearIs, 9, DayOfWeek_Monday,1);
Columbus := getdow(YearIs, 10, DayOfWeek_Monday,2);
Veterans := EncodeDate(YearIs,11,11);
Thanksgiving := getdow(YearIs, 11, DayOfWeek_Thursday,4);
Christmas := EncodeDate(YearIs,12,25);
Secretaries := getLastdow(YearIs, 5, DayOfWeek_Saturday) - 3; //wednesday before last saturday in april
AprilFools := EncodeDate(YearIs,4,1);

Earth := EncodeDate(YearIs,4,22);
Fathers := getdow(YearIs, 6, DayOfWeek_Sunday,3);
Groundhog := EncodeDate(YearIs,2,2);
Halloween := EncodeDate(YearIs,10,31);
Lincoln := EncodeDate(YearIs,2,12);
Mothers := getdow(YearIs, 5, DayOfWeek_Sunday,2);
StPatty := EncodeDate(YearIs,3,17);
UnitedNations:= EncodeDate(YearIs,10,24);
Valentines := EncodeDate(YearIs,2,14);
end;

end.

Comments

Anonymous said…
You use "When is Easter..." as an example in your post, but don't include it in the code (unless I missed something). Funny.
Marshall Fryman said…
Whoops. Sorry about that. Easter is one of those funny days that doesn't follow a nice pattern. I've updated the post to include it.
Anonymous said…
I've been looking over the code you have for calculating easter it seems incorrect

please refer to these sources of info

http://www.smart.net/~mmontes/freq3.html#LBY

2001 4/15
2002 3/31
2003 4/20
2004 4/11
2005 3/27
2006 4/16
2007 4/8
2008 3/23
2009 4/12
2010 4/4

and wikipedia

http://en.wikipedia.org/wiki/Easter

or the following code from about.com which seems to calculate it correctly

http://delphi.about.com/cs/adptips2002/a/bltip0302_2.htm

Frank
Marshall Fryman said…
Frank -

The Easter code is a direct translation of Samuel Butcher's Easter algorithm from 1876. I think the original I took was from Peter Duffett-Smith's Practical Astronomy With Your Calculator, but I'm not 100% sure. As I mentioned, I've misplaced the source for some of these calculations. At any rate, I used the following code:

procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
THC:THolidayClass;
begin
THC:=THolidayClass.Create(2000);
for i := 2000 to 2024 do
begin
THC.changeYear(i);
ListBox1.Items.add(FormatDateTime('m/d/y', THC.Easter));
end;
THC.Free;
end;

to test it and derived the same dates as your references.

Also, I've updated the post slight to fix the typos in my Easter add-on. One of the hazards of adding code directly into the HTML and formatting it instead of copying it from the source file.

I looked at Zarko's post and wasn't able to determine which algorithm he was using but it appears to generate the same dates as mine.

Could you provide a few sample dates that don't match or did I misinterpret what you were saying?

m

Popular posts from this blog

SMTP Mail and Indy (again)

Having spent a lot of time recently working on a ping scanner using Indy, I noticed that there's a lot of questions still on using SMTP with Indy. Let me first say that I am not an Indy expert. I get along with it successfully but find I still have to research things frequently. With the disclaimer out of the way, we can get to the offering. A while back I wrote a handy little unit to simply send a mail message with or without attachments and with or without providing authentication. It even has support for OpenSSL. I use this unit in a number of applications and have seen it successfully send hundreds of e-mails without issue. I recently added support for threaded message sending to take advantage of the multi-core system I'm now running on. This code has had a few additions (like the logger) that I've gleaned over time from various newsgroup postings, but I didn't record the authors so let me credit the anonymous Internet authors who support Indy. It's really amaz...

Detecting a virtualized environment

CubicDesign on delphi-talk.elists.org recently asked the question: "How do I know/detect if my software is running under Windows [or a virtual environment]?" Well, it turns out that it's a lot harder to tell than you would think. Apparently, the VM (VMware, Xen, Wine, etc.) doesn't really want you to be able to do this. At least not easily. For VMware, there is a decent little C routine called jerry.c that does the trick. Jerry actually uses a simple communication channel that VMware left open. It's not 100% foolproof since the admin can change the channel, but that's not likely going to happen unless the system is specifically designed to be a honeypot. If you're running on a honeypot and still have a legitimate reason for detection, you could look at the much more complex scoopy implementation which inspects how the system is actually virtualized using the SIDT CPU instruction instead of a communication channel. Another reference (red pill) is here . F...

Delphi Case with Strings

Zarko Gajic posted about this topic on his Delphi Tips recently showing how one could use a case statement with strings. His solution basically passes in an array on the stack and then iterates through it providing the index number of the matching string. I don't often want to do this, but the idea comes up occassionally enough that I thought I'd play with it a little. The first thing that struck me with this is that passing things on the stack is bound to be slow. Any time you can avoid memory allocation in your routines, do it. The way Zarko wrote his StringToCaseSelect routine created a COPY of the information on the stack. In my testing, just changing the CaseList: array of string to CaseList:const array of string improved the performance of the code by almost 30% for his example. Mind, I'm not using hyper-precise counters; however, it definitely makes a difference. Secondly, I was curious how the performance changed if I used the old stand-by: If then else. Using the ...

Copyright 2008-2022, Marshall Fryman