Skip to content
ieshanPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Timi - Nullable Time for Go

Go Reference Go Report Card

A Go package that provides a nullable time type with support for JSON, SQL databases, and optional BSON marshaling for MongoDB.

🎯 Zero Dependencies Approach

Timi uses Go workspaces to provide a clean, dependency-free core package while offering optional integrations and comprehensive testing.

import "github.com/ieshan/timi"
// βœ… Zero external dependencies added to your project!

πŸ“ Project Structure

timi/
β”œβ”€β”€ go.work                     # Generated by ./dev.sh setup (gitignored)
β”œβ”€β”€ go.mod                      # Main package (zero dependencies)
β”œβ”€β”€ timi.go                     # Core nullable time functionality
β”œβ”€β”€ timi_unit_test.go          # Unit tests (no external deps)
β”œβ”€β”€ dev.sh                      # Primary development script
β”œβ”€β”€ integration-tests/          # Database integration testing workspace
β”‚   β”œβ”€β”€ go.mod                  # Integration dependencies isolated here
β”‚   β”œβ”€β”€ timi_mongo_test.go     # MongoDB integration tests
β”‚   └── timi_gorm_test.go      # SQL database tests (MariaDB, PostgreSQL, SQLite)
β”œβ”€β”€ mongodb/                    # MongoDB utilities workspace
β”‚   β”œβ”€β”€ go.mod                  # MongoDB driver dependencies
β”‚   β”œβ”€β”€ bson_helpers.go        # BSON marshaling utilities
β”‚   └── bson_helpers_test.go   # BSON helpers tests
└── compose.yml                # Docker services for integration testing

πŸš€ Installation

go get github.com/ieshan/timi

πŸ’‘ Core Features

Zero Dependencies Core

  • βœ… JSON marshaling/unmarshaling
  • βœ… SQL database support via database/sql
  • βœ… Time manipulation methods (After, Before, Add, etc.)
  • βœ… UTC timezone enforcement
  • βœ… Null value handling

Optional Integrations

  • πŸ”§ MongoDB BSON support (dedicated workspace)
  • πŸ§ͺ Full database integration tests (MongoDB, MariaDB, PostgreSQL, SQLite)

πŸ“– Usage Examples

Basic Usage (Zero Dependencies)

package main

import (
    "encoding/json"
    "fmt"
    "time"
    
    "github.com/ieshan/timi"
)

func main() {
    // Create times
    now := timi.Now()
    christmas := timi.Date(2024, time.December, 25, 15, 30, 0, 0, time.UTC)
    nilTime := timi.NilTime
    
    fmt.Printf("Current time: %s\n", now.String())
    fmt.Printf("Christmas: %s\n", christmas.String()) 
    fmt.Printf("Nil time is null: %t\n", nilTime.IsNull())
    
    // JSON marshaling - works out of the box
    type Event struct {
        Name      string    `json:"name"`
        StartTime timi.Time `json:"start_time"`
        EndTime   timi.Time `json:"end_time,omitempty"`
    }
    
    event := Event{
        Name:      "Holiday Party",
        StartTime: christmas,
        EndTime:   nilTime, // Will be null in JSON
    }
    
    jsonData, _ := json.Marshal(event)
    fmt.Printf("JSON: %s\n", jsonData)
    // Output: {"name":"Holiday Party","start_time":"2024-12-25T15:30:00Z","end_time":null}
}

SQL Database Usage

import (
    "database/sql"
    "github.com/ieshan/timi"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    db, _ := sql.Open("sqlite3", ":memory:")
    
    // Create table
    db.Exec(`CREATE TABLE events (
        id INTEGER PRIMARY KEY,
        name TEXT,
        start_time DATETIME,
        end_time DATETIME
    )`)
    
    // Insert with timi.Time (handles NULL values automatically)
    _, err := db.Exec(
        "INSERT INTO events (name, start_time, end_time) VALUES (?, ?, ?)",
        "Meeting", timi.Now(), timi.NilTime,
    )
    
    // Query back
    var name string
    var startTime, endTime timi.Time
    err = db.QueryRow("SELECT name, start_time, end_time FROM events WHERE id = 1").
        Scan(&name, &startTime, &endTime)
    
    fmt.Printf("Event: %s, Start: %s, End null: %t\n", 
        name, startTime.String(), endTime.IsNull())
}

MongoDB BSON Support (mongodb/ workspace)

The mongodb workspace provides ready-to-use BSON helpers:

// In your project, import the MongoDB driver:
// go get go.mongodb.org/mongo-driver/v2

// Copy the pattern from mongodb/bson_helpers.go:
import (
    "github.com/ieshan/timi"
    "github.com/ieshan/timi/mongodb"
    "go.mongodb.org/mongo-driver/v2/bson"
)

// Option 1: Use helper functions directly
bType, data, err := mongodb.MarshalTimiBSON(timi.Now())
nullType, nullData, _ := mongodb.MarshalTimiBSON(timi.NilTime)

// Convert BSON back to timi.Time
t, err := mongodb.UnmarshalTimiBSON(bType, data)

// Option 2: Use TimiBSONWrapper (implements bson.ValueMarshaler/ValueUnmarshaler)
type Event struct {
    ID        bson.ObjectID     `bson:"_id"`
    Name      string            `bson:"name"`
    StartTime mongodb.TimiBSONWrapper `bson:"start_time"`
    EndTime   mongodb.TimiBSONWrapper `bson:"end_time"`
}

event := Event{
    ID:        bson.NewObjectID(),
    Name:      "Meeting",
    StartTime: mongodb.TimiBSONWrapper{Time: timi.Now()},
    EndTime:   mongodb.TimiBSONWrapper{Time: timi.NilTime},
}

πŸ§ͺ Testing

Quick Start with dev.sh (Recommended)

The dev.sh script is the primary tool for local development:

# Setup: initialize workspace, sync deps, tidy all modules
./dev.sh setup

# Run all tests with race detection across all modules
./dev.sh test

# Run short tests (skips integration tests)
./dev.sh test-short

# Full CI simulation (vet + build + test)
./dev.sh ci

# Other useful commands
./dev.sh build      # Build all packages
./dev.sh vet        # Run go vet in all modules
./dev.sh tidy       # Tidy all go.mod files and sync workspace
./dev.sh clean      # Clean caches and remove generated workspace files
./dev.sh help       # Show all available commands

Docker Testing

For tests that require databases (MongoDB, PostgreSQL, MariaDB), use Docker:

# Run all tests via Docker Compose (includes databases)
./dev.sh test-docker

# Run only integration tests via Docker
./dev.sh test-integration-docker

# Run only MongoDB workspace tests via Docker
./dev.sh test-mongodb-docker

You can also use docker compose directly:

# Bring up test services and run all tests
docker compose up --abort-on-container-exit
docker compose down

Docker Services

Service Purpose
test Runs all Go tests across all modules
mongo MongoDB 8 for integration tests
postgres PostgreSQL 18 for integration tests
mariadb MariaDB 12 for integration tests

Local Testing

If you prefer running Go commands directly:

# Unit tests (fast, no setup required)
go test -v

# Integration tests (requires manual database setup)
cd integration-tests && go test -v

# MongoDB workspace tests
cd mongodb && go test -v

Test Coverage

Unit Tests (timi_unit_test.go)

  • JSON marshaling/unmarshaling
  • Time creation and manipulation
  • String representation
  • Null value handling

Integration Tests

  • SQL Databases (timi_gorm_test.go):

    • MariaDB with microsecond precision
    • PostgreSQL with timezone support
    • SQLite with nanosecond precision
    • CRUD operations, queries, edge cases
  • MongoDB (timi_mongo_test.go):

    • BSON marshaling/unmarshaling
    • MongoDB queries and operations
    • Precision handling (milliseconds)
    • Edge cases and timezone handling

MongoDB Workspace Tests (bson_helpers_test.go)

  • BSON helper function testing
  • Round-trip conversion validation
  • Null value handling in BSON context

πŸ—οΈ Architecture: Go Workspaces

This project uses Go workspaces to solve the dependency management problem:

The Problem

# Traditional approach - forces dependencies on all users
go mod tidy  # ❌ Adds MongoDB, GORM, database drivers to main go.mod

The Solution

# Workspace approach - clean separation
go mod tidy                    # βœ… Main package: zero dependencies
cd integration-tests && go mod tidy  # βœ… Heavy deps isolated here
cd mongodb && go mod tidy      # βœ… MongoDB deps isolated here

Workspace Configuration (go.work)

go 1.26

use (
    .                    # Main timi package
    ./integration-tests  # Integration tests module
    ./mongodb           # MongoDB utilities module
)

Benefits

  • βœ… Zero forced dependencies: Main package stays clean
  • βœ… Complete testing: Full database integration coverage
  • βœ… Developer friendly: Work on all modules simultaneously
  • βœ… CI/CD ready: Test modules independently
  • βœ… Optional features: Use MongoDB helpers when needed

πŸ“Š Dependency Breakdown

Module Dependencies Purpose
Main Package go.mod β†’ Zero external deps Core time functionality
Integration Tests go.mod β†’ MongoDB, GORM, DB drivers Comprehensive database testing
MongoDB Workspace go.mod β†’ MongoDB driver only BSON utilities and tests
Your Project Only what you choose Clean imports

πŸ”§ For Package Consumers

Scenario 1: Basic Usage (Most Common)

import "github.com/ieshan/timi"
// βœ… No external dependencies added to your go.mod
// βœ… JSON, SQL, time operations work perfectly

Scenario 2: With MongoDB Support

# You explicitly add MongoDB to YOUR project
go get go.mongodb.org/mongo-driver/v2

# Import the mongodb submodule for BSON helpers
# import "github.com/ieshan/timi/mongodb"
# βœ… You control your dependencies

Scenario 3: Contributing/Testing

# Clone repository
git clone https://github.com/ieshan/timi
cd timi

# Setup and test everything locally
./dev.sh setup               # Initialize workspace
./dev.sh test                # Run all tests
./dev.sh ci                  # Full CI simulation

# Or work directly with Go commands
go work sync
go test -v
cd integration-tests && go test -v
cd ../mongodb && go test -v

🚦 Development Workflow

Local Development (Recommended)

# Initial setup
./dev.sh setup

# Quick development cycle
./dev.sh test-short           # Fast tests (skips integration)
./dev.sh test                 # All tests with race detection
./dev.sh ci                   # Full CI simulation

# Workspace management
./dev.sh tidy                 # Tidy modules and sync workspace
./dev.sh clean                # Clean caches and generated files

Docker Development

# Run tests that need databases (MongoDB, PostgreSQL, MariaDB)
./dev.sh test-docker

# Targeted Docker testing
./dev.sh test-integration-docker    # Integration tests only
./dev.sh test-mongodb-docker        # MongoDB workspace only

# Or use docker compose directly
docker compose up --abort-on-container-exit
docker compose down

πŸ“‹ API Reference

Core Types

type Time sql.NullTime
var NilTime = Time{Time: time.Time{}, Valid: false}

Creation Functions

func Now() Time
func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Time

Key Methods

func (t Time) String() string
func (t *Time) IsNull() bool
func (t Time) IsZero() bool
func (t Time) Zone() (string, int)

// Comparison
func (t Time) After(u Time) bool
func (t Time) Before(u Time) bool
func (t Time) Compare(u Time) int
func (t Time) Equal(u Time) bool

// Time components
func (t Time) Date() (year int, month time.Month, day int)
func (t Time) Year() int
func (t Time) Month() time.Month
func (t Time) Day() int
func (t Time) Weekday() time.Weekday
func (t Time) ISOWeek() (year, week int)
func (t Time) Clock() (hour, min, sec int)
func (t Time) Hour() int
func (t Time) Minute() int
func (t Time) Second() int
func (t Time) Nanosecond() int
func (t Time) YearDay() int

// Arithmetic
func (t Time) Add(d time.Duration) Time
func (t Time) Sub(u Time) time.Duration
func (t Time) AddDate(years int, months int, days int) Time
func (t Time) Truncate(d time.Duration) Time
func (t Time) Round(d time.Duration) Time

// Unix timestamps
func (t Time) Unix() int64
func (t Time) UnixMilli() int64
func (t Time) UnixMicro() int64
func (t Time) UnixNano() int64

// JSON support
func (t Time) MarshalJSON() ([]byte, error)
func (t *Time) UnmarshalJSON(data []byte) error
func (t Time) MarshalText() ([]byte, error)
func (t *Time) UnmarshalText(data []byte) error

// SQL support
func (t *Time) Scan(value interface{}) error
func (t Time) Value() (driver.Value, error)

MongoDB BSON Helpers (mongodb/ workspace)

func MarshalTimiBSON(t timi.Time) (bson.Type, []byte, error)
func UnmarshalTimiBSON(bType bson.Type, data []byte) (timi.Time, error)

type TimiBSONWrapper struct {
    timi.Time
}
// Implements bson.ValueMarshaler and bson.ValueUnmarshaler

🀝 Contributing

  1. Setup: Run ./dev.sh setup to initialize the workspace
  2. Core changes: Work in main directory, test with ./dev.sh test-short
  3. Integration changes: Work in integration-tests/, test with ./dev.sh test-integration-docker
  4. MongoDB changes: Work in mongodb/, test with ./dev.sh test-mongodb-docker
  5. Before submitting: Always run ./dev.sh ci to simulate the full CI pipeline

Use ./dev.sh for day-to-day development. Use Docker for integration tests that require databases.

πŸ“„ License

MIT


πŸŽ‰ Timi provides clean, nullable time handling with zero dependency pollution - the best of both worlds!

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages