Forget-Me-Not/helpers/datetime.nim

114 lines
5.1 KiB
Nim

#[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.]#
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(now()), weekdayString, parseInt(ocurrence))
nextSendDate = nthWeekdayInMonth(year(now()), month(now()), weekdayString, parseInt(ocurrence))
if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
nextSendDate = nthWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString, parseInt(ocurrence))
else:
#nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(now()), weekdayString)
nextSendDate = lastWeekdayInMonth(year(now()), month(now()), weekdayString)
if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString)
return nextSendDate
proc nextYearlyOnWeekdayOfWeekOfMonth*(weekdayString, ocurrence, monthString: string): DateTime =
var nextSendDate, startingDate, nextYearsDate: DateTime
nextYearsDate = now() + 1.years
nextYearsDate = dateTime(year(nextYearsDate), monthFromString(monthString), 01)
let weekNumStrings = @["1", "2", "3"]
if ocurrence in weekNumStrings:
nextSendDate = nthWeekdayInMonth(year(now()), month(now()), weekdayString, parseInt(ocurrence))
if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
nextSendDate = nthWeekdayInMonth(year(nextYearsDate), month(nextYearsDate), weekdayString, parseInt(ocurrence))
else:
nextSendDate = lastWeekdayInMonth(year(now()), month(now()), weekdayString)
if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
nextSendDate = lastWeekdayInMonth(year(nextYearsDate), month(nextYearsDate), weekdayString)
return nextSendDate
#[ 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 =
let baseDate = now()
let targetMonth = monthFromString(monthString)
# Get the current year
let currentYear = baseDate.year
# Create a DateTime for the target date in the current year
var nextSendDate = dateTime(currentYear, targetMonth, targetDay, 00, 00, 00, 00)
# If the target date has already passed this year, move to next year
if nextSendDate <= baseDate:
nextSendDate = dateTime(currentYear + 1, targetMonth, targetDay, 00, 00, 00, 00)
let nextSendDateString = format(nextSendDate, "yyyy-MM-dd")
nextSendDate = parse(nextSendDateString, "yyyy-MM-dd")
return nextSendDate