bublyk

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: GPL-3.0 Imports: 3 Imported by: 0

README

Tests and linter GitHub release MIT license codecov help wanted

bublyk

A lightweight, memory-efficient Go package for working with dates without time components. The bublyk package introduces the Date type, specifically designed for applications that only need calendar dates in UTC, without the overhead of time details.

Features

Memory Efficiency
  • Only 2 bytes per date - Uses uint16 internally with bit packing
  • 87.5% less memory than time.Time (16 bytes)
  • Perfect for large datasets, databases, and memory-constrained environments
Developer Experience
  • Direct comparisons - Use standard operators (>, <, ==, >=, <=)
  • No boilerplate - Clean, intuitive API
  • Automatic normalization - Invalid dates are automatically corrected
  • 100% test coverage - Thoroughly tested and reliable
Date Range
  • Supported range: January 1, 2000 to December 31, 2127
  • Dates outside this range are clamped to boundaries
  • Optimized for modern applications

Installation

go get github.com/kaatinga/bublyk

Quick Start

package main

import (
	"fmt"
	"time"

	"github.com/kaatinga/bublyk"
)

func main() {
	// Get current date
	today := bublyk.Now()
	fmt.Println("Today:", today) // Output: 2025-11-16

	// Create specific dates
	date1 := bublyk.NewDate(2024, 12, 25)
	date2 := bublyk.NewDate(2025, 1, 1)

	// Direct comparisons
	if date2 > date1 {
		fmt.Println("New Year comes after Christmas")
	}

	// Convert from time.Time
	futureDate := bublyk.NewDateFromTime(time.Now().AddDate(0, 0, 5))
	fmt.Println("5 days from now:", futureDate)
}

API Reference

Creating Dates
Now() Date

Returns the current date in UTC.

today := bublyk.Now()
NewDate(year uint16, month, day byte) Date

Creates a new date. Automatically normalizes invalid dates.

date := bublyk.NewDate(2024, 2, 29) // Leap day
normalized := bublyk.NewDate(2024, 13, 1) // Becomes 2025-01-01
NewDateFromTime(t time.Time) Date

Converts a time.Time to a Date.

date := bublyk.NewDateFromTime(time.Now())
CurrentMonth() Date

Returns the first day of the current month.

monthStart := bublyk.CurrentMonth() // e.g., 2025-11-01
Parse(formattedDate string) (Date, error)

Parses a date string in YYYY-MM-DD format.

date, err := bublyk.Parse("2024-12-25")
if err != nil {
	log.Fatal(err)
}
Date Methods
Accessors
date := bublyk.NewDate(2024, 12, 25)

year := date.Year()   // uint16: 2024
month := date.Month() // byte: 12
day := date.Day()     // byte: 25
Checking and Comparing
// Check if date is set (non-zero)
if date.IsSet() {
	fmt.Println("Date is valid")
}

// Check if date is in the future
if date.IsFuture() {
	fmt.Println("This date hasn't happened yet")
}

// Month-level comparisons
date1 := bublyk.NewDate(2024, 6, 15)
date2 := bublyk.NewDate(2024, 3, 20)

if date1.MonthAfter(date2) {
	fmt.Println("date1 is at least one month after date2")
}

if date2.MonthBefore(date1) {
	fmt.Println("date2 is at least one month before date1")
}
Date Navigation
date := bublyk.NewDate(2024, 12, 25)

// Move by days
tomorrow := date.NextDay()
yesterday := date.PreviousDay()

// Move by weeks
nextWeek := date.NextWeek()
lastWeek := date.PreviousWeek()
Formatting
date := bublyk.NewDate(2024, 12, 25)

// Default format (YYYY-MM-DD)
str := date.String() // "2024-12-25"

// Custom format using time.Time layouts
formatted := date.Format(time.RFC822) // "25 Dec 24 00:00 UTC"

// European format (DD.MM.YYYY)
european := date.DMYWithDots() // "25.12.2024"
Conversion
date := bublyk.NewDate(2024, 12, 25)

// Convert to time.Time
t := date.Time() // time.Time in UTC

Use Cases

Database Storage

Store dates efficiently in databases:

type Event struct {
	ID        int
	Name      string
	EventDate bublyk.Date // Only 2 bytes!
}
Date Ranges and Filtering
startDate := bublyk.NewDate(2024, 1, 1)
endDate := bublyk.NewDate(2024, 12, 31)

for date := startDate; date <= endDate; date = date.NextDay() {
	// Process each day of 2024
}
Business Logic
// Check if subscription is expired
subscription := getSubscription()
if subscription.ExpiryDate < bublyk.Now() {
	sendRenewalNotice()
}

// Calculate billing periods (month 13 normalizes to January of the next year)
billingDate := bublyk.NewDate(2024, 1, 1)
for i := 0; i < 12; i++ {
	processBilling(billingDate)
	billingDate = bublyk.NewDate(billingDate.Year(), billingDate.Month()+1, 1)
}

Technical Details

Memory Layout

The Date type uses bit packing to store year, month, and day in a single uint16:

Bits: [15-9: Year offset] [8-5: Month] [4-0: Day]
      7 bits (0-127)      4 bits (0-15) 5 bits (0-31)
  • Year: Stored as offset from 2000 (0-127 = years 2000-2127)
  • Month: 4 bits (1-12)
  • Day: 5 bits (1-31)
Date Normalization

Invalid dates are automatically normalized using Go's time package:

bublyk.NewDate(2024, 2, 30)  // → 2024-03-01
bublyk.NewDate(2024, 13, 1)  // → 2025-01-01
bublyk.NewDate(2024, 4, 31)  // → 2024-05-01
bublyk.NewDate(1999, 1, 1)   // → 2000-01-01 (minimum)
bublyk.NewDate(2200, 1, 1)   // → 2127-12-31 (maximum)
Performance
  • Comparisons: Direct integer comparison (extremely fast)
  • Memory: 2 bytes vs 16 bytes for time.Time
  • Parsing: Optimized with faststrconv package
  • Navigation: Bit manipulation for same-month operations

Limitations

  • Date range: 2000-01-01 to 2127-12-31 only
  • No time component: Only stores calendar dates
  • UTC only: No timezone support (by design)
  • No locale: Formatting is limited to standard layouts

Contributing

Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.

License

MIT License - see LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrIncorrectFormat = errors.New("incorrect date format")

ErrIncorrectFormat is returned by Parse when the input does not match the YYYY-MM-DD format.

Functions

This section is empty.

Types

type Date

type Date uint16

Date represents a calendar date starting 2000 year and finishing the year 2127.

func CurrentMonth

func CurrentMonth() Date

func NewDate

func NewDate(year uint16, month, day byte) Date

func NewDateFromTime

func NewDateFromTime(t time.Time) Date

NewDateFromTime create new date using time.Time model.

func Now

func Now() Date

func Parse

func Parse(formattedDate string) (Date, error)

func (Date) DMYWithDots

func (thisDate Date) DMYWithDots() string

DMYWithDots returns date as string in the DD.MM.YYYY format.

func (Date) Day

func (thisDate Date) Day() byte

func (Date) Format

func (thisDate Date) Format(layout string) string

func (Date) IsFuture

func (thisDate Date) IsFuture() bool

func (Date) IsSet

func (thisDate Date) IsSet() bool

func (Date) Month

func (thisDate Date) Month() byte

func (Date) MonthAfter

func (thisDate Date) MonthAfter(date Date) bool

MonthAfter reports whether the date falls in a later calendar month than the target date. Days are ignored: 2021-06-01 is MonthAfter 2021-05-31.

func (Date) MonthBefore

func (thisDate Date) MonthBefore(date Date) bool

MonthBefore reports whether the date falls in an earlier calendar month than the target date. Days are ignored: 2021-05-31 is MonthBefore 2021-06-01.

func (Date) NextDay

func (thisDate Date) NextDay() Date

func (Date) NextWeek

func (thisDate Date) NextWeek() Date

func (Date) PreviousDay

func (thisDate Date) PreviousDay() Date

func (Date) PreviousWeek

func (thisDate Date) PreviousWeek() Date

func (Date) String

func (thisDate Date) String() string

String returns date as string in the default PostgreSQL date format, YYYY-MM-DD.

func (Date) Time

func (thisDate Date) Time() time.Time

func (Date) Year

func (thisDate Date) Year() uint16

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL