110 lines
3.9 KiB
Go
110 lines
3.9 KiB
Go
package toml
|
|
|
|
import (
|
|
"time"
|
|
|
|
"git.makaay.nl/mauricem/go-parsekit/parse"
|
|
"git.makaay.nl/mauricem/go-parsekit/tokenize"
|
|
)
|
|
|
|
var (
|
|
// Note: in the definitions below, the token types are chosen based on the
|
|
// formatting definitions as used by https://golang.org/src/time/format.go
|
|
|
|
// To unambiguously represent a specific instant in time, you may use an
|
|
// RFC 3339 formatted date-time with offset.
|
|
//
|
|
// odt1 = 1979-05-27T07:32:00Z
|
|
// odt2 = 1979-05-27T00:32:00-07:00
|
|
// odt3 = 1979-05-27T00:32:00.999999-07:00
|
|
//
|
|
// If you include only the time portion of an RFC 3339 formatted date-time,
|
|
// it will represent that time of day without any relation to a specific
|
|
// day or any offset or timezone.
|
|
//
|
|
// lt1 = 07:32:00
|
|
// lt2 = 00:32:00.999999
|
|
year = a.Digit.Times(4)
|
|
month = a.Digit.Times(2)
|
|
day = a.Digit.Times(2)
|
|
yyyymmdd = c.Seq(year, a.Minus, month, a.Minus, day)
|
|
dateTok = tok.Str("2006-01-02", yyyymmdd)
|
|
hour = a.Digit.Times(2)
|
|
minute = a.Digit.Times(2)
|
|
seconds = a.Digit.Times(2)
|
|
hhmmss = c.Seq(hour, a.Colon, minute, a.Colon, seconds)
|
|
timeTok = tok.Str("15:04:05", hhmmss)
|
|
|
|
// The precision of fractional seconds is implementation-specific, but at
|
|
// least millisecond precision is expected. If the value contains greater
|
|
// precision than the implementation can support, the additional precision
|
|
// must be truncated, not rounded.
|
|
micro = a.Dot.Then(c.MinMax(1, 9, a.Digit).Then(m.Drop(c.ZeroOrMore(a.Digit))))
|
|
microTok = c.Optional(tok.Str(".999999999", micro))
|
|
|
|
// For the sake of readability, you may replace the T delimiter between
|
|
// date and time with a space (as permitted by RFC 3339 section 5.6).
|
|
//
|
|
// odt4 = 1979-05-27 07:32:00Z
|
|
tdelimTok = tok.Str("T", a.Rune('T')).Or(tok.Str(" ", a.Rune(' ')))
|
|
|
|
// If you omit the offset from an RFC 3339 formatted date-time, it will
|
|
// represent the given date-time without any relation to an offset or
|
|
// timezone.
|
|
//
|
|
// ldt1 = 1979-05-27T07:32:00
|
|
// ldt2 = 1979-05-27T00:32:00.999999
|
|
//
|
|
// It cannot be converted to an instant in time without additional
|
|
// information. Conversion to an instant, if required, is
|
|
// implementation-specific.
|
|
zulu = a.Rune('Z')
|
|
offset = c.Seq(a.Runes('+', '-'), hour, a.Colon, minute)
|
|
tz = zulu.Or(offset)
|
|
tzTok = tok.Str("Z07:00", tz)
|
|
|
|
// The full date/time parse format, based on the above definitions.
|
|
// The first token denotes the type of date/time value.
|
|
// The rest of the tokens contain layout fragments for time.Parse().
|
|
offsetDateTime = tok.Str(pOffsetDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok, tzTok))
|
|
localDateTime = tok.Str(pLocalDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok))
|
|
localDate = tok.Str(pLocalDate, dateTok)
|
|
localTime = tok.Str(pLocalTime, c.Seq(timeTok, microTok))
|
|
datetime = c.Any(offsetDateTime, localDateTime, localDate, localTime)
|
|
)
|
|
|
|
func (t *parser) startDateTime(p *parse.API) {
|
|
if !p.Accept(datetime) {
|
|
p.Expected("a date and/or time")
|
|
return
|
|
}
|
|
tokens := p.Result().Tokens()
|
|
valueType := getDateTimeValueType(&tokens)
|
|
input, value, err := getDateTimeValue(&tokens)
|
|
if err == nil {
|
|
t.addParsedItem(valueType, value)
|
|
} else {
|
|
p.Error("Cannot parse value 0%s: %s", input, err)
|
|
}
|
|
}
|
|
|
|
// The first token is a token that wraps the complete date/time input.
|
|
// Its type denotes the type of date/time value that it wraps.
|
|
func getDateTimeValueType(tokens *[]*tokenize.Token) itemType {
|
|
return (*tokens)[0].Type.(itemType)
|
|
}
|
|
|
|
// The rest of the tokens contain fragments that can be used with
|
|
// time.Parse() to parse the provided date/time input. Here, these fragments
|
|
// are combined into a layout string, which is then used to parse
|
|
// the input string.
|
|
func getDateTimeValue(tokens *[]*tokenize.Token) (string, time.Time, error) {
|
|
layout := ""
|
|
for _, l := range (*tokens)[1:] {
|
|
layout += l.Type.(string)
|
|
}
|
|
input := string((*tokens)[0].Runes)
|
|
value, err := time.Parse(layout, input)
|
|
return input, value, err
|
|
}
|