github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/fs/time.go (about) 1 // +build linux darwin 2 3 /* 4 Copyright 2013 Google Inc. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package fs 20 21 import ( 22 "errors" 23 "fmt" 24 "math" 25 "strconv" 26 "time" 27 ) 28 29 var timeFormats = []string{ 30 time.RFC3339Nano, 31 time.RFC3339, 32 time.RFC1123Z, 33 time.RFC1123, 34 time.UnixDate, 35 time.ANSIC, 36 time.RubyDate, 37 "2006-01-02T15:04", 38 "2006-01-02T15", 39 "2006-01-02", 40 "2006-01", 41 "2006", 42 } 43 44 var errUnparseableTimestamp = errors.New("unparsable timestamp") 45 46 var powTable = []int{ 47 10e8, 48 10e7, 49 10e6, 50 10e5, 51 10e4, 52 10e3, 53 10e2, 54 10e1, 55 10, 56 1, 57 } 58 59 // Hand crafted this parser since it's a really common path. 60 func parseCanonicalTime(in string) (time.Time, error) { 61 if len(in) < 20 || in[len(in)-1] != 'Z' { 62 return time.Time{}, errUnparseableTimestamp 63 } 64 65 if !(in[4] == '-' && in[7] == '-' && in[10] == 'T' && 66 in[13] == ':' && in[16] == ':' && (in[19] == '.' || in[19] == 'Z')) { 67 return time.Time{}, fmt.Errorf("positionally incorrect: %v", in) 68 } 69 70 // 2012-08-28T21:24:35.37465188Z 71 // 4 7 10 13 16 19 72 // ----------------------------- 73 // 0-4 5 8 11 14 17 20 74 75 year, err := strconv.Atoi(in[0:4]) 76 if err != nil { 77 return time.Time{}, fmt.Errorf("error parsing year: %v", err) 78 } 79 80 month, err := strconv.Atoi(in[5:7]) 81 if err != nil { 82 return time.Time{}, fmt.Errorf("error parsing month: %v", err) 83 } 84 85 day, err := strconv.Atoi(in[8:10]) 86 if err != nil { 87 return time.Time{}, fmt.Errorf("error parsing day: %v", err) 88 } 89 90 hour, err := strconv.Atoi(in[11:13]) 91 if err != nil { 92 return time.Time{}, fmt.Errorf("error parsing hour: %v", err) 93 } 94 95 minute, err := strconv.Atoi(in[14:16]) 96 if err != nil { 97 return time.Time{}, fmt.Errorf("error parsing minute: %v", err) 98 } 99 100 second, err := strconv.Atoi(in[17:19]) 101 if err != nil { 102 return time.Time{}, fmt.Errorf("error parsing second: %v", err) 103 } 104 105 var nsecstr string 106 if in[19] != 'Z' { 107 nsecstr = in[20 : len(in)-1] 108 } 109 var nsec int 110 111 if nsecstr != "" { 112 nsec, err = strconv.Atoi(nsecstr) 113 if err != nil { 114 return time.Time{}, fmt.Errorf("error parsing nanoseconds: %v", err) 115 } 116 } 117 118 nsec *= powTable[len(nsecstr)] 119 120 return time.Date(year, time.Month(month), day, 121 hour, minute, second, nsec, time.UTC), nil 122 } 123 124 func parseTime(in string) (time.Time, error) { 125 // First, try a few numerics 126 n, err := strconv.ParseInt(in, 10, 64) 127 if err == nil { 128 switch { 129 case n > int64(math.MaxInt32)*1000: 130 // nanosecond timestamps 131 return time.Unix(n/1e9, n%1e9), nil 132 case n > int64(math.MaxInt32): 133 // millisecond timestamps 134 return time.Unix(n/1000, (n%1000)*1e6), nil 135 case n > 10000: 136 // second timestamps 137 return time.Unix(n, 0), nil 138 } 139 } 140 rv, err := parseCanonicalTime(in) 141 if err == nil { 142 return rv, nil 143 } 144 for _, f := range timeFormats { 145 parsed, err := time.Parse(f, in) 146 if err == nil { 147 return parsed, nil 148 } 149 } 150 return time.Time{}, errUnparseableTimestamp 151 }