fuzzytime

package module
v0.0.0-...-05ea001 Latest Latest
Warning

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

Go to latest
Published: Oct 10, 2019 License: GPL-2.0 Imports: 6 Imported by: 30

README

fuzzytime

Build Status

A date/time parsing package for Go.

Documentation: GoDoc

Example:

func ExampleExtract() {

inputs := []string{
"Wed Apr 16 17:32:51 NZST 2014",
"2010-02-01T13:14:43Z", // an iso 8601 form
"no date or time info here",
"Published on March 10th, 1999 by Brian Credability",
"2:51pm",
}

for _, inp := range inputs {
dt := Extract(inp)
fmt.Println(dt.ISOFormat())
}

// Output:
// 2014-04-16T17:32:51+12:00
// 2010-02-01T13:14:43Z
//
// 1999-03-10
// T14:51
}

Documentation

Overview

Package fuzzytime helps with the parsing and representation of dates and times.

Fuzzytime defines types (Date, Time, DateTime) which have optional fields. So you can represent a date with a year and month, but no day.

A quick parsing example:

package main

import (
    "fmt"
    "github.com/bcampbell/fuzzytime"
)

func main() {

    inputs := []string{
        "Wed Apr 16 17:32:51 NZST 2014",
        "2010-02-01T13:14:43Z", // an iso 8601 form
        "no date or time info here",
        "Published on March 10th, 1999 by Brian Credability",
        "2:51pm",
        "April 2004",
    }

    for _, s := range inputs {
        dt, _, _ := fuzzytime.Extract(s)
        fmt.Println(dt.ISOFormat())
    }

}

This should output:

2014-04-16T17:32:51+12:00
2010-02-01T13:14:43Z

1999-03-10
T14:51
2004-04

Timezones, once resolved, are stored as an offset from UTC (in seconds).

Sometimes dates and times are ambiguous and can't be parsed without extra information (eg "dd/mm/yy" vs "mm/dd/yy"). The default behaviour when such a data is encountered is for Extract() function to just return an error. This can be overriden by using a Context struct, which provides functions to perform the decisions required in otherwise-ambiguous cases.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultContext = Context{
	DateResolver: func(a, b, c int) (Date, error) {
		return Date{}, errors.New("ambiguous date")
	},
	TZResolver: DefaultTZResolver(""),
}

DefaultContext is a predefined context which bails out if timezones or dates are ambiguous. It makes no attempt to resolve them.

View Source
var USContext = Context{
	DateResolver: MDYResolver,
	TZResolver:   DefaultTZResolver("US"),
}

USContext is a prefefined Context which opts for US timezones and mm/dd/yy dates

View Source
var WesternContext = Context{
	DateResolver: DMYResolver,
	TZResolver:   DefaultTZResolver("GB,US"),
}

WesternContext is a predefined Context which opts for UK and US timezones and dd/mm/yy dates

Functions

func DefaultTZResolver

func DefaultTZResolver(preferredLocales string) func(name string) (int, error)

DefaultTZResolver returns a TZResolver function which uses a list of country codes in preferredLocales to resolve ambigous timezones. For example, if you were expecting Bangladeshi times, then:

DefaultTZResolver("BD")

would treat "BST" as Bangladesh Standard Time rather than British Summer Time

func ExtendYear

func ExtendYear(year int) int

ExtendYear extends 2-digit years into 4 digits. the rules used: 00-69 => 2000-2069 70-99 => 1970-1999

func Extract

func Extract(s string) (DateTime, []Span, error)

Extract tries to parse a Date and Time from a string. If none found, the returned DateTime will be empty Equivalent to DefaultContext.Extract()

Example
inputs := []string{
	"Wed Apr 16 17:32:51 NZST 2014",
	"2010-02-01T13:14:43Z", // an iso 8601 form
	"no date or time info here",
	"Published on March 10th, 1999 by Brian Credability",
	"2:51pm",
}

for _, inp := range inputs {
	dt, spans, err := Extract(inp)
	if err != nil {
		panic(fmt.Errorf("Extract(%s) error: %s", inp, err))
	}
	fmt.Println(dt.ISOFormat(), spans)
}
Output:

2014-04-16T17:32:51+12:00 [{0 29}]
2010-02-01T13:14:43Z [{0 20}]
 []
1999-03-10 [{13 29}]
T14:51 [{0 6}]

func ExtractDate

func ExtractDate(s string) (Date, Span, error)

ExtractDate tries to parse a Date from a string. Equivalent to DefaultContext.ExtractDate() Returns the parsed date information and a span showing which portion of the text matched, or an error.

func ExtractTime

func ExtractTime(s string) (Time, Span, error)

ExtractTime tries to parse a Time from a string. Equivalent to DefaultContext.ExtractTime() Returns the parsed time information and a span showing which portion of the text matched, or an error.

func OffsetToTZ

func OffsetToTZ(secs int) string

OffsetToTZ converts an offset in seconds from UTC into an ISO8601-style offset (like "+HH:MM")

func TZToOffset

func TZToOffset(s string) (int, error)

TZToOffset parses an ISO8601 timezone offset ("Z", "[+-]HH" "[+-]HH[:]?MM" etc...) and returns the offset from UTC in seconds

Types

type Context

type Context struct {
	// DateResolver is called when ambigous dates are encountered eg (10/11/12)
	// It should return a date, if one can be decided. Returning an error
	// indicates the resolver can't decide.
	DateResolver func(a, b, c int) (Date, error)
	// TZResolver returns the offset in seconds from UTC of the named zone (eg "EST").
	// if the resolver can't decide which timezone it is, it will return an error.
	TZResolver func(name string) (int, error)
}

Context provides helper functions to resolve ambiguous dates and timezones. For example, "CST" can mean China Standard Time, Central Standard Time in or Central Standard Time in Australia. Or, the date "5/2/10". It could Feburary 5th, 2010 or May 2nd 2010. Or even Feb 10th 2005, depending on country. Even "05/02/2010" is ambiguous. If you know something about the types of times and dates you're likely to encounter, you can provide a Context struct to guide the parsing.

Example
inputs := []string{
	"01/02/03",
	"12/23/99",
	"10:25CST",
}
// USA context:
fmt.Println("in USA:")
for _, inp := range inputs {
	dt, _, _ := USContext.Extract(inp)
	fmt.Println(dt.ISOFormat())
}

// custom context for Australia:
aussie := Context{
	DateResolver: DMYResolver,
	TZResolver:   DefaultTZResolver("AU"),
}

fmt.Println("in Australia:")
for _, inp := range inputs {
	dt, _, _ := aussie.Extract(inp)
	fmt.Println(dt.ISOFormat())
}
Output:

in USA:
2003-01-02
1999-12-23
T10:25-06:00
in Australia:
2003-02-01

T10:25+09:30

func (*Context) Extract

func (ctx *Context) Extract(s string) (DateTime, []Span, error)

Extract tries to parse a Date and Time from a string It also returns a sorted list of spans specifing which bits of the string were used. If none found (or if there is an error), the returned DateTime will be empty.

func (*Context) ExtractDate

func (ctx *Context) ExtractDate(s string) (Date, Span, error)

ExtractDate tries to parse a date from a string. It returns a Date and Span indicating which part of string matched. If an error occurs, an empty Date will be returned.

func (*Context) ExtractTime

func (ctx *Context) ExtractTime(s string) (Time, Span, error)

ExtractTime tries to parse a time from a string. It returns a Time and a Span indicating which part of string matched. Time and Span may be empty, indicating no time was found. An error will be returned if a time is found but cannot be correctly parsed. If error is not nil time the returned time and span will both be empty

type Date

type Date struct {
	// contains filtered or unexported fields
}

A Date represents a year/month/day set where any of the three may be unset. default initialisation (ie Date{}) is a valid but empty Date.

func DMYResolver

func DMYResolver(a, b, c int) (Date, error)

DMYResolver is a helper function for Contexts which treats ambiguous dates as DD/MM/YY

func MDYResolver

func MDYResolver(a, b, c int) (Date, error)

MDYResolver is a helper function for Contexts which treats ambiguous dates as MM/DD/YY

func NewDate

func NewDate(y, m, d int) *Date

NewDate creates a Date with all fields set

func (*Date) Conflicts

func (d *Date) Conflicts(other *Date) bool

Conflicts returns true if date d conflicts with the other date. Missing fields are not considered so, for example "2013-01-05" doesn't conflict with "2013-01"

func (*Date) Day

func (d *Date) Day() int

Day returns the day (result undefined if field unset)

func (*Date) Empty

func (d *Date) Empty() bool

Empty tests if date is blank (ie all fields unset)

func (*Date) Equals

func (d *Date) Equals(other *Date) bool

Equals returns true if dates match. Fields present in one date but not the other are considered mismatches.

func (*Date) HasDay

func (d *Date) HasDay() bool

HasDay returns trus if the day is set

func (*Date) HasMonth

func (d *Date) HasMonth() bool

HasMonth returns true if the month is set

func (*Date) HasYear

func (d *Date) HasYear() bool

HasYear returns true if the year is set

func (*Date) ISOFormat

func (d *Date) ISOFormat() string

ISOFormat returns "YYYY-MM-DD", "YYYY-MM" or "YYYY" depending on which fields are available (or "" if year is missing).

func (*Date) Merge

func (d *Date) Merge(other *Date)

Merge copies all fields set in other into d. any fields unset in other are left unchanged in d.

func (*Date) Month

func (d *Date) Month() int

Month returns the month (result undefined if field unset)

func (*Date) SetDay

func (d *Date) SetDay(day int)

SetDay sets the day field

func (*Date) SetMonth

func (d *Date) SetMonth(month int)

SetMonth sets the month field

func (*Date) SetYear

func (d *Date) SetYear(year int)

SetYear sets the year field

func (*Date) String

func (d *Date) String() string

String returns "YYYY-MM-DD" with question marks in place of any missing values

func (*Date) Year

func (d *Date) Year() int

Year returns the year (result undefined if field unset)

type DateTime

type DateTime struct {
	Date
	Time
}

DateTime represents a set of fields for date and time, any of which may be unset. The default initialisation is a valid empty datetime with no fields set.

func (*DateTime) Conflicts

func (dt *DateTime) Conflicts(other *DateTime) bool

Conflicts returns true if the two datetimes conflict. Note that this is not the same as the two being equal - one datetime can be more precise than the other. They are only in conflict if they have different values set for the same field. eg "2012-01-01T03:34:10" doesn't conflict with "03:34"

func (*DateTime) Empty

func (dt *DateTime) Empty() bool

Empty tests if datetime is blank (ie all fields unset)

func (*DateTime) Equals

func (dt *DateTime) Equals(other *DateTime) bool

Equals returns true if dates and times match

func (*DateTime) HasFullDate

func (dt *DateTime) HasFullDate() bool

HasFullDate returns true if Year, Month and Day are all set

func (*DateTime) ISOFormat

func (dt *DateTime) ISOFormat() string

ISOFormat returns the most precise-possible datetime given the available data. Aims for "YYYY-MM-DDTHH:MM:SS+TZ" but will drop off higher-precision components as required eg "YYYY-MM"

func (*DateTime) String

func (dt *DateTime) String() string

String returns "YYYY-MM-DD hh:mm:ss tz" with question marks in place of any missing values (except for timezone, which will be blank if missing)

type Span

type Span struct {
	Begin int
	End   int
}

Span represents the range [Begin,End), used to indicate the part of a string from which time or date information was parsed.

type TZInfo

type TZInfo struct {
	// Name of the timezone eg "BST", "UTC", "NZDT"
	Name string
	// Offset from UTC, in ISO8601 form [+-]<HH>[:<MM>]
	Offset string
	// Locale contains comma-separated country identifiers
	// to help resolve ambiguities
	Locale string
}

TZInfo holds info about a timezone offset

func FindTimeZone

func FindTimeZone(name string) []TZInfo

FindTimeZone returns timezones with the matching name (eg "BST") Some timezone names are ambiguous (eg "BST"), so all the matching ones will be returned. It's up to the caller to disambiguate them. To aid in this, ambiguous timezones include a list of country locale codes ("US", "AU" etc) in where they are used.

type Time

type Time struct {
	// contains filtered or unexported fields
}

Time represents a set of time fields, any of which may be unset. The default initialisation (ie Time{}) produces a Time with all fields unset.

func (*Time) Conflicts

func (t *Time) Conflicts(other *Time) bool

Conflicts returns true if time t conflicts with the other time. Missing fields are not considered so, for example "10:59:01" doesn't conflict with "10:59"

func (*Time) Empty

func (t *Time) Empty() bool

Empty tests if time is blank (ie all fields unset)

func (*Time) Equals

func (t *Time) Equals(other *Time) bool

Equals returns true if the two times have the same fields set and match exactly. Fields present in one time but not the other are considered mismatches.

func (*Time) Fractional

func (t *Time) Fractional() int

Fractional returns the second (result undefined if field unset)

func (*Time) HasFractional

func (t *Time) HasFractional() bool

HasFractional returns true if fraction second is set

func (*Time) HasHour

func (t *Time) HasHour() bool

HasHour returns true if the hour is set

func (*Time) HasMinute

func (t *Time) HasMinute() bool

HasMinute returns true if the minute is set

func (*Time) HasSecond

func (t *Time) HasSecond() bool

HasSecond returns true if the second is set

func (*Time) HasTZOffset

func (t *Time) HasTZOffset() bool

HasTZOffset returns true if the timezone offset field is set

func (*Time) Hour

func (t *Time) Hour() int

Hour returns the hour (result undefined if field unset)

func (*Time) ISOFormat

func (t *Time) ISOFormat() string

ISOFormat returns the most precise possible ISO-formatted time

func (*Time) Minute

func (t *Time) Minute() int

Minute returns the minute (result undefined if field unset)

func (*Time) Second

func (t *Time) Second() int

Second returns the second (result undefined if field unset)

func (*Time) SetFractional

func (t *Time) SetFractional(fractional int)

SetFractional sets the Fractional Second field (0-999)

func (*Time) SetHour

func (t *Time) SetHour(hour int)

SetHour sets the hour (0-23)

func (*Time) SetMinute

func (t *Time) SetMinute(minute int)

SetMinute sets the Minute field (0-59)

func (*Time) SetSecond

func (t *Time) SetSecond(second int)

SetSecond sets the Second field (0-59)

func (*Time) SetTZOffset

func (t *Time) SetTZOffset(tzOffset int)

SetTZOffset sets the timezone offset from UTC, in seconds

func (*Time) String

func (t *Time) String() string

String returns "hh:mm:ss.fff+tz", with question marks in place of any missing values (except for timezone and fractional seconds which will be blank if missing)

func (*Time) TZOffset

func (t *Time) TZOffset() int

TZOffset returns the offset from UTC, in seconds. Result undefined if field is unset.

Jump to

Keyboard shortcuts

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