1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
// Copyright 2015 Rick Beton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package clock
import (
"fmt"
"runtime"
"strconv"
"strings"
)
// MustParse is as per Parse except that it panics if the string cannot be parsed.
// This is intended for setup code; don't use it for user inputs.
func MustParse(hms string) Clock {
t, err := Parse(hms)
if err != nil {
panic(err)
}
return t
}
// Parse converts a string representation to a Clock. Acceptable representations
// are as per ISO-8601 - see https://en.wikipedia.org/wiki/ISO_8601#Times
//
// Also, conventional AM- and PM-based strings are parsed, such as "2am", "2:45pm".
// Remember that 12am is midnight and 12pm is noon.
func Parse(hms string) (clock Clock, err error) {
if strings.HasSuffix(hms, "am") || strings.HasSuffix(hms, "AM") {
return parseAmPm(hms, 0)
} else if strings.HasSuffix(hms, "pm") || strings.HasSuffix(hms, "PM") {
return parseAmPm(hms, 12)
}
return parseISO(hms)
}
func parseISO(hms string) (clock Clock, err error) {
switch len(hms) {
case 2: // HH
return parseClockParts(hms, hms, "", "", "", 0, 0)
case 4: // HHMM
return parseClockParts(hms, hms[:2], hms[2:], "", "", 0, 0)
case 5: // HH:MM
if hms[2] != ':' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, hms[:2], hms[3:], "", "", 0, 0)
case 6: // HHMMSS
return parseClockParts(hms, hms[:2], hms[2:4], hms[4:], "", 0, 0)
case 8: // HH:MM:SS
if hms[2] != ':' || hms[5] != ':' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, hms[:2], hms[3:5], hms[6:], "", 0, 0)
case 9, 10: // HH:MM:SS.0
if hms[2] != ':' || hms[5] != ':' || hms[8] != '.' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, hms[:2], hms[3:5], hms[6:8], hms[9:]+"00", 0, 0)
case 11: // HH:MM:SS.00
if hms[2] != ':' || hms[5] != ':' || hms[8] != '.' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, hms[:2], hms[3:5], hms[6:8], hms[9:]+"0", 0, 0)
case 12: // HH:MM:SS.000
if hms[2] != ':' || hms[5] != ':' || hms[8] != '.' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, hms[:2], hms[3:5], hms[6:8], hms[9:], 0, 0)
}
return 0, parseError(hms, nil)
}
func parseAmPm(hms string, offset int) (clock Clock, err error) {
n := len(hms)
switch len(hms) {
case 3: // Ham
return parseClockParts(hms, "0"+hms[:1], "", "", "", 12, offset)
case 4: // HHam
return parseClockParts(hms, hms[:2], "", "", "", 12, offset)
}
colon := strings.IndexByte(hms, ':')
if colon < 0 {
return 0, parseError(hms, nil)
}
h := hms[:colon]
rest := hms[colon+1 : n-2]
switch len(rest) {
case 2: // MM
return parseClockParts(hms, h, rest, "", "", 12, offset)
case 5: // MM:SS
if rest[2] != ':' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, h, rest[:2], rest[3:], "", 12, offset)
case 6, 7: // MM:SS.0xm
if rest[2] != ':' || rest[5] != '.' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, h, rest[:2], rest[3:5], rest[6:]+"00", 12, offset)
case 8: // MM:SS.00xm
if rest[2] != ':' || rest[5] != '.' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, h, rest[:2], rest[3:5], rest[6:]+"0", 12, offset)
case 9: // MM:SS.000xm
if rest[2] != ':' || rest[5] != '.' {
return 0, parseError(hms, nil)
}
return parseClockParts(hms, h, rest[:2], rest[3:5], rest[6:], 12, offset)
}
return 0, parseError(hms, nil)
}
func parseClockParts(hms, hh, mm, ss, mmms string, mod, offset int) (clock Clock, err error) {
h := 0
m := 0
s := 0
ms := 0
if hh != "" {
h, err = strconv.Atoi(hh)
if err != nil {
return 0, parseError(hms, err)
}
}
if mm != "" {
m, err = strconv.Atoi(mm)
if err != nil {
return 0, parseError(hms, err)
}
}
if ss != "" {
s, err = strconv.Atoi(ss)
if err != nil {
return 0, parseError(hms, err)
}
}
if mmms != "" {
ms, err = strconv.Atoi(mmms)
if err != nil {
return 0, parseError(hms, err)
}
}
if mod > 0 {
h = h % mod
}
return New(h+offset, m, s, ms), nil
}
func parseError(hms string, err error) error {
_, _, line, _ := runtime.Caller(1)
if err != nil {
return fmt.Errorf("parse.go:%d: clock.Clock: cannot parse %s: %v", line, hms, err)
}
return fmt.Errorf("parse.go:%d: clock.Clock: cannot parse %s", line, hms)
}
|