Skip to content

Instantly share code, notes, and snippets.

@gatlin
Created November 22, 2025 12:29
Show Gist options
  • Select an option

  • Save gatlin/4bee9645b96e8b9e733960453b972f49 to your computer and use it in GitHub Desktop.

Select an option

Save gatlin/4bee9645b96e8b9e733960453b972f49 to your computer and use it in GitHub Desktop.
(partially) vibe coded bible reading planner
cabal-version: 3.0
name: bible-reading
version: 0.1.0.0
synopsis: Bible reading plan generator
description: A simple program to generate Bible reading schedules following the tradition of reading 3 chapters Monday-Saturday and 5 on Sunday.
license: MIT
license-file: LICENSE
author: Your Name
maintainer: [email protected]
homepage: https://github.com/yourusername/bible-reading
category: Text
build-type: Simple
executable bible-reading
main-is: Main.hs
hs-source-dirs: .
default-language: Haskell2010
build-depends:
base >= 4.14 && < 5,
time >= 1.9 && < 1.10,
text >= 1.2 && < 2
ghc-options:
-Werror
-O2
default-extensions:
OverloadedStrings
import qualified Data.Time as T
import qualified Data.Time.Calendar.WeekDate as WD
import Text.Printf (printf)
import System.Environment (getArgs)
import Data.List (findIndex)
main :: IO ()
main = do
args <- getArgs
case args of
[dateStr, startBook, startChapterStr] -> do
let startDate = readDay dateStr
startChapter = (startBook, read startChapterStr)
putStrLn "Bible Reading Plan"
putStrLn "=================="
mapM_ putStrLn $ generateReadingPlan startDate startChapter
_ -> do
putStrLn "Usage: bible-reading <date> <start-book> <start-chapter>"
putStrLn "Example: bible-reading \"2023-12-01\" \"Genesis\" 1"
putStrLn ""
putStrLn "This program generates a 30-day Bible reading plan following"
putStrLn "the tradition of reading 3 chapters Monday through Saturday,"
putStrLn "and 5 on Sunday."
-- Bible book data structure
data Book = Book
{ bookName :: String
, bookChapters :: Int
} deriving (Show, Eq)
-- Chapter reference (book name, chapter number)
type ChapterRef = (String, Int)
-- Reading schedule: [Sun, Mon, Tues, Wed, Thu, Fri, Sat]
schedule :: [Int]
schedule = [5, 3, 3, 3, 3, 3, 3]
-- Generate list of days with their reading assignments
generateReadingPlan :: T.Day -> ChapterRef -> [String]
generateReadingPlan startDate startChapter =
take daysRemaining $ zipWith3 formatDayReading days chaps plan
where
days = map (`T.addDays` startDate) [0..]
plan = [schedule !! dayOfWeek d | d <- days]
startChapterNumber = chapterToNumber startChapter
cumulative = scanl (+) startChapterNumber plan
chaps = map numberToChapter cumulative
totalChapters = sum (map bookChapters bibleBooks)
daysRemaining = length $ takeWhile (<= totalChapters) cumulative
-- Format a single day's reading
formatDayReading :: T.Day -> ChapterRef -> Int -> String
formatDayReading day (book, chapter) =
printf "%s: %s %d (%d chapters)" (formatDay day) book chapter
-- Format day as string
formatDay :: T.Day -> String
formatDay = T.formatTime T.defaultTimeLocale "%A, %B %d, %Y"
-- Get book index by name
bookIndexByName :: String -> Int
bookIndexByName name =
case findIndex (\b -> bookName b == name) bibleBooks of
Just idx -> idx
Nothing -> error $ "Unknown book: " ++ name
-- Get book by index
bookByIndex :: Int -> Book
bookByIndex idx
| idx < length bibleBooks = bibleBooks !! idx
| otherwise = error "Ran out of Bible."
-- Convert chapter reference to a simple number for easier calculation
chapterToNumber :: ChapterRef -> Int
chapterToNumber (bkName, chapter) = cumulative + chapter
where
bookIdx = bookIndexByName bkName
cumulative = sum [bookChapters (bibleBooks !! i) | i <- [0..bookIdx-1]]
-- Convert number back to chapter reference
numberToChapter :: Int -> ChapterRef
numberToChapter n = (bookName book, localChapter)
where
cumulative = scanl (+) 0 (map bookChapters bibleBooks)
bookIdx = length (takeWhile (< n) cumulative) - 1
book = bookByIndex bookIdx
localChapter = n - cumulative !! bookIdx
-- Get day of week (0 = Sunday, 1 = Monday, etc.)
dayOfWeek :: T.Day -> Int
dayOfWeek day = d `mod` 7 where (_, _, d) = WD.toWeekDate day
-- Simple date parsing
readDay :: String -> T.Day
readDay str = case map read (splitOn '-' str) :: [Int] of
[year, month, day] -> T.fromGregorian (toInteger year) month day
_ -> error "error parsing date string"
-- Helper function to split string by delimiter
splitOn :: Eq a => a -> [a] -> [[a]]
splitOn _ [] = []
splitOn delim str =
let (before, after) = break (== delim) str
in before : case after of
[] -> []
(_:rest) -> splitOn delim rest
-- Bible books with their chapter counts
bibleBooks :: [Book]
bibleBooks =
[ Book "Genesis" 50
, Book "Exodus" 40
, Book "Leviticus" 27
, Book "Numbers" 36
, Book "Deuteronomy" 34
, Book "Joshua" 24
, Book "Judges" 21
, Book "Ruth" 4
, Book "1 Samuel" 31
, Book "2 Samuel" 24
, Book "1 Kings" 22
, Book "2 Kings" 25
, Book "1 Chronicles" 29
, Book "2 Chronicles" 36
, Book "Ezra" 10
, Book "Nehemiah" 13
, Book "Esther" 10
, Book "Job" 42
, Book "Psalms" 150
, Book "Proverbs" 31
, Book "Ecclesiastes" 12
, Book "Song of Solomon" 8
, Book "Isaiah" 66
, Book "Jeremiah" 52
, Book "Lamentations" 5
, Book "Ezekiel" 48
, Book "Daniel" 12
, Book "Hosea" 14
, Book "Joel" 3
, Book "Amos" 9
, Book "Obadiah" 1
, Book "Jonah" 4
, Book "Micah" 7
, Book "Nahum" 3
, Book "Habakkuk" 3
, Book "Zephaniah" 3
, Book "Haggai" 2
, Book "Zechariah" 14
, Book "Malachi" 4
, Book "Matthew" 28
, Book "Mark" 16
, Book "Luke" 24
, Book "John" 21
, Book "Acts" 28
, Book "Romans" 16
, Book "1 Corinthians" 16
, Book "2 Corinthians" 13
, Book "Galatians" 6
, Book "Ephesians" 6
, Book "Philippians" 4
, Book "Colossians" 4
, Book "1 Thessalonians" 5
, Book "2 Thessalonians" 3
, Book "1 Timothy" 6
, Book "2 Timothy" 4
, Book "Titus" 3
, Book "Philemon" 1
, Book "Hebrews" 13
, Book "James" 5
, Book "1 Peter" 5
, Book "2 Peter" 3
, Book "1 John" 5
, Book "2 John" 1
, Book "3 John" 1
, Book "Jude" 1
, Book "Revelation" 22
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment