Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# Coverage file
coverage.txt
coverage.out
coverage.html

# Go workspace file
go.work
Expand Down
49 changes: 47 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ The `utc` package provides an enhanced alias of Go's `time.Time` that ensures yo
## Features

- Guaranteed UTC time handling
- JSON marshaling/unmarshaling support
- SQL database compatibility
- JSON marshaling/unmarshaling support with flexible parsing
- SQL database compatibility with enhanced type support
- Automatic timezone handling (PST/PDT, EST/EDT, etc.)
- Extensive formatting options:
- US date formats (MM/DD/YYYY)
Expand All @@ -29,6 +29,10 @@ The `utc` package provides an enhanced alias of Go's `time.Time` that ensures yo
- Common components (weekday, month, etc.)
- Timezone conversion methods with fallback support
- Full compatibility with Go's standard `time.Time` methods
- Nil-safe operations that return errors instead of panicking
- Debug mode with detailed logging for development
- Text encoding support for broader codec compatibility
- Unix timestamp helpers and day boundary utilities

## Installation

Expand All @@ -38,6 +42,8 @@ To install the `utc` package, use the following command:
go get github.com/agentstation/utc
```

**Requirements**: Go 1.18 or later (uses `any` type and other modern Go features)

## Usage

1. Import the package:
Expand Down Expand Up @@ -109,6 +115,45 @@ type Record struct {
}
```

## Debug Mode

The package includes a debug mode that helps identify potential bugs during development:

```sh
# Build with debug mode enabled
go build -tags debug

# Run tests with debug mode
go test -tags debug ./...
```

When debug mode is enabled, the package logs warnings when methods are called on nil receivers:

```
[UTC DEBUG] 2024/01/02 15:04:05 debug.go:26: String() called on nil *Time receiver
[UTC DEBUG] 2024/01/02 15:04:05 debug.go:26: Value() called on nil *Time receiver
```

## Additional Utilities

The package includes several convenience methods:

```go
// Unix timestamp conversions
t1 := utc.FromUnix(1704199445) // From Unix seconds
t2 := utc.FromUnixMilli(1704199445000) // From Unix milliseconds
seconds := t.Unix() // To Unix seconds
millis := t.UnixMilli() // To Unix milliseconds

// Day boundaries
start := t.StartOfDay() // 2024-01-02 00:00:00.000000000 UTC
end := t.EndOfDay() // 2024-01-02 23:59:59.999999999 UTC

// Generic timezone conversion
eastern, err := t.In("America/New_York")
tokyo, err := t.In("Asia/Tokyo")
```

<!-- gomarkdoc:embed:start -->

<!-- Code generated by gomarkdoc. DO NOT EDIT -->
Expand Down
27 changes: 27 additions & 0 deletions debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build debug
// +build debug

package utc

import (
"log"
"os"
"sync"
)

// debugLogger is only available in debug builds
var (
debugLogger *log.Logger
debugOnce sync.Once
)

func initDebugLogger() {
debugOnce.Do(func() {
debugLogger = log.New(os.Stderr, "[UTC DEBUG] ", log.Ldate|log.Ltime|log.Lshortfile)
})
}

func debugLog(format string, v ...any) {
initDebugLogger()
debugLogger.Printf(format, v...)
}
69 changes: 69 additions & 0 deletions debug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//go:build debug
// +build debug

package utc

import (
"bytes"
"os"
"strings"
"testing"
)

// TestDebugLogging verifies that debug logging works when enabled
func TestDebugLogging(t *testing.T) {
// Capture stderr
old := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

// Test nil receiver calls
var nilTime *Time

// These should log to stderr in debug mode
_ = nilTime.String()
_, _ = nilTime.Value()
_, _ = nilTime.MarshalJSON()

// Restore stderr and read output
w.Close()
os.Stderr = old

var buf bytes.Buffer
buf.ReadFrom(r)
output := buf.String()

// Verify debug logs were written
expectedLogs := []string{
"String() called on nil *Time receiver",
"Value() called on nil *Time receiver",
"MarshalJSON() called on nil *Time receiver",
}

for _, expected := range expectedLogs {
if !strings.Contains(output, expected) {
t.Errorf("Expected debug log containing %q, but got: %s", expected, output)
}
}

// Verify the format includes timestamp and file info
if !strings.Contains(output, "[UTC DEBUG]") {
t.Error("Debug logs should include [UTC DEBUG] prefix")
}
}

// TestDebugLogDirect tests the debugLog function directly
func TestDebugLogDirect(t *testing.T) {
// This test just verifies debugLog doesn't panic and initializes correctly
// The actual output verification is done in TestDebugLogging which captures
// output in a more controlled way by testing real usage patterns

// This should not panic
debugLog("test message: %s", "hello")
debugLog("another test")

// Verify debugLogger is initialized (it should be after first call)
if debugLogger == nil {
t.Error("debugLogger should be initialized after debugLog calls")
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/agentstation/utc

go 1.22.5
go 1.18
9 changes: 9 additions & 0 deletions nodebug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !debug
// +build !debug

package utc

// debugLog is a no-op in non-debug builds
func debugLog(format string, v ...any) {
// No-op in production builds
}
Loading
Loading