package types
import (
"bytes"
"encoding/json"
"time"
)
type RFC3339Time struct{ time.Time }
func (t *RFC3339Time) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
t.Time = time.Time{}
return nil
}
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
parsed, err := time.Parse(time.RFC3339, s)
if err != nil {
return err
}
t.Time = parsed
return nil
}
func (t RFC3339Time) MarshalJSON() ([]byte, error) {
if t.Time.IsZero() {
return []byte("null"), nil
}
return json.Marshal(t.Time.Format(time.RFC3339))
}
Time parsing bugs are common in APIs: clients send different formats, empty strings, or timezone-less values. I like making time parsing explicit by wrapping time.Time and implementing UnmarshalJSON. The wrapper accepts RFC3339, treats null as “unset,” and rejects anything else. That gives you clear 400 errors instead of weird “zero time” values drifting into the database. I also implement MarshalJSON so the server emits a consistent format, which makes caching and client parsing easier. The key is to keep semantics tight: if a field is optional, make it a pointer or allow null; if it’s required, validate it. This pattern prevents subtle bugs in scheduling and billing systems where a single timezone mistake can cause real customer impact.
Related snips
package api
import (
"net/http"
"runtime/debug"
)
Expose build metadata for debugging deploys
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
package dbutil
import (
"context"
"github.com/jackc/pgconn"
Retry Postgres serialization failures with bounded attempts
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
timeout: 15000,
Axios API client with interceptors
Share this code
Here's the card — post it anywhere.