go-date-fns
A comprehensive date utility library for Go, inspired by date-fns, with 140+ pure and immutable functions
About go-date-fns
This large README is shown as a plain-text preview. Read the full README at the source
# ๐๏ธ go-date-fns
> A modern, functional date utility library for Go, inspired by the beloved date-fns JavaScript library.
[](https://golang.org/)
[](https://www.gnu.org/licenses/lgpl-2.1)
[](https://goreportcard.com/report/github.com/chmenegatti/go-date-fns)
[](https://github.com/chmenegatti/go-date-fns/actions/workflows/ci.yml)
[](https://pkg.go.dev/github.com/chmenegatti/go-date-fns)
[](https://app.codecov.io/gh/chmenegatti/go-date-fns)
**go-date-fns** brings the power and elegance of functional date manipulation to Go. With over **150+ pure, immutable functions**, it provides a comprehensive toolkit for working with dates and times in a safe, predictable, and timezone-aware manner.
---
## โจ Why go-date-fns?
In the world of Go development, working with dates can be complex and error-prone. **go-date-fns** solves this by providing:
- ๐งฎ **Pure Functions**: No side effects, original dates never modified
- ๐ **Timezone Aware**: Full timezone support across all operations
- ๐ **Zero Dependencies**: Only uses Go standard library
- ๐ฏ **Type Safe**: Leverages Go's strong typing system
- ๐ฆ **WebAssembly Ready**: Compile to WASM without issues
- ๐งช **Battle Tested**: 100% test coverage with comprehensive benchmarks
- ๐ **Well Documented**: Every function has clear examples and documentation
---
## ๐ Quick Start
### Installation
```bash
go get github.com/chmenegatti/go-date-fns
```
### Basic Usage
```go
package main
import (
"fmt"
"time"
"github.com/chmenegatti/go-date-fns/dateutils"
)
func main() {
// Parse a date
date, _ := dateutils.Parse("2024-01-15", time.UTC)
// Add 7 days
nextWeek := dateutils.AddDays(date, 7)
// Format the result
formatted, _ := dateutils.Format(nextWeek, dateutils.DateISO, nil)
fmt.Println(formatted) // Output: 2024-01-22
// Check if it's a weekend
isWeekend := dateutils.IsWeekend(nextWeek)
fmt.Printf("Is weekend: %t\n", isWeekend)
}
```
---
## ๐ Complete Feature Overview
### ๐ **Parsing Functions**
Convert strings to time.Time objects with intelligent format detection:
```go
// Smart parsing with common formats
date, err := dateutils.Parse("2024-01-15", time.UTC)
date, err := dateutils.Parse("01/15/2024", time.UTC)
date, err := dateutils.Parse("January 15, 2024", time.UTC)
// ISO 8601 parsing
isoDate, err := dateutils.ParseISO("2024-01-15T14:30:00Z", time.UTC)
// Custom format parsing
customDate, err := dateutils.ParseWithFormat("15-01-2024", "02-01-2006", time.UTC)
// Validation
isValid := dateutils.IsValidISO("2024-01-15T14:30:00Z")
```
### ๐จ **Formatting Functions**
Transform dates into human-readable strings:
```go
date := time.Date(2024, 1, 15, 14, 30, 0, 0, time.UTC)
// Predefined formats
iso, _ := dateutils.Format(date, dateutils.DateISO, nil) // "2024-01-15"
us, _ := dateutils.Format(date, dateutils.DateUS, nil) // "01/15/2024"
readable, _ := dateutils.Format(date, dateutils.Readable, nil) // "January 15, 2024"
dateTime, _ := dateutils.Format(date, dateutils.DateTime24, nil) // "2024-01-15 14:30:00"
// Custom formatting with user-friendly placeholders
custom, _ := dateutils.FormatCustom(date, "DD/MM/YYYY at HH:mm", nil) // "15/01/2024 at 14:30"
custom, _ := dateutils.FormatCustom(date, "MMMM DD, YYYY", nil) // "January 15, 2024"
// Safe formatting (returns empty string on error)
safe := dateutils.FormatSafe(date, dateutils.DateISO, nil)
// Formatting with fallback
withDefault := dateutils.FormatWithDefault(date, dateutils.DateISO, nil, "N/A")
```
### โ๏ธ **Comparison Functions**
Compare dates with precision and timezone awareness:
```go
date1 := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
date2 := time.Date(2024, 1, 16, 15, 0, 0, 0, time.UTC)
// Precise comparisons (includes time)
dateutils.IsBefore(date1, date2) // true
dateutils.IsAfter(date1, date2) // false
dateutils.IsEqual(date1, date1) // true
dateutils.IsBeforeOrEqual(date1, date1) // true
// Date-only comparisons (ignores time)
dateutils.IsBeforeDate(date1, date2) // true
dateutils.IsAfterDate(date1, date2) // false
// Same period comparisons
dateutils.IsSameDay(date1, date2) // false (different days)
dateutils.IsSameWeek(date1, date2) // true (same week)
dateutils.IsSameMonth(date1, date2) // true (same month)
dateutils.IsSameYear(date1, date2) // true (same year)
dateutils.IsSameHour(date1, date2) // false (different hours)
dateutils.IsSameMinute(date1, date2) // false (different minutes)
// Sorting and selection
times := []time.Time{date1, date2, time.Now()}
earliest := dateutils.Min(times) // Get earliest time
latest := dateutils.Max(times) // Get latest time
closest := dateutils.ClosestTo(date1, times) // Get closest to date1
// Comparison with ordering
result := dateutils.CompareAsc(date1, date2) // -1 (date1 before date2)
result = dateutils.CompareDesc(date1, date2) // 1 (desc order)
// Timezone-aware comparisons
est, _ := time.LoadLocation("America/New_York")
dateutils.IsBeforeInTimezone(date1, date2, est)
```
### ๐ง **Manipulation Functions**
Add, subtract, and modify dates immutably:
```go
baseDate := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC) // Monday
// Add/subtract days, weeks, months, years
nextWeek := dateutils.AddDays(baseDate, 7) // 2024-01-22
lastWeek := dateutils.SubDays(baseDate, 7) // 2024-01-08
nextMonth := dateutils.AddMonths(baseDate, 1) // 2024-02-15
lastYear := dateutils.SubYears(baseDate, 1) // 2023-01-15
// Add/subtract time units
laterToday := dateutils.AddHours(baseDate, 5) // Add 5 hours
earlierToday := dateutils.SubMinutes(baseDate, 30) // Subtract 30 minutes
futureTime := dateutils.AddSeconds(baseDate, 120) // Add 2 minutes
// Business days (skips weekends)
nextBusinessDay := dateutils.AddBusinessDays(baseDate, 5) // Skips weekend, lands on weekday
pastBusinessDay := dateutils.SubBusinessDays(baseDate, 3) // Goes back 3 business days
// Timezone-aware addition
est, _ := time.LoadLocation("America/New_York")
dateInEST := dateutils.AddDaysWithTimezone(baseDate, 1, est)
```
### ๐ **Get Functions**
Extract specific components from dates:
```go
date := time.Date(2024, 6, 15, 14, 30, 45, 500000000, time.UTC) // Saturday
// Date components
year := dateutils.GetYear(date) // 2024
month := dateutils.GetMonth(date) // 6 (June)
day := dateutils.GetDate(date) // 15
dayOfWeek := dateutils.GetDay(date) // 6 (Saturday, 0=Sunday)
dayOfYear := dateutils.GetDayOfYear(date) // 167
quarter := dateutils.GetQuarter(date) // 2 (Q2)
// Time components
hours := dateutils.GetHours(date) // 14
minutes := dateutils.GetMinutes(date) // 30
seconds := dateutils.GetSeconds(date) // 45
milliseconds := dateutils.GetMilliseconds(date) // 500
// Week-related
week := dateutils.GetWeek(date) // ISO week number
weekOfMonth := dateutils.GetWeekOfMonth(date) // Week of the month
weekYear := dateutils.GetWeekYear(date) // ISO week year
```
### โ๏ธ **Set Functions**
Create new dates by setting specific components:
```go
baseDate := time.Date(2024, 6, 15, 14, 30, 45, 0, time.UTC)
// Set date components
newYear := dateutils.SetYear(baseDate, 2025) // 2025-06-15 14:30:45
newMonth := dateutils.SetMonth(baseDate, 12) // 2024-12-15 14:30:45
newDay := dateutils.SetDate(baseDate, 1) // 2024-06-01 14:30:45
newQuarter := dateutils.SetQuarter(baseDate, 1) // 2024-01-15 14:30:45
// Set time components
newHours := dateutils.SetHours(baseDate, 9) // 2024-06-15 09:30:45
newMinutes := dateutils.SetMinutes(baseDate, 0) // 2024-06-15 14:00:45
newSeconds := dateutils.SetSeconds(baseDate, 0) // 2024-06-15 14:30:00
// Set by day of week or day of year
monday := dateutils.SetDay(baseDate, 1) // Set to Monday of the same week
newYearDay := dateutils.SetDayOfYear(baseDate, 100) // Set to 100th day of year
```
### ๐ **Difference Functions**
Calculate time differences with various precision levels:
```go
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, 1, 15, 12, 30, 45, 0, time.UTC)
// Time unit differences (integer)
years := dateutils.DifferenceInYears(end, start) // 0
months := dateutils.DifferenceInMonths(end, start) // 0
weeks := dateutils.DifferenceInWeeks(end, start) // 2
days := dateutils.DifferenceInDays(end, start) // 14
hours := dateutils.DifferenceInHours(end, start) // 348
minutes := dateutils.DifferenceInMinutes(end, start) // 20910
seconds := dateutils.DifferenceInSeconds(end, start) // 1254645
// Calendar-based differences (ignore time components)
calendarYears := dateutils.DifferenceInCalendarYears(end, start) // 0
calendarMonths := dateutils.DifferenceInCalendarMonths(end, start)// 0
calendarDays := dateutils.DifferenceInCalendarDays(end, start) // 14
quarters := dateutils.DifferenceInQuarters(end, start) // 0
calendarQuarters := dateutils.DifferenceInCalendarQuarters(end, start) // 0
// Business and specialized differences
businessDays := dateutils.DifferenceInBusinessDays(end, start) // 10 (excluding weekends)
// Floating-point precision
daysPrecise := dateutils.DifferenceInDaysFloat(end, start) // 14.52...
weeksPrecise := dateutils.DifferenceInWeeksFloat(end, start) // 2.07...
hoursPrecise := dateutils.DifferenceInHoursFloat(end, start) // 348.51...
// Absolute differences (always positive)
absDays := dateutils.AbsDifferenceInDays(start, end) // 14
absHours := dateutils.AbsDifferenceInHours(start, end) // 348
absMinutes := dateutils.AbsDifferenceInMinutes(start, end) // 20910
absMonths := dateutils.AbsDifferenceInMonths(start, end) // 0
```
### โ
**Validation Functions**
Comprehensive date validation and checking:
```go
date := time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC) // Leap year date
today := time.Now()
// Basic validation
dateutils.IsValid(date) // true
dateutils.IsValid(time.Time{}) // false (zero time)
// Year validation
dateutils.IsLeapYear(2024) // true
dateutils.IsLeapYear(2023) // false
// Day type validation
dateutils.IsWeekend(date) // true/false based on day
dateutils.IsWeekday(date) // opposite of IsWeekend
// Time-based validation
dateutils.IsToday(today) // true
dateutils.IsTomorrow(dateutils.AddDays(today, 1)) // true
dateutils.IsYesterday(dateutils.AddDays(today, -1)) // true
// Interval validation
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, 12, 31, 0, 0, 0, 0, time.UTC)
test := time.Date(2024, 6, 15, 0, 0, 0, 0, time.UTC)
dateutils.IsWithinInterval(test, start, end) // true
// Boundary validation
dateutils.IsFirstDayOfMonth(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) // true
dateutils.IsLastDayOfMonth(time.Date(2024, 1, 31, 0, 0, 0, 0, time.UTC)) // true
dateutils.IsFirstDayOfYear(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) // true
dateutils.IsLastDayOfYear(time.Date(2024, 12, 31, 0, 0, 0, 0, time.UTC)) // true
// Comparison validation
date1 := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
date2 := time.Date(2024, 1, 15, 15, 0, 0, 0, time.UTC)
dateutils.IsSameDate(date1, date2) // true (same date, different time)
dateutils.IsSameMonth(date1, date2) // true
dateutils.IsSameYear(date1, date2) // true
```
### ๐
**Period Utility Functions**
Get the start and end of various time periods:
```go
date := time.Date(2024, 1, 15, 14, 30, 45, 0, time.UTC) // Monday, mid-day
// Day boundaries
startOfDay := dateutils.StartOfDay(date) // 2024-01-15 00:00:00
endOfDay := dateutils.EndOfDay(date) // 2024-01-15 23:59:59
// Week boundaries (Monday-based)
startOfWeek := dateutils.StartOfWeek(date) // Previous Monday 00:00:00
endOfWeek := dateutils.EndOfWeek(date) // Next Sunday 23:59:59
// Week boundaries (Sunday-based)
startOfWeekSunday := dateutils.StartOfWeekSunday(date) // Previous Sunday 00:00:00
endOfWeekSaturday := dateutils.EndOfWeekSaturday(date) // Next Saturday 23:59:59
// Month boundaries
startOfMonth := dateutils.StartOfMonth(date) // 2024-01-01 00:00:00
endOfMonth := dateutils.EndOfMonth(date) // 2024-01-31 23:59:59
// Year boundaries
startOfYear := dateutils.StartOfYear(date) // 2024-01-01 00:00:00
endOfYear := dateutils.EndOfYear(date) // 2024-12-31 23:59:59
// Hour boundaries
startOfHour := dateutils.StartOfHour(date) // 2024-01-15 14:00:00
endOfHour := dateutils.EndOfHour(date) // 2024-01-15 14:59:59
// Minute boundaries
startOfMinute := dateutils.StartOfMinute(date) // 2024-01-15 14:30:00
endOfMinute := dateutils.EndOfMinute(date) // 2024-01-15 14:30:59
```
---
### ๐ **Interval Utilities**
**NEW:** Generate arrays of dates within intervals - perfect for reporting, scheduling, and data analysis:
```go
// Define an interval for Q1 2024
q1Interval := dateutils.Interval{
Start: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
End: time.Date(2024, 3, 31, 23, 59, 59, 0, time.UTC),
}
// Get all months in Q1
months := dateutils.EachMonthOfInterval(q1Interval)
// Returns: [2024-01-01, 2024-02-01, 2024-03-01]
// Get all weekends in January 2024
january := dateutils.Interval{
Start: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
End: time.Date(2024, 1, 31, 23, 59, 59, 0, time.UTC),
}
weekends := dateutils.EachWeekendOfInterval(january)
// Returns all Saturday and Sunday dates in January
// Get business days for a work week
workWeek := dateutils.Interval{
Start: time.Date(2024, 1, 8, 0, 0, 0, 0, time.UTC), // Monday
End: time.Date(2024, 1, 12, 23, 59, 59, 0, time.UTC), // Friday
}
businessDays := dateutils.EachBusinessDayOfInterval(workWeek)
// Returns: [Mon, Tue, Wed, Thu, Fri] (5 business days)
// Get all quarters in 2024
year := dateutils.Interval{
Start: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
End: time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC),
}
quarters := dateutils.EachQuarterOfInterval(year)
// Returns: [Q1, Q2, Q3, Q4] start dates
// Get hourly intervals for a workday
workday := dateutils.Interval{
Start: time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC),
End: time.Date(2024, 1, 15, 17, 0, 0, 0, time.UTC),
}
hours := dateutils.EachHourOfInterval(workday)
// Returns: [09:00, 10:00, 11:00, ..., 17:00]
```
**Available Each Functions:**
- `EachDayOfInterval` - All days in interval
- `EachWeekOfInterval` - Weekly intervals (Monday start)
- `EachWeekOfIntervalSunday` - Weekly intervals (Sunday start)
- `EachMonthOfInterval` - Monthly intervals
- `EachYearOfInterval` - Yearly intervals
- `EachQuarterOfInterval` - Quarterly intervals
- `EachHourOfInterval` - Hourly intervals
- `EachMinuteOfInterval` - Minute intervals
- `EachWeekendOfInterval` - Weekend days only
- `EachBusinessDayOfInterval` - Business days only
---
## ๐ Timezone Support
**go-dateutils** provides first-class timezone support across all functions:
```go
// Load timezones
utc := time.UTC
est, _ := time.LoadLocation("America/New_York")
pst, _ := time.LoadLocation("America/Los_Angeles")
tokyo, _ := time.LoadLocation("Asia/Tokyo")
// Parse with timezone
date, _ := dateutils.ParseISO("2024-01-15T10:00:00Z", est)
// Format in different timezones
utcFormat, _ := dateutils.Format(date, dateutils.DateTime24, utc)
estFormat, _ := dateutils.Format(date, dateutils.DateTime24, est)
pstFormat, _ := dateutils.Format(date, dateutils.DateTime24, pst)
// Timezone-aware comparisons
isAfterInEST := dateutils.IsAfterInTimezone(date1, date2, est)
// Add days with timezone conversion
futureInTokyo := dateutils.AddDaysWithTimezone(date, 7, tokyo)
```
---
## ๐ฏ Real-World Examples
### ๐ Business Date Calculations
```go
// Calculate project deadline (20 business days from today)
today := time.Now()
deadline := dateutils.AddBusinessDays(today, 20)
fmt.Printf("Project deadline: %s\n",
dateutils.FormatSafe(deadline, dateutils.Readable, nil))
// Calculate working days between two dates
startDate, _ := dateutils.Parse("2024-01-01", time.UTC)
endDate, _ := dateutils.Parse("2024-01-31", time.UTC)
workingDays := dateutils.DifferenceInBusinessDays(endDate, startDate)
fmt.Printf("Working days in January 2024: %d\n", workingDays)
// Set specific meeting times using Set functions
meetingBase := time.Date(2024, 6, 15, 0, 0, 0, 0, time.UTC)
quarterlyMeeting := dateutils.SetQuarter(meetingBase, 3) // Q3 meeting
monthlyMeeting := dateutils.SetDate(quarterlyMeeting, 1) // First of the month
finalTime := dateutils.SetHours(monthlyMeeting, 14) // 2 PM meeting
// Get components for reporting
quarter := dateutils.GetQuarter(finalTime) // Which quarter?
week := dateutils.GetWeek(finalTime) // Which week of year?
dayOfYear := dateutils.GetDayOfYear(finalTime) // Day number in year
// Calculate time spans using new time unit functions
projectStart := dateutils.SubMonths(today, 3) // 3 months ago
deadline2 := dateutils.AddHours(projectStart, 8*24*30) // Add work hours
timeLeft := dateutils.SubDays(deadline2, 5) // 5 days before deadline
```
### ๐
Event Planning
```go
// Plan a weekly meeting series
meetingStart, _ := dateutils.ParseISO("2024-01-15T14:00:00Z", time.UTC)
meetings := make([]time.Time, 0, 12)
for i := 0; i < 12; i++ {
meeting := dateutils.AddWeeks(meetingStart, i)
// Skip if it falls on a weekend
if dateutils.IsWeekend(meeting) {
meeting = dateutils.AddDays(meeting, 1) // Move to Monday
}
meetings = append(meetings, meeting)
}
// Display the meeting schedule
for i, meeting := range meetings {
formatted, _ := dateutils.FormatCustom(meeting, "MMMM DD, YYYY at HH:mm", nil)
fmt.Printf("Meeting %d: %s\n", i+1, formatted)
}
```
### ๐ง Date Component Manipulation
```go
// Extract and manipulate date components
baseDate := time.Date(2024, 8, 15, 16, 45, 30, 0, time.UTC)
// Extract all components
fmt.Printf("Year: %d, Quarter: Q%d\n", dateutils.GetYear(baseDate), dateutils.GetQuarter(baseDate))
fmt.Printf("Month: %d, Day: %d, Day of Year: %d\n",
dateutils.GetMonth(baseDate), dateutils.GetDate(baseDate), dateutils.GetDayOfYear(baseDate))
fmt.Printf("Week: %d, Day of Week: %d\n", dateutils.GetWeek(baseDate), dateutils.GetDay(baseDate))
fmt.Printf("Time: %02d:%02d:%02d.%03d\n",
dateutils.GetHours(baseDate), dateutils.GetMinutes(baseDate),
dateutils.GetSeconds(baseDate), dateutils.GetMilliseconds(baseDate))
// Create variations by setting components
yearEnd := dateutils.SetMonth(dateutils.SetDate(baseDate, 31), 12) // Dec 31st
startOfQuarter := dateutils.SetQuarter(baseDate, 1) // Q1 start
mondayMeeting := dateutils.SetDay(baseDate, 1) // Next Monday
nineAM := dateutils.SetHours(dateutils.SetMinutes(baseDate, 0), 9) // 9:00 AM
// Time comparisons using new functions
date1 := time.Date(2024, 6, 15, 14, 30, 0, 0, time.UTC)
date2 := time.Date(2024, 6, 22, 10, 15, 0, 0, time.UTC)
if dateutils.IsSameWeek(date1, date2) {
fmt.Println("Same week!")
} else if dateutils.IsSameMonth(date1, date2) {
fmt.Println("Same month, different week")
}
// Find closest date from a setFrequently Asked Questions
What is go-date-fns?
go-date-fns is a Date and Time library for the Go programming language. A comprehensive date utility library for Go, inspired by date-fns, with 140+ pure and immutable functions
How do I install go-date-fns?
Install go-date-fns with the Go module system using `go get chmenegatti/go-date-fns`. Check the repository for the current installation instructions.
What category does go-date-fns belong to?
go-date-fns is listed under Date and Time, specifically Date and Time.