...

Source file src/github.com/go-asn1-ber/asn1-ber/generalizedTime.go

Documentation: github.com/go-asn1-ber/asn1-ber

     1  package ber
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"time"
     9  )
    10  
    11  // ErrInvalidTimeFormat is returned when the generalizedTime string was not correct.
    12  var ErrInvalidTimeFormat = errors.New("invalid time format")
    13  
    14  var zeroTime = time.Time{}
    15  
    16  // ParseGeneralizedTime parses a string value and if it conforms to
    17  // GeneralizedTime[^0] format, will return a time.Time for that value.
    18  //
    19  // [^0]: https://www.itu.int/rec/T-REC-X.690-201508-I/en Section 11.7
    20  func ParseGeneralizedTime(v []byte) (time.Time, error) {
    21  	var format string
    22  	var fract time.Duration
    23  
    24  	str := []byte(DecodeString(v))
    25  	tzIndex := bytes.IndexAny(str, "Z+-")
    26  	if tzIndex < 0 {
    27  		return zeroTime, ErrInvalidTimeFormat
    28  	}
    29  
    30  	dot := bytes.IndexAny(str, ".,")
    31  	switch dot {
    32  	case -1:
    33  		switch tzIndex {
    34  		case 10:
    35  			format = `2006010215Z`
    36  		case 12:
    37  			format = `200601021504Z`
    38  		case 14:
    39  			format = `20060102150405Z`
    40  		default:
    41  			return zeroTime, ErrInvalidTimeFormat
    42  		}
    43  
    44  	case 10, 12:
    45  		if tzIndex < dot {
    46  			return zeroTime, ErrInvalidTimeFormat
    47  		}
    48  		// a "," is also allowed, but would not be parsed by time.Parse():
    49  		str[dot] = '.'
    50  
    51  		// If <minute> is omitted, then <fraction> represents a fraction of an
    52  		// hour; otherwise, if <second> and <leap-second> are omitted, then
    53  		// <fraction> represents a fraction of a minute; otherwise, <fraction>
    54  		// represents a fraction of a second.
    55  
    56  		// parse as float from dot to timezone
    57  		f, err := strconv.ParseFloat(string(str[dot:tzIndex]), 64)
    58  		if err != nil {
    59  			return zeroTime, fmt.Errorf("failed to parse float: %s", err)
    60  		}
    61  		// ...and strip that part
    62  		str = append(str[:dot], str[tzIndex:]...)
    63  		tzIndex = dot
    64  
    65  		if dot == 10 {
    66  			fract = time.Duration(int64(f * float64(time.Hour)))
    67  			format = `2006010215Z`
    68  		} else {
    69  			fract = time.Duration(int64(f * float64(time.Minute)))
    70  			format = `200601021504Z`
    71  		}
    72  
    73  	case 14:
    74  		if tzIndex < dot {
    75  			return zeroTime, ErrInvalidTimeFormat
    76  		}
    77  		str[dot] = '.'
    78  		// no need for fractional seconds, time.Parse() handles that
    79  		format = `20060102150405Z`
    80  
    81  	default:
    82  		return zeroTime, ErrInvalidTimeFormat
    83  	}
    84  
    85  	l := len(str)
    86  	switch l - tzIndex {
    87  	case 1:
    88  		if str[l-1] != 'Z' {
    89  			return zeroTime, ErrInvalidTimeFormat
    90  		}
    91  	case 3:
    92  		format += `0700`
    93  		str = append(str, []byte("00")...)
    94  	case 5:
    95  		format += `0700`
    96  	default:
    97  		return zeroTime, ErrInvalidTimeFormat
    98  	}
    99  
   100  	t, err := time.Parse(format, string(str))
   101  	if err != nil {
   102  		return zeroTime, fmt.Errorf("%s: %s", ErrInvalidTimeFormat, err)
   103  	}
   104  	return t.Add(fract), nil
   105  }
   106  

View as plain text