Skip to content

8beeeaaat/ixdtf

Repository files navigation

IXDTF - RFC 9557: Internet Extended Date/Time Format Support for Go

Go Reference codecov CI

A Go implementation of RFC 9557: Internet Extended Date/Time Format (IXDTF).

IXDTF extends RFC 3339 by adding optional suffix elements for timezone names and additional metadata while maintaining full backward compatibility.

strictMode := false
rfc9557 := "2025-02-03T04:05:06+09:00[Asia/Tokyo][!u-ca=gregory]"

parsedTime, ixdtfExtensions, _ := ixdtf.Parse(rfc9557, strictMode)
// parsedTime => 2025-02-03 04:05:06 +0900 JST
// ixdtfExtensions => &ixdtf.IXDTFExtensions{Asia/Tokyo map[u-ca:gregory] map[u-ca:true]}

result, _ := ixdtf.Format(parsedTime, ixdtfExtensions)
// result is the same rfc9557

Features

  • RFC 3339 Compatible: Full backward compatibility with existing RFC 3339 date/time strings
  • Extended Format Support: Handles timezone names and additional metadata via suffix elements
  • Zero Dependencies: Pure Go implementation using only the standard library
  • Comprehensive Validation: ABNF-based validation ensuring format compliance

Installation

go get github.com/8beeeaaat/ixdtf

Quick Start

Basic Usage

package main

import (
    "fmt"
    "time"

    "github.com/8beeeaaat/ixdtf"
)

func main() {
    rfc9557InNY := "2025-01-02T03:04:05-05:00[America/New_York][!u-ca=gregorian]"
    // Parse an IXDTF string in strict mode
    parsedTime, parsedExt, err := ixdtf.Parse(rfc9557InNY, true)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Parsed Time: %v\n", parsedTime)
    // => Parsed Time: 2025-01-02 03:04:05 -0500 EST
    fmt.Printf("Extensions: %+v\n", parsedExt)
    // => &ixdtf.IXDTFExtensions{Location:America/New_York Tags:map[u-ca:gregorian] Critical:map[u-ca:true]}

    // Format a time with same extensions
    now := time.Now()
    fmt.Printf("Now: %v\n", now)
    // => Now: 2025-09-01 12:34:56.123456789 +0900 JST

    formattedNano, err := ixdtf.FormatNano(now, parsedExt)
    if err != nil {
        panic(err)
    }
    fmt.Printf("FormattedNano: %s\n", formattedNano)
    // => FormattedNano: 2025-09-01T12:34:56.123456789+09:00[America/New_York][!u-ca=gregorian]

    // Parse back the formattedNano string
    // In non-strict mode, this should succeed even if there's an offset mismatch
    reParsedTime, _, err := ixdtf.Parse(formattedNano, false)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Re:Parsed Time: %v\n", reParsedTime)
    // => Re:Parsed Time: 2025-09-01 12:34:56.123456789 +0900 JST

    // In strict mode, this should error if the offset does not match the timezone
    _, _, err = ixdtf.Parse(formattedNano, true)
    if err != nil {
        panic(err)
        // => panic: IXDTFE parsing time "2025-09-01T12:34:56.123456789+09:00[America/New_York][!u-ca=gregorian]" as "2006-01-02T15:04:05.999999999Z07:00*([time-zone-name][tags])": timezone offset does not match the specified timezone
    }

}

Validation Only

strictMode := true
err = ixdtf.Validate("2023-08-07T14:30:00+09:00[America/New_York]", strictMode)
if err != nil {
    fmt.Printf("Invalid format: %v\n", err)
}
// => Invalid format: IXDTFE parsing time "2023-08-07T14:30:00+09:00[America/New_York]" as "2006-01-02T15:04:05.999999999Z07:00*([time-zone-name][tags])": timezone offset does not match the specified timezone

IXDTF Format

IXDTF extends RFC 3339 with optional suffix elements:

<RFC3339-date-time>[<timezone-name>][<extension>...]

Examples

  • 2023-08-07T14:30:00Z - Standard RFC 3339
  • 2023-08-07T14:30:00Z[America/New_York] - With timezone name
  • 2023-08-07T14:30:00Z[u-ca=gregorian] - With Unicode calendar extension
  • 2025-02-03T04:05:06+09:00[Asia/Tokyo][!u-ca=gregory] - Multiple suffixes
  • 2023-08-07T14:30:00Z[!u-ca=gregorian] - Critical extension (must be processed)

Extension Tag Validation

IXDTF supports extension tags with specific validation rules following RFC 9557:

Validation Flow

Extension tags undergo multi-layer validation:

  1. ABNF Syntax Validation: Keys and values must conform to RFC-defined patterns
  2. Extension Type Validation:
    • Private extensions (x-*, X-*): Rejected per BCP 178
    • Experimental extensions (_*): Rejected unless specifically configured
  3. Critical Extension Processing: Extensions marked with ! must be processable or rejected

ref: https://www.rfc-editor.org/rfc/rfc9557.html#section-3.2

Rejection Cases

The following extension patterns are automatically rejected:

// Private extensions
"2023-08-07T14:30:00Z[x-custom=value]"       // Error: private extension cannot be processed

// Experimental extensions
"2023-08-07T14:30:00Z[_experimental=value]"  // Error: experimental extension cannot be processed

// Critical private/experimental extensions also
"2023-08-07T14:30:00Z[!x-custom=value]"      // Error: private extension cannot be processed
"2023-08-07T14:30:00Z[!_experimental=value]" // Error: experimental extension cannot be processed

API Reference

Core Functions

  • Parse(s string, strict bool) (time.Time, *IXDTFExtensions, error) - Parse IXDTF string
  • Format(t time.Time, ext *IXDTFExtensions) (string, error) - Format time with extensions
  • FormatNano(t time.Time, ext *IXDTFExtensions) (string, error) - Format with nanosecond precision
  • Validate(s string, strict bool) error - Validate format without parsing

Strict flag

The second argument strict in Parse / Validate controls how strictly the library enforces consistency between:

  1. the UTC offset embedded in the RFC 3339 portion, and
  2. the IANA time zone name supplied in a suffix.
Mode Behavior Example
true If the zone-derived offset for that instant differs from the RFC 3339 numeric offset, an error (ErrTimezoneOffsetMismatch) is returned. 2025-01-01T12:00:00+09:00[America/New_York] → New York at that instant is -05:00, so mismatch → error
false Mismatches do NOT produce an error. The original timestamp (its instant + numeric offset) is kept; the location is only applied if offsets match. Same example above: no error; the provided time value is kept as-is (location not applied)

Notes:

  • An invalid or unresolvable time zone name always yields an error (regardless of mode).
  • Time zones with Etc/GMT±X naming are skipped for consistency checking (POSIX inverted offset semantics would cause false positives).
  • Validate follows the same policy: with strict=false an offset mismatch is considered acceptable.
  • Extension tag syntax and critical tag handling are independent of strict.
  • Recommended usage: accept loosely formed inputs with strict=false at system boundaries (ingest phase), then re-normalize if needed; enforce strict=true where data integrity or audit requirements apply.

Types

  • IXDTFExtensions - Container for extension data
  • ParseError - Detailed parsing error with position information
  • TimezoneConsistencyResult - Timezone validation results

Error Handling

The library provides structured error types for different failure scenarios:

 _, _, err := ixdtf.Parse("invalid-date", false)
 if err != nil {
  if parseErr, ok := err.(*ixdtf.ParseError); ok {
   fmt.Printf("Parse error, %s: %s\n", parseErr.Layout, parseErr.Value)
   // => Parse error, 2006-01-02T15:04:05Z07:00: invalid-date
  }
 }

Development

Prerequisites

  • Go 1.24 or later

Building and Testing

# Run tests
go test ./...

# Run tests with race detection
go test -race ./...

# Generate coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

# Lint code (requires golangci-lint)
golangci-lint run

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes and add tests
  4. Ensure all tests pass and code is properly formatted
  5. Commit your changes (git commit -am 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Code Quality

This project maintains high code quality standards:

  • All tests must pass
  • Code coverage should not decrease
  • golangci-lint must pass with zero warnings
  • gosec security scan must pass
  • Code must be properly formatted with go fmt

License

This project is licensed under the MIT License - see the LICENSE file for details.

References

About

A Go implementation of RFC 9557 Internet Extended Date/Time Format (IXDTF)

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages