Anyone expand on this MonthCal with bold dates? AHK v1 Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 23 May 2023, 16:37

A long time ago I stumbled across this MonthCal script that bolds certain days.
Spoiler
It's PhiLho's code from here:
https://www.autohotkey.com/board/topic/34150-possible-to-color-days-in-monthcal/

Do you guys know if other folks have made different versions of this, or expanded on it? It seems like it would be clever to have common US holidays bolded. Tidbit gives us some excellent code in his isHoliday() function here:
viewtopic.php?t=77312

If there are any ambitious gurus who are bored, please consider a mashup between these scripts. If anyone does take this on, please make the MonthCal capable of showing bolded dates for "multi-month" e.g. r3 views. I was not even able to get PhiLho's code to work with three rows/month showing.
ste(phen|ve) kunkel

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

MonthCal with bold holidays -- is flickering...

Post by kunkel321 » 30 May 2023, 16:51

If anyone tries to run this, just hold down the Esc key to kill it.

I'm trying to combine PhiLho's calendar with Tidbit's holiday function. In the code below, I've indicated which are the parts that I added. DISCLAIMER: I don't understand much of the code in the calendar. Even with that being the case, I was able to use the loop, 31 to get all of holiday dates onto the %days% variable, and the calendar WAS correctly displaying them. The problem was that, when I browsed to another month, it wasn't updating the holidays (was just bolding the holidays of the current month). So I added the gosub, top near the bottom of the calendar. That of course causes the calendar to redraw.

I need the loop, 31 to redefine %days% and update the calendar but NOT redraw it with the default month. Also, there is a problem that (I think) the calendar immediately follows the gBoldify label, not just when I browse to a different month. I believe those two things are what is causing the feedback loop.

Any thoughts on how to get this to cooperate?

Code: Select all

#NoEnv ; For security
#SingleInstance force
#Persistent
; For AHK v1
;  ##### WARNING feedback loop.  Press esc to kill. 
Top: ; Kludgy part that Steve added. =================
If (month = "")
   month := A_Mon

loop, 31
{
day:=SubStr("0" . A_Index, -1)
date := A_Year . month . day . "000000"
isHoli := isHoliday(date)
if (isHoli != "")
	days := days . "," . A_Index
} ; end of Steve's kludy part ===============


; Calendar code by PhiLho
;https://www.autohotkey.com/board/topic/34150-possible-to-color-days-in-monthcal/
monthNb := 3  ; How many months are displayed. Can be fetched with MCM_GETMONTHRANGE
MCM_FIRST := 0x1000
MCM_SETDAYSTATE := MCM_FIRST + 8

; MCS_DAYSTATE = 1
Gui, destroy
Gui +LastFound -minimizeBox +Toolwindow
Gui, Add, MonthCal, +0x1 vMyCalendar gBoldify
Gui Show

Gui +LastFound
guiID := WinExist()
ControlGet mcID, Hwnd, , SysMonthCal321, ahk_id %guiID%

Gosub Boldify
Return

Boldify:
VarSetCapacity(daysBuffer, 4 * monthNb, 0)

; PhiLho had %days% assigned here, but Steve assigns it up at the top.

addr := &daysBuffer + 4
Loop Parse, days, `,
{
   o1 := (A_LoopField - 1) / 8
   o2 := Mod(A_LoopField - 1, 8)
   val := *(addr + o1) | (1 << o2)
   DllCall("RtlFillMemory", "UInt", addr + o1, "UInt", 1, "UChar", val)
}
Gosub SetDayState

month := SubStr(MyCalendar, 5, 2) ; Steve added this part.  Is causting a feedback loop?? =========================
;MsgBox % month
Gosub, top ; Steve added this part.  Is causting a feedback loop?? =========================

Return

SetDayState:
   SendMessage MCM_SETDAYSTATE, monthNb, &daysBuffer, , ahk_id %mcID%
Return

GuiClose:
GuiEscape:
ExitApp

; Name: isHoliday; Author: tidbit; Created: Thursday, June 11, 2020
isHoliday(yyyymmddhhmiss:="", businessOnly:=0, stopAtFirst:=0)
{
	tstamp:=(yyyymmddhhmiss="") ? A_Now : yyyymmddhhmiss

	; not a valid timestamp
	if (strLen(yyyymmddhhmiss)!=14)
		return -1
	if yyyymmddhhmiss is not number
		return -2

	date={}
	out:="" ; return a string of all possible events today

	; grab more data than needed. safety first.
	formatTime, ttt, %tstamp%, yyyy|MM|dd|MMMM|dddd
	date:=strSplit(ttt, "|")
	formatTime, ttt, %tstamp%, YWeek
	date.push(substr(ttt, 5))
	formatTime, ttt, %tstamp%, YDay
	date.push(ttt)

	date:={year: date[1], mon: date[2], day: date[3], monN: date[4], dayN: date[5], dayY: date[7], weekY: date[6]}

	; Leap-year
	isLeap:=0
	if ((mod(date.year, 4)=0 && mod(date.year, 100)!=0) || mod(date.year, 400)=0)
		isLeap:=1

	; Easter... Amazing. Thank you, "Nature" Journal - 1876
	a:=mod(date.year, 19), b:=floor(date.year/100), c:=mod(date.year, 100)
	d:=floor(b/4), e:=mod(b, 4), f:=floor((b+8)/25)
	g:=floor((b-f+1)/3), h:=mod(((19*a)+b-d-g+15), 30), i:=floor(c/4),
	k:=mod(c, 4), l:=mod((32+(2*e)+(2*i)-h-k), 7), m:=floor((a+(11*h)+(22*L))/451)
	emonth:=format("{:02}", (floor((h+l+(7*m)+114)/31)))
	eday:=format("{:02}", mod((h+l-(7*m)+114), 31)+1)

	; single space delimited, strictly
	; ["month day-day dayName", "Day Text", federal/business day]
	; if "dayName" = "absolute", "month" becomes "isLeapYear", and "day-day" is a number between 1-366
	dates:=[["01 01", "New Year's Day", 1]
	, ["01 15-21 Monday", "Martin Luther King Jr. Day", 1]
	, ["02 15-21 Monday", "Presidents Day", 1]
	, [ emonth " " eday, "Easter Sunday", 0]
	, ["05 25-31 Monday", "Memorial Day", 1]
	, ["07 04", "Fourth of July", 1]
	, ["09 01-07 Monday", "Labor Day", 1]
	, ["11 11", "Veterans Day", 1]
	, ["11 19", "Juneteenth", 1]
	, ["11 22-28 Thursday", "Thanksgiving Day", 1]
	, ["12 25", "Christmas Day", 1]
	, ["02 14", "Valentines Day", 0]
	, ["05 08-14 Sunday", "Mother's Day", 0]
	, ["06 15-21 Sunday", "Father's Day", 0]
	, ["03 17", "St. Patrick's Day", 0]
	, ["08 31", "Halloween", 0]
	, ["03 14", "Pi Day", 0]
	, ["0 182 absolute", "50% through the year", 0]
	, ["1 183 absolute", "50% through the year", 0]]

	stop:=0
	for k, day in dates
	{
		if (day[3]=0 && businessOnly=1)
			continue

		holiday:=day[2] ; give it a nicer name
		stamp:=strSplit(day[1], " ")
		range:=strSplit(stamp[2], "-")
		range[2]:=(range[2]="") ? range[1] : range[2]

		if (stamp[3]!="absolute")
		{
			; set a temp var to blank if a weekday wasn't specified.
			; Otherwise check if the specified day is today
			ttt:=(stamp[3]="") ? "" : date.dayN
			isBetween:=(date.day>=range[1] && date.day<=range[2]) ? 1 : 0
			if (date.mon=stamp[1] && isBetween=1 && ttt=stamp[3])
				out.=holiday "`n", stop:=1
		}
		else
		{
			if (isLeap=stamp[1] && date.dayY=stamp[2])
				out.=holiday "`n", stop:=1
		}

		if (stopAtFirst=1 && stop=1) ; sometimes you'll want to be a nanosecond faster.
			return trim(out, "`r`n `t")
	}
	return trim(out, "`r`n `t")
}
ste(phen|ve) kunkel

just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Anyone expand on this MonthCal with bold dates? AHK v1  Topic is solved

Post by just me » 31 May 2023, 06:11

Code: Select all

#NoEnv ; For security
#SingleInstance force
#Persistent
; Calendar code by PhiLho
;https://www.autohotkey.com/board/topic/34150-possible-to-color-days-in-monthcal/
; MCS_DAYSTATE = 1
OnMessage(0x4E, "WM_NOTIFY")
Gui, -MinimizeBox +LastFound +Toolwindow
Gui, Add, MonthCal, w-3 +0x01 vMyCalendar
Gui, Show
Return
GuiClose:
GuiEscape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
; Process the MCN_GETDAYSTATE notification
; https://learn.microsoft.com/en-us/windows/win32/controls/mcn-getdaystate
; The first notification is sent while the GuiControl is created.
; ----------------------------------------------------------------------------------------------------------------------
WM_NOTIFY(W, L, M, H) {
   Static OffHwnd := 0
        , OffCode := OffHwnd + (A_PtrSize * 2)
        , OffYear := OffCode + A_PtrSize
        , OffMonth := OffYear + 2
        , OffCount := OffMonth + 14
        , OffArray := OffCount + A_PtrSize
   If (NumGet(L + OffCode, "Int") = -747) { ; MCN_GETDAYSTATE
      Year := NumGet(L + OffYear, "UShort")
      Month := NumGet(L + OffMonth, "UShort")
      MonthCount := NumGet(L + OffCount, "Int")
      Addr := NumGet(L + OffArray, "UPtr")
      CurrentDate := Format("{:}{:02}01000000", Year, Month)
      Loop, %MonthCount% {
         LastDay := LDOM(CurrentDate)
         BoldDays := 0
         Loop, %LastDay% {
            I := A_Index - 1
            If (isHoliday(CurrentDate) <> "") {
               BoldDays |= 1 << I
            }
            CurrentDate += 1, D
         }
         NumPut(BoldDays, Addr + 0, "UInt")
         Addr += 4
         CurrentDate := SubStr(CurrentDate, 1, 6) . "01000000"
      }
      Return 1
   }
}
; ----------------------------------------------------------------------------------------------------------------------
LDOM(Date) { ; get the number of last day of the month
   Date += 31, D
   Date := SubStr(Date, 1, 6)
   Date += -1, D
   Return (SubStr(Date, 7, 2) + 0)
}
; ----------------------------------------------------------------------------------------------------------------------
; Name: isHoliday; Author: tidbit; Created: Thursday, June 11, 2020
isHoliday(yyyymmddhhmiss:="", businessOnly:=0, stopAtFirst:=0)
{
	tstamp:=(yyyymmddhhmiss="") ? A_Now : yyyymmddhhmiss

	; not a valid timestamp
	if (strLen(yyyymmddhhmiss)!=14)
		return -1
	if yyyymmddhhmiss is not number
		return -2

	date={}
	out:="" ; return a string of all possible events today

	; grab more data than needed. safety first.
	formatTime, ttt, %tstamp%, yyyy|MM|dd|MMMM|dddd
	date:=strSplit(ttt, "|")
	formatTime, ttt, %tstamp%, YWeek
	date.push(substr(ttt, 5))
	formatTime, ttt, %tstamp%, YDay
	date.push(ttt)

	date:={year: date[1], mon: date[2], day: date[3], monN: date[4], dayN: date[5], dayY: date[7], weekY: date[6]}

	; Leap-year
	isLeap:=0
	if ((mod(date.year, 4)=0 && mod(date.year, 100)!=0) || mod(date.year, 400)=0)
		isLeap:=1

	; Easter... Amazing. Thank you, "Nature" Journal - 1876
	a:=mod(date.year, 19), b:=floor(date.year/100), c:=mod(date.year, 100)
	d:=floor(b/4), e:=mod(b, 4), f:=floor((b+8)/25)
	g:=floor((b-f+1)/3), h:=mod(((19*a)+b-d-g+15), 30), i:=floor(c/4),
	k:=mod(c, 4), l:=mod((32+(2*e)+(2*i)-h-k), 7), m:=floor((a+(11*h)+(22*L))/451)
	emonth:=format("{:02}", (floor((h+l+(7*m)+114)/31)))
	eday:=format("{:02}", mod((h+l-(7*m)+114), 31)+1)

	; single space delimited, strictly
	; ["month day-day dayName", "Day Text", federal/business day]
	; if "dayName" = "absolute", "month" becomes "isLeapYear", and "day-day" is a number between 1-366
	dates:=[["01 01", "New Year's Day", 1]
	, ["01 15-21 Monday", "Martin Luther King Jr. Day", 1]
	, ["02 15-21 Monday", "Presidents Day", 1]
	, [ emonth " " eday, "Easter Sunday", 0]
	, ["05 25-31 Monday", "Memorial Day", 1]
	, ["07 04", "Fourth of July", 1]
	, ["09 01-07 Monday", "Labor Day", 1]
	, ["11 11", "Veterans Day", 1]
	, ["11 19", "Juneteenth", 1]
	, ["11 22-28 Thursday", "Thanksgiving Day", 1]
	, ["12 25", "Christmas Day", 1]
	, ["02 14", "Valentines Day", 0]
	, ["05 08-14 Sunday", "Mother's Day", 0]
	, ["06 15-21 Sunday", "Father's Day", 0]
	, ["03 17", "St. Patrick's Day", 0]
	, ["08 31", "Halloween", 0]
	, ["03 14", "Pi Day", 0]
	, ["0 182 absolute", "50% through the year", 0]
	, ["1 183 absolute", "50% through the year", 0]]

	stop:=0
	for k, day in dates
	{
		if (day[3]=0 && businessOnly=1)
			continue

		holiday:=day[2] ; give it a nicer name
		stamp:=strSplit(day[1], " ")
		range:=strSplit(stamp[2], "-")
		range[2]:=(range[2]="") ? range[1] : range[2]

		if (stamp[3]!="absolute")
		{
			; set a temp var to blank if a weekday wasn't specified.
			; Otherwise check if the specified day is today
			ttt:=(stamp[3]="") ? "" : date.dayN
			isBetween:=(date.day>=range[1] && date.day<=range[2]) ? 1 : 0
			if (date.mon=stamp[1] && isBetween=1 && ttt=stamp[3])
				out.=holiday "`n", stop:=1
		}
		else
		{
			if (isLeap=stamp[1] && date.dayY=stamp[2])
				out.=holiday "`n", stop:=1
		}

		if (stopAtFirst=1 && stop=1) ; sometimes you'll want to be a nanosecond faster.
			return trim(out, "`r`n `t")
	}
	return trim(out, "`r`n `t")
}
?

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 31 May 2023, 08:35

Thanks so much Just Me! This is excellent. I'll have to spend some more time studying what your code does. :D

On a side note: I just realized that Tidbit had a typo in Halloween's date... Should be "10 31" rather than "8 31". I'll comment on his thread too.

EDIT: I should also point out that I removed a couple of holidays from Tidbit's list, including Columbus Day. I replaced Columbus Day with Juneteenth (June 19), but I just now realized that I have a type there also... Should be "06 19" not "11 19".
ste(phen|ve) kunkel

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 02 Jun 2023, 17:22

Quick question for Just Me (or anyone) ...
In your(his) code is this:

Code: Select all

         Loop, %LastDay% {
            I := A_Index - 1
            If (isHoliday(CurrentDate) <> "") {
               BoldDays |= 1 << I
            }
            CurrentDate += 1, D
What does the pipe character in BoldDays |= 1 << I do? I assume that it is similar to .= , which is like var := var . var2. I don't see it in the help docs though. For that matter... What is there a 1 there?

Also, I know that I := A_Index - 1 but what is the << doing??

NEXT DAY EDIT: Actually... Nevermind. I peeked at the BoldDays variable, and it is (seemingly) random numbers. So the answers to the above questions probably wouldn't mean anything to me anyway, until I learn more about those Windows constants (or whatever they are.) :crazy:
ste(phen|ve) kunkel

just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by just me » 04 Jun 2023, 05:22

Hi @kunkel321,

it's the mystery of the MONTHDAYSTATE structure.

It's a 32-bit value. Bits 0 to 30 (counting starts at 0) represent the days 1 to 31. Bit 0 is the rightmost bit in 'human' oder. If the day shall be displayed in bold you have to set the associated bit to 1. That's what the code does.
For day 1 we set bit 0 of an integer value to 1 and leave it at this position (respectively shift it to the left << by 0 positions.): A_Index = 1 -> I = A_Index - 1 -> 0
For day 2 we set bit 0 of an integer value to 1 and shift it one position to the left: A_Index = 2 -> I = A_Index - 1 -> 1.
And so on ...

BoldDays |= 1 << I performs a :arrow: bitwise-or (|), that is, bits set in the value on the right side of the assignment are set in the value of the left side.

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 04 Jun 2023, 10:04

Thanks for the run-down. So the numbers must all be 0 to 30, but in binary I guess?? That's not right though... Or they would be 1s and 0s... I know that sometimes computers increment numbers by doubling them: 2,4,8, 16,32 ... They don't follow that pattern either though...
EDIT: Un-embeded the below image because it was hurting my eyes... :sick:
https://i.imgur.com/MHEpYK0.gif
ste(phen|ve) kunkel

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 04 Jun 2023, 16:18

Okay, here's a nice addition to the code...
While the calendar is showing, press h to get a list of the names of the currently-bolded dates. I call it "DatePicker - H."
:geek:
Spoiler
To get it to work, Just Me, I declared a few of your variables as Global, and borrowed them... Didn't seem to break the script(?) FYI you can change the number of months shown (line 18) and the correct holidays will (should) be shown.

FYI if anyone uses this, it's worth noting that the Juneteenth holiday follows a different algorithm than other holidays. And Tidbit's IsHoliday function doesn't have it, because it was made before the adoption of Juneteenth as a federal holiday. Juneteenth is June 16, but if it falls on a weekend, you celebrate it on Friday -or- Monday, whichever is closest.
ste(phen|ve) kunkel

just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by just me » 05 Jun 2023, 02:52

@kunkel321,
the value doesn't matter, just the binary presentation. If you want to display days 1, 16, and 23 as bold you have to set bits 0, 15, and 22:

Code: Select all

31                              0
 00000000001000000100000000000001
The numeric value would be 2^0 = 1 + 2^15 = 32768 + 2^22 = 4194304 = 4227073 in this case.

If you don't like bit-shifting, you also can calculate the value as follows:

Code: Select all

BoldDays := [1, 16, 23]
MonthDayStateValue := 0
For Each, Day In BoldDays
   MonthDayStateValue += 2**(Day - 1)
MsgBox, %MonthDayStateValue%
VarSetCapacity(MONTHDAYSTATE, 4, 0) ; MONTHDAYSTATE structure
NumPut(MonthDayStateValue, MONTHDAYSTATE, "UInt")

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 05 Jun 2023, 07:49

Very cool -- Thanks for the information Just Me!!!
ste(phen|ve) kunkel

User avatar
kunkel321
Posts: 974
Joined: 30 Nov 2015, 21:19

Re: Anyone expand on this MonthCal with bold dates? AHK v1

Post by kunkel321 » 05 Jun 2023, 14:20

Here is a more functional version of "DatePicker - H" as described above. This one is more functional because it stays in RAM and you activate it with !^+d. The selected date gets typed into... Whatever. Press Esc while gui is shown to close it.

6-8-2023 got rid of an errant Gui, Show.

Code: Select all

#NoEnv ; For security
#SingleInstance force
#Persistent
; A simple popup calendar that has US Holidays in bold font.
; Original calendar-with-bolded-dates code by PhiLho
; https://www.autohotkey.com/board/topic/13441-monthcal-setdaystate/
; IsHoliday function by TidBit.
; https://www.autohotkey.com/boards/viewtopic.php?t=77312
; The two were integrated by Just Me.
; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=117399&p
; Then kunkel321 added Holiday names when pressing "h"
; Double-click a date, or press {Enter}, to type it.
; Calendar appears placed over active window.
; Waits for window to be active again before typing date.

!^+d:: ; Hotkey is Alt+Ctrl+Shift+D
;======== User Options ==========
PreferredFormat := "M-d-yyyy"
BorderColor := "Gray"
;================================
WinGetActiveTitle, myWinTarget
WinGetPos, X, Y, W, H, A
	X := X + (W * 0.30)
	Y := Y + (H * 0.04)
tics:=0 ; for below double-click code
global Year, Month, MonthCount
OnMessage(0x4E, "WM_NOTIFY")
Gui, Destroy
Gui, Color, %BorderColor%
Gui, -MinimizeBox  +LastFound +Toolwindow
Gui, Add, MonthCal, R3 +0x01  +AltSubmit vDatePicked gDblClick
Gui, Show, x%X% y%Y%, DatePicker - H
Return

GuiClose:
GuiEscape:
Gui, Hide
Return

#IfWinActive, DatePicker - H
Enter::
	gosub, SendDate
Return
; If you're editing this ahk file, and it is named "DatePicker - H",
; this will prevent you from typing 'h' and will drive you crazy.
h::
	gosub, Holidays
return
Esc::
	Gui, Destroy
return
#IfWinActive

DblClick:
Gui, submit, noHide
; Below several lines adapted from TidBit's 2015 Get A Date.
If (A_GuiControlEvent=1 && (A_TickCount-tics)<=DllCall("GetDoubleClickTime") )
	gosub, SendDate
Else If (A_GuiControlEvent=1)
	tics:=A_TickCount
Return

SendDate:
Gui, Submit
Gui, Destroy
FormatTime, MyDate, %DatePicked%, %PreferredFormat%
WinWaitActive, %myWinTarget%
SendInput, %MyDate%
Return

Holidays:
theseHolis =
loopNumber := (MonthCount - 2) * 31
Month := SubStr("0" . Month, strlen(Month), 2)
thisLoopDate := Year . Month . "01000000"
thisLoopDate += 30, D
loop, %loopNumber%
{
	HoliThisLoop := isHoliday(thisLoopDate)
	If (HoliThisLoop = "") || (HoliThisLoop = "-1") || (HoliThisLoop = " ")
	{
		EnvAdd, thisLoopDate, +1, days
		continue
	}
	FormatTime, thisLoopMD, %thisLoopDate%, MMM-dd
	thisHoli := isHoliday(thisLoopDate)
	theseHolis .= thisLoopMD . "`t" . thisHoli . "`n"
	thisLoopDate += 1, D
}
MsgBox Holidays currently displayed...`n`n%theseHolis%
return

; ------------------------------------------------------------------------------------
; Process the MCN_GETDAYSTATE notification
; https://learn.microsoft.com/en-us/windows/win32/controls/mcn-getdaystate
; The first notification is sent while the GuiControl is created.
; ------------------------------------------------------------------------------------
WM_NOTIFY(W, L, M, H) {

   Static OffHwnd := 0
        , OffCode := OffHwnd + (A_PtrSize * 2)
        , OffYear := OffCode + A_PtrSize
        , OffMonth := OffYear + 2
        , OffCount := OffMonth + 14
        , OffArray := OffCount + A_PtrSize
   If (NumGet(L + OffCode, "Int") = -747) { ; MCN_GETDAYSTATE
      Year := NumGet(L + OffYear, "UShort")
      Month := NumGet(L + OffMonth, "UShort")
      MonthCount := NumGet(L + OffCount, "Int")
      Addr := NumGet(L + OffArray, "UPtr")
      CurrentDate := Format("{:}{:02}01000000", Year, Month)
      Loop, %MonthCount% {
         LastDay := LDOM(CurrentDate)
         BoldDays := 0
         Loop, %LastDay% {
            I := A_Index - 1
            If (isHoliday(CurrentDate) <> "") {
               BoldDays |= 1 << I
            }
            CurrentDate += 1, D
         }
         NumPut(BoldDays, Addr + 0, "UInt")
         Addr += 4
         CurrentDate := SubStr(CurrentDate, 1, 6) . "01000000"
      }
      Return 1
   }
}
; ------------------------------------------------------------------------
LDOM(Date) { ; get the number of last day of the month
   Date += 31, D
   Date := SubStr(Date, 1, 6)
   Date += -1, D
   Return (SubStr(Date, 7, 2) + 0)
}
; ------------------------------------------------------------------------
; Name: isHoliday; Author: tidbit; Created: Thursday, June 11, 2020
isHoliday(yyyymmddhhmiss:="", businessOnly:=0, stopAtFirst:=0)
{
	tstamp:=(yyyymmddhhmiss="") ? A_Now : yyyymmddhhmiss

	; not a valid timestamp
	if (strLen(yyyymmddhhmiss)!=14)
		return -1
	if yyyymmddhhmiss is not number
		return -2

	date={}
	out:="" ; return a string of all possible events today

	; grab more data than needed. safety first.
	formatTime, ttt, %tstamp%, yyyy|MM|dd|MMMM|dddd
	date:=strSplit(ttt, "|")
	formatTime, ttt, %tstamp%, YWeek
	date.push(substr(ttt, 5))
	formatTime, ttt, %tstamp%, YDay
	date.push(ttt)

	date:={year: date[1], mon: date[2], day: date[3], monN: date[4], dayN: date[5], dayY: date[7], weekY: date[6]}

	; Leap-year
	isLeap:=0
	if ((mod(date.year, 4)=0 && mod(date.year, 100)!=0) || mod(date.year, 400)=0)
		isLeap:=1

	; Easter... Amazing. Thank you, "Nature" Journal - 1876
	a:=mod(date.year, 19), b:=floor(date.year/100), c:=mod(date.year, 100)
	d:=floor(b/4), e:=mod(b, 4), f:=floor((b+8)/25)
	g:=floor((b-f+1)/3), h:=mod(((19*a)+b-d-g+15), 30), i:=floor(c/4),
	k:=mod(c, 4), l:=mod((32+(2*e)+(2*i)-h-k), 7), m:=floor((a+(11*h)+(22*L))/451)
	emonth:=format("{:02}", (floor((h+l+(7*m)+114)/31)))
	eday:=format("{:02}", mod((h+l-(7*m)+114), 31)+1)

	; single space delimited, strictly
	; ["month day-day dayName", "Day Text", federal/business day]
	; if "dayName" = "absolute", "month" becomes "isLeapYear", and "day-day" is a number between 1-366
	dates:=[["01 01", "New Year's Day", 1]
	, ["01 15-21 Monday", "Martin Luther King Jr. Day", 1]
	, ["02 15-21 Monday", "Presidents Day", 1]
	, [ emonth " " eday, "Easter Sunday", 0]
	, ["05 25-31 Monday", "Memorial Day", 1]
	, ["07 04", "Fourth of July", 1]
	, ["09 01-07 Monday", "Labor Day", 1]
	, ["11 11", "Veterans Day", 1]
	, ["06 19", "Juneteenth", 1]
	, ["11 22-28 Thursday", "Thanksgiving Day", 1]
	, ["12 25", "Christmas Day", 1]
	, ["02 14", "Valentines Day", 0]
	, ["05 08-14 Sunday", "Mother's Day", 0]
	, ["06 15-21 Sunday", "Father's Day", 0]
	, ["03 17", "St. Patrick's Day", 0]
	, ["10 31", "Halloween", 0]
	, ["03 14", "Pi Day", 0]
	, ["0 182 absolute", "50% through the year", 0]
	, ["1 183 absolute", "50% through the year", 0]]

	stop:=0
	for k, day in dates
	{
		if (day[3]=0 && businessOnly=1)
			continue

		holiday:=day[2] ; give it a nicer name
		stamp:=strSplit(day[1], " ")
		range:=strSplit(stamp[2], "-")
		range[2]:=(range[2]="") ? range[1] : range[2]

		if (stamp[3]!="absolute")
		{
			; set a temp var to blank if a weekday wasn't specified.
			; Otherwise check if the specified day is today
			ttt:=(stamp[3]="") ? "" : date.dayN
			isBetween:=(date.day>=range[1] && date.day<=range[2]) ? 1 : 0
			if (date.mon=stamp[1] && isBetween=1 && ttt=stamp[3])
				out.=holiday "`n", stop:=1
		}
		else
		{
			if (isLeap=stamp[1] && date.dayY=stamp[2])
				out.=holiday "`n", stop:=1
		}

		if (stopAtFirst=1 && stop=1) ; sometimes you'll want to be a nanosecond faster.
			return trim(out, "`r`n `t")
	}
	return trim(out, "`r`n `t")
}
ste(phen|ve) kunkel

Post Reply

Return to “Ask for Help (v1)”