Forget-Me-Not/helpers/datetime.nim

122 lines
5.4 KiB
Nim
Raw Normal View History

2025-05-17 09:02:52 -05:00
#[Copyright 2025 ITwrx.
This file is part of Forget-Me-Not.
Forget-Me-Not is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#
2025-05-15 08:01:35 -05:00
import std/[times, strutils]
proc weekdayFromString(dayStr: string): Weekday =
## Convert a string representation of a weekday to Weekday enum
case dayStr
of "Monday": return dMon
of "Tuesday": return dTue
of "Wednesday": return dWed
of "Thursday": return dThu
of "Friday": return dFri
of "Saturday": return dSat
of "Sunday": return dSun
proc monthFromString(monthStr: string): Month =
## Convert a string representation of a weekday to Weekday enum
case monthStr
of "January": return mJan
of "Febuary": return mFeb
of "March": return mMar
of "April": return mApr
of "May": return mMay
of "June": return mJun
of "July": return mJul
of "August": return mAug
of "September": return mSep
of "October": return mOct
of "November": return mNov
of "December": return mDec
proc nextWeekday*(targetWeekdayString: string): DateTime =
## Calculates the next occurrence of a specific weekday
var nextDate = now()
# We're not including current day, so we move to the next day
nextDate = nextDate + 1.days
#convert passed weekday string to nim Weekday.
let targetWeekday = weekdayFromString(targetWeekdayString)
# Find the next occurrence of the target weekday
while nextDate.weekday != targetWeekday:
nextDate = nextDate + 1.days
return nextDate
proc nthWeekdayInMonth*(year: int, month: Month, weekdayString: string, nth: range[1..3]): DateTime =
# Start from the first day of the month
var currentDate = dateTime(year, month, 1)
let weekday = weekdayFromString(weekdayString)
# Find the first occurrence of the specified weekday
while currentDate.weekday != weekday:
currentDate = currentDate + 1.days
# Move to the nth occurrence
currentDate = currentDate + days((nth - 1) * 7)
return currentDate
proc lastWeekdayInMonth(year: int, month: Month, weekdayString: string): DateTime =
# Create a DateTime for the last day of the given month
var lastDay = dateTime(year, month, getDaysInMonth(month, year))
let weekday = weekdayFromString(weekdayString)
# Work backwards until we find the last occurrence of the specified weekday
while lastDay.weekday != weekday:
lastDay = lastDay - 1.days
return lastDay
proc nextMonthlyOnWeekdayOfWeek*(weekdayString: string, ocurrence: string): DateTime =
var nextSendDate: DateTime
let nextMonthsDate = now() + 1.months
let weekNumStrings = @["1", "2", "3"]
if ocurrence in weekNumStrings:
nextSendDate = nthWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString, parseInt(ocurrence))
#when ocurrence == "last".
else:
nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString)
#only use next month if that day has already occured this month, otherwise adjust it for this month instead.
if monthDay(nextSendDate - 1.months) > monthDay(now()):
if ocurrence in weekNumStrings:
nextSendDate = nthWeekdayInMonth(year(nextMonthsDate), month(now()), weekdayString, parseInt(ocurrence))
else:
nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(now()), weekdayString)
return nextSendDate
proc nextYearlyOnWeekdayOfWeekOfMonth*(weekdayString, ocurrence, monthString: string): DateTime =
var nextSendDate, startingDate: DateTime
let nextYearsDate = now() + 1.years
#let startingDate = dateTime(year(nextYearsDate), monthFromString(monthString), 01, 0, 0, 0, 0)
startingDate = dateTime(year(nextYearsDate), monthFromString(monthString), 01)
let weekNumStrings = @["1", "2", "3"]
if ocurrence in weekNumStrings:
nextSendDate = nthWeekdayInMonth(year(startingDate), month(startingDate), weekdayString, parseInt(ocurrence))
#when ocurrence == "last".
else:
nextSendDate = lastWeekdayInMonth(year(startingDate), month(startingDate), weekdayString)
#only use next year if that month and day has already occured this year, otherwise adjust it for this year instead.
if nextSendDate - 1.years > now():
startingDate = dateTime(year(now()), monthFromString(monthString), 01)
let weekNumStrings = @["1", "2", "3"]
if ocurrence in weekNumStrings:
nextSendDate = nthWeekdayInMonth(year(startingDate), month(startingDate), weekdayString, parseInt(ocurrence))
#when ocurrence == "last".
else:
nextSendDate = lastWeekdayInMonth(year(startingDate), month(startingDate), weekdayString)
return nextSendDate
2025-05-17 09:02:52 -05:00
#[ Returns the next occurrence of the specified month and day, which could be:
- Later this year if the target date hasn't occurred yet
- Next year if the target date has already passed this year]#
proc nextYearlyDate*(monthString: string, targetDay: int): DateTime =
2025-05-15 08:01:35 -05:00
let baseDate = now()
2025-05-17 09:02:52 -05:00
let targetMonth = monthFromString(monthString)
2025-05-15 08:01:35 -05:00
# Get the current year
2025-05-17 09:02:52 -05:00
let currentYear = baseDate.year
2025-05-15 08:01:35 -05:00
# Create a DateTime for the target date in the current year
2025-05-17 09:02:52 -05:00
var nextSendDate = dateTime(currentYear, targetMonth, targetDay, 00, 00, 00, 00)
2025-05-15 08:01:35 -05:00
# If the target date has already passed this year, move to next year
if nextSendDate <= baseDate:
2025-05-17 09:02:52 -05:00
nextSendDate = dateTime(currentYear + 1, targetMonth, targetDay, 00, 00, 00, 00)
2025-05-15 08:01:35 -05:00
let nextSendDateString = format(nextSendDate, "yyyy-MM-dd")
2025-05-17 09:02:52 -05:00
nextSendDate = parse(nextSendDateString, "yyyy-MM-dd")
2025-05-15 08:01:35 -05:00
return nextSendDate