func parseMinute(buff []byte, cursor *int, l int) (int, error) {
	return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrMinuteInvalid)
}
TIME-MINUTE = 2DIGIT  ; 00-59
package rfc5424
import (
	"fmt"
	"math"
	"strconv"
	"time"
	"gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
)
const (
	NILVALUE = '-'
)
var (
	ErrYearInvalid       = &syslogparser.ParserError{"Invalid year in timestamp"}
	ErrMonthInvalid      = &syslogparser.ParserError{"Invalid month in timestamp"}
	ErrDayInvalid        = &syslogparser.ParserError{"Invalid day in timestamp"}
	ErrHourInvalid       = &syslogparser.ParserError{"Invalid hour in timestamp"}
	ErrMinuteInvalid     = &syslogparser.ParserError{"Invalid minute in timestamp"}
	ErrSecondInvalid     = &syslogparser.ParserError{"Invalid second in timestamp"}
	ErrSecFracInvalid    = &syslogparser.ParserError{"Invalid fraction of second in timestamp"}
	ErrTimeZoneInvalid   = &syslogparser.ParserError{"Invalid time zone in timestamp"}
	ErrInvalidTimeFormat = &syslogparser.ParserError{"Invalid time format"}
	ErrInvalidAppName    = &syslogparser.ParserError{"Invalid app name"}
	ErrInvalidProcId     = &syslogparser.ParserError{"Invalid proc ID"}
	ErrInvalidMsgId      = &syslogparser.ParserError{"Invalid msg ID"}
	ErrNoStructuredData  = &syslogparser.ParserError{"No structured data"}
)
type Parser struct {
	buff           []byte
	cursor         int
	l              int
	header         header
	structuredData string
	message        string
}
type header struct {
	priority  syslogparser.Priority
	version   int
	timestamp time.Time
	hostname  string
	appName   string
	procId    string
	msgId     string
}
type partialTime struct {
	hour    int
	minute  int
	seconds int
	secFrac float64
}
type fullTime struct {
	pt  partialTime
	loc *time.Location
}
type fullDate struct {
	year  int
	month int
	day   int
}
func NewParser(buff []byte) *Parser {
	return &Parser{
		buff:   buff,
		cursor: 0,
		l:      len(buff),
	}
}
func (p *Parser) Location(location *time.Location) {
}
func (p *Parser) Parse() error {
	hdr, err := p.parseHeader()
	if err != nil {
		return err
	}
	p.header = hdr
	sd, err := p.parseStructuredData()
	if err != nil {
		return err
	}
	p.structuredData = sd
	p.cursor++
	if p.cursor < p.l {
		p.message = string(p.buff[p.cursor:])
	}
	return nil
}
func (p *Parser) Dump() syslogparser.LogParts {
	return syslogparser.LogParts{
		"priority":        p.header.priority.P,
		"facility":        p.header.priority.F.Value,
		"severity":        p.header.priority.S.Value,
		"version":         p.header.version,
		"timestamp":       p.header.timestamp,
		"hostname":        p.header.hostname,
		"app_name":        p.header.appName,
		"proc_id":         p.header.procId,
		"msg_id":          p.header.msgId,
		"structured_data": p.structuredData,
		"message":         p.message,
	}
}
func (p *Parser) parseHeader() (header, error) {
	hdr := header{}
	pri, err := p.parsePriority()
	if err != nil {
		return hdr, err
	}
	hdr.priority = pri
	ver, err := p.parseVersion()
	if err != nil {
		return hdr, err
	}
	hdr.version = ver
	p.cursor++
	ts, err := p.parseTimestamp()
	if err != nil {
		return hdr, err
	}
	hdr.timestamp = ts
	p.cursor++
	host, err := p.parseHostname()
	if err != nil {
		return hdr, err
	}
	hdr.hostname = host
	p.cursor++
	appName, err := p.parseAppName()
	if err != nil {
		return hdr, err
	}
	hdr.appName = appName
	p.cursor++
	procId, err := p.parseProcId()
	if err != nil {
		return hdr, nil
	}
	hdr.procId = procId
	p.cursor++
	msgId, err := p.parseMsgId()
	if err != nil {
		return hdr, nil
	}
	hdr.msgId = msgId
	p.cursor++
	return hdr, nil
}
func (p *Parser) parsePriority() (syslogparser.Priority, error) {
	return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
}
func (p *Parser) parseVersion() (int, error) {
	return syslogparser.ParseVersion(p.buff, &p.cursor, p.l)
}
func (p *Parser) parseTimestamp() (time.Time, error) {
	var ts time.Time
	if p.cursor >= p.l {
		return ts, ErrInvalidTimeFormat
	}
	if p.buff[p.cursor] == NILVALUE {
		p.cursor++
		return ts, nil
	}
	fd, err := parseFullDate(p.buff, &p.cursor, p.l)
	if err != nil {
		return ts, err
	}
	if p.cursor >= p.l || p.buff[p.cursor] != 'T' {
		return ts, ErrInvalidTimeFormat
	}
	p.cursor++
	ft, err := parseFullTime(p.buff, &p.cursor, p.l)
	if err != nil {
		return ts, syslogparser.ErrTimestampUnknownFormat
	}
	nSec, err := toNSec(ft.pt.secFrac)
	if err != nil {
		return ts, err
	}
	ts = time.Date(
		fd.year,
		time.Month(fd.month),
		fd.day,
		ft.pt.hour,
		ft.pt.minute,
		ft.pt.seconds,
		nSec,
		ft.loc,
	)
	return ts, nil
}
func (p *Parser) parseHostname() (string, error) {
	return syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
}
func (p *Parser) parseAppName() (string, error) {
	return parseUpToLen(p.buff, &p.cursor, p.l, 48, ErrInvalidAppName)
}
func (p *Parser) parseProcId() (string, error) {
	return parseUpToLen(p.buff, &p.cursor, p.l, 128, ErrInvalidProcId)
}
func (p *Parser) parseMsgId() (string, error) {
	return parseUpToLen(p.buff, &p.cursor, p.l, 32, ErrInvalidMsgId)
}
func (p *Parser) parseStructuredData() (string, error) {
	return parseStructuredData(p.buff, &p.cursor, p.l)
}
func parseFullDate(buff []byte, cursor *int, l int) (fullDate, error) {
	var fd fullDate
	year, err := parseYear(buff, cursor, l)
	if err != nil {
		return fd, err
	}
	if *cursor >= l || buff[*cursor] != '-' {
		return fd, syslogparser.ErrTimestampUnknownFormat
	}
	*cursor++
	month, err := parseMonth(buff, cursor, l)
	if err != nil {
		return fd, err
	}
	if *cursor >= l || buff[*cursor] != '-' {
		return fd, syslogparser.ErrTimestampUnknownFormat
	}
	*cursor++
	day, err := parseDay(buff, cursor, l)
	if err != nil {
		return fd, err
	}
	fd = fullDate{
		year:  year,
		month: month,
		day:   day,
	}
	return fd, nil
}
func parseYear(buff []byte, cursor *int, l int) (int, error) {
	yearLen := 4
	if *cursor+yearLen > l {
		return 0, syslogparser.ErrEOL
	}
	sub := string(buff[*cursor : *cursor+yearLen])
	*cursor += yearLen
	year, err := strconv.Atoi(sub)
	if err != nil {
		return 0, ErrYearInvalid
	}
	return year, nil
}
func parseMonth(buff []byte, cursor *int, l int) (int, error) {
	return syslogparser.Parse2Digits(buff, cursor, l, 1, 12, ErrMonthInvalid)
}
func parseDay(buff []byte, cursor *int, l int) (int, error) {
	return syslogparser.Parse2Digits(buff, cursor, l, 1, 31, ErrDayInvalid)
}
func parseFullTime(buff []byte, cursor *int, l int) (fullTime, error) {
	var loc = new(time.Location)
	var ft fullTime
	pt, err := parsePartialTime(buff, cursor, l)
	if err != nil {
		return ft, err
	}
	loc, err = parseTimeOffset(buff, cursor, l)
	if err != nil {
		return ft, err
	}
	ft = fullTime{
		pt:  pt,
		loc: loc,
	}
	return ft, nil
}
func parsePartialTime(buff []byte, cursor *int, l int) (partialTime, error) {
	var pt partialTime
	hour, minute, err := getHourMinute(buff, cursor, l)
	if err != nil {
		return pt, err
	}
	if *cursor >= l || buff[*cursor] != ':' {
		return pt, ErrInvalidTimeFormat
	}
	*cursor++
	seconds, err := parseSecond(buff, cursor, l)
	if err != nil {
		return pt, err
	}
	pt = partialTime{
		hour:    hour,
		minute:  minute,
		seconds: seconds,
	}
	if *cursor >= l || buff[*cursor] != '.' {
		return pt, nil
	}
	*cursor++
	secFrac, err := parseSecFrac(buff, cursor, l)
	if err != nil {
		return pt, nil
	}
	pt.secFrac = secFrac
	return pt, nil
}
func parseHour(buff []byte, cursor *int, l int) (int, error) {
	return syslogparser.Parse2Digits(buff, cursor, l, 0, 23, ErrHourInvalid)
}