diff --git a/LICENSE.txt b/LICENSE.txt index 2c7cc592..ccbe5c69 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -11,29 +11,3 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -timefuncs license ----------------------------------------------------------------------- -Copyright (c) 2013, Sevki Hasirci -All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmd/ledger/print.go b/cmd/ledger/print.go index c152cfe4..d4829972 100644 --- a/cmd/ledger/print.go +++ b/cmd/ledger/print.go @@ -8,6 +8,7 @@ import ( "time" "unicode/utf8" + "github.com/hako/durafmt" "github.com/howeyc/ledger" ) @@ -32,8 +33,8 @@ func PrintStats(generalLedger []*ledger.Transaction) { days := math.Floor(endDate.Sub(startDate).Hours() / 24) - fmt.Printf("%-25s : %s to %s (%s)\n", "Transactions span", startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), DurationInWords(endDate.Sub(startDate))) - fmt.Printf("%-25s : %s\n", "Since last post", DurationInWords(time.Since(endDate))) + fmt.Printf("%-25s : %s to %s (%s)\n", "Transactions span", startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), durafmt.Parse(endDate.Sub(startDate)).String()) + fmt.Printf("%-25s : %s\n", "Since last post", durafmt.ParseShort(time.Since(endDate)).String()) fmt.Printf("%-25s : %d (%.1f per day)\n", "Transactions", len(generalLedger), float64(len(generalLedger))/days) fmt.Printf("%-25s : %d\n", "Payees", len(payees)) fmt.Printf("%-25s : %d\n", "Referenced Accounts", len(accounts)) diff --git a/cmd/ledger/timefuncs.go b/cmd/ledger/timefuncs.go deleted file mode 100644 index fb6dd5b8..00000000 --- a/cmd/ledger/timefuncs.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013 Sevki Hasirci . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Original location: "github.com/sevki/fathertime" - -package main - -import ( - "fmt" - "time" -) - -const lssthnd = "less than %d %s" -const lssthns = "less than a %s" -const aboutnd = "about %d %s" -const day time.Duration = 86400000000000 -const month time.Duration = 2628000000001209 -const year time.Duration = 31535999999964780 - -/* -DurationInWords returns duration in words. -*/ -func DurationInWords(d time.Duration) string { - - if d >= time.Second && d <= (time.Second*4) { - return fmt.Sprintf(lssthnd, 5, "seconds") - } else if d >= (time.Second*5) && d < (time.Second*10) { - return fmt.Sprintf(lssthnd, 10, "seconds") - } else if d >= (time.Second*10) && d < (time.Second*20) { - return fmt.Sprintf(lssthnd, 20, "seconds") - } else if d >= (time.Second*20) && d < (time.Second*40) { - return "half a minute" - } else if d >= (time.Second*40) && d < (time.Second*60) { - return fmt.Sprintf(lssthns, "minute") - } else if d >= (time.Second*60) && d < time.Minute+(time.Second*30) { - return "1 minute" - } else if d >= time.Minute+(time.Second*30) && d < (time.Minute*44)+(time.Second*30) { - return fmt.Sprintf("%d minutes", (d / time.Minute)) - } else if d >= (time.Minute*44)+(time.Second*30) && d < (time.Minute*89)+(time.Second*30) { - return fmt.Sprintf(aboutnd, d/time.Hour, "hour") - } else if d >= (time.Minute*89)+(time.Second*30) && d < (time.Hour*29)+(time.Minute*59)+(time.Second*30) { - return fmt.Sprintf(aboutnd, (d / time.Hour), "hours") - } else if d >= (time.Hour*23)+(time.Minute*59)+(time.Second*30) && d < (time.Hour*41)+(time.Minute*59)+(time.Second*30) { - return "1 day" - } else if d >= (time.Hour*41)+(time.Minute*59)+(time.Second*30) && d < (day*29)+(time.Hour*23)+(time.Minute*59)+(time.Second*30) { - return fmt.Sprintf("%d days", d/(time.Hour*24)) - } else if d >= (day*29)+(time.Hour*23)+(time.Minute*59)+(time.Second*30) && d < (day*59)+(time.Hour*23)+(time.Minute*59)+(time.Second*30) { - return fmt.Sprintf(aboutnd, 1, "month") - } else if d >= (day*59)+(time.Hour*23)+(time.Minute*59)+(time.Second*30) && d < (year) { - return fmt.Sprintf(aboutnd, d/month+1, "months") - } else if d >= year && d < year+(3*month) { - return fmt.Sprintf(aboutnd, 1, "year") - } else if d >= year+(3*month) && d < year+(9*month) { - return "over 1 year" - } else if d >= year+(9*month) && d < (year*2) { - return "almost 2 years" - } else { - return fmt.Sprintf(aboutnd, d/year, "years") - } -} diff --git a/go.mod b/go.mod index 387f2b68..866ae37a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.15 require ( github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab + github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/jbrukh/bayesian v0.0.0-20200318221351-d726b684ca4a + github.com/joyt/godate v0.0.0-20150226210126-7151572574a7 github.com/juztin/numeronym v0.0.0-20160223091026-859fcc2918e2 github.com/lucasb-eyer/go-colorful v1.0.3 github.com/marcmak/calc v0.0.0-20150509200512-5bbbfc3b3149 diff --git a/go.sum b/go.sum index ce12644c..4987323c 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,15 @@ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ= +github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE= github.com/jbrukh/bayesian v0.0.0-20200318221351-d726b684ca4a h1:gbdjhSslIoRRiSSLCP3kKuLmqAJGmhnPVhIyf6Dbw34= github.com/jbrukh/bayesian v0.0.0-20200318221351-d726b684ca4a/go.mod h1:SELxwZQq/mPnfPCR2mchLmT4TQaPJvYtLcCtDWSM7vM= +github.com/joyt/godate v0.0.0-20150226210126-7151572574a7 h1:2wH5antjhmU3EuWyidm0lJ4B9hGMpl5lNRo+M9uGJ5A= +github.com/joyt/godate v0.0.0-20150226210126-7151572574a7/go.mod h1:R+UgFL3iylLhx9N4w35zZ2HdhDlgorRDx4SxbchWuN0= github.com/juztin/numeronym v0.0.0-20160223091026-859fcc2918e2 h1:jrs0oyU9XY7MlTHbNxecqFgY+fgEENZdP4Z8FZln/pw= github.com/juztin/numeronym v0.0.0-20160223091026-859fcc2918e2/go.mod h1:uVDl4OnjvPk07IzoXF/dFM7nBYqAKdJsz4e9xjjWo7Q= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= diff --git a/internal/github.com/joyt/godate/LICENSE b/internal/github.com/joyt/godate/LICENSE deleted file mode 100644 index 9689ae2b..00000000 --- a/internal/github.com/joyt/godate/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Joy Tao - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/internal/github.com/joyt/godate/README.md b/internal/github.com/joyt/godate/README.md deleted file mode 100644 index 8fa9f2fc..00000000 --- a/internal/github.com/joyt/godate/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# godate -Golang package for intelligently parsing date strings like javascript's Date.parse() and getting the layout of date strings. -Fully compatible with the native time package. - -This package is still under development. - -# Usage -### Installation -``` -go get github.com/joyt/godate -``` - -In your program: -```go -var timestamp time.Time -var err error -timestamp, err = date.Parse("Mar 14 2003") -``` - -## Example -```go -import ( - "github.com/joyt/godate" - "time" - "fmt" -) - -func main() { - var t time.Time - - t = date.MustParse("Aug 31 1999") - fmt.Println(t) - // Prints 1999-08-31 00:00:00 +0000 UTC - - t = date.MustParse("Tuesday, August 31, 1999") - fmt.Println(t) - // Prints 1999-08-31 00:00:00 +0000 UTC - - t = date.MustParse("Tue 31 Aug '99") - fmt.Println(t) - // Prints 1999-08-31 00:00:00 +0000 UTC - - t = date.MustParse("08/31/1999") - fmt.Println(t) - // Prints 1999-08-31 00:00:00 +0000 UTC - - t = date.MustParse("8/31/1999 20:05") - fmt.Println(t) - // Prints 1999-08-31 21:05:00 +0000 UTC - - t = date.MustParse("31/08/1999 8:05pm") - fmt.Println(t) - // Prints 1999-08-31 21:05:00 +0000 UTC - - t = date.MustParse("8/31/1999 8:05PM EST") - fmt.Println(t) - // Prints 1999-08-31 21:05:00 -0400 EDT - - t = date.MustParse("Aug-1999") - fmt.Println(t) - // Prints 1999-08-01 00:00:00 +0000 UTC -} -``` - -# Notes - -The parser is extremely lenient, and will try to interpret whatever it is given as a date as much as possible. - -In cases where the meaning of the date is ambiguous (such as 6/09, which could mean Jun 9th or Jun 2009), the parser generally defaults to the higher resolution date (Jun 9th). An exception is made for dates without separators such as "200609", where the parser will always try to assume the year is first (200609 -> Sep 2006, NOT Jun 20th 2009). diff --git a/internal/github.com/joyt/godate/godate.go b/internal/github.com/joyt/godate/godate.go deleted file mode 100644 index 43b8f005..00000000 --- a/internal/github.com/joyt/godate/godate.go +++ /dev/null @@ -1,451 +0,0 @@ -package date - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strings" - "time" -) - -const ( - formatyyyyMMdd = "__06.01._2" - formatyyyyMdd = "__06.1._2" - formatyyyyMMMdd = "__06.Jan._2" - formatyyyyMMMMdd = "__06.January._2" - formatMMddyyyy = "01._2.__06" - formatMddyyyy = "1._2.__06" - formatMMMddyyyy = "Jan._2.__06" - formatMMMMddyyyy = "January._2.__06" - formatddMMyyyy = "_2.01.__06" - formatddMyyyy = "_2.1.__06" - formatddMMMyyyy = "_2.Jan.__06" - formatddMMMMyyyy = "_2.January.__06" - - formatyyyyMM = "__06.01" - formatyyyyM = "__06.1" - formatyyyyMMM = "__06.Jan" - formatyyyyMMMM = "__06.January" - formatMMyyyy = "01.__06" - formatMyyyy = "1.__06" - formatMMMyyyy = "Jan.__06" - formatMMMMyyyy = "January.__06" - - formatMMdd = "01._2" // Always chosen over ddMM if both are possible. - formatMdd = "1._2" // Always chosen over ddMM if both are possible. - formatMMMdd = "Jan._2" - formatMMMMdd = "January._2" - formatddMM = "_2.01" - formatddM = "_2.1" - formatddMMM = "_2.Jan" - formatddMMMM = "_2.January" - - formatHHmmss = "15:04:05" - formatHHmm = "15:04" - formathhmmssa = "03:04:05pm" - formathhmma = "03:04pm" - formathmmssa = "3:04:05pm" - formathmma = "3:04pm" - - formatzzz = "MST" - formatZZZ = "-0700" - formatZZZZ = "GMT-07:00" - // TODO: Handle Z format timezones outside of standard. - - formatEEE = "Mon" - formatEEEE = "Monday" -) - -var ( - separatorRegex = regexp.MustCompile("[^a-zA-Z0-9]+") - timezoneRegex = regexp.MustCompile("[A-Z]{2,4}T") - timezoneNumericRegex = regexp.MustCompile("((GMT)|Z)?[-+][0-9]{2}:?[0-9]{2}") - - // TODO: Nov and Mar may conflict with some timezones. - months = []string{"january", "februrary", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"} - monthsShort = []string{"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"} - daysOfWeekShort = []string{"mon", "tue", "wed", "thu", "fri", "sat", "sun"} - daysOfWeek = []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"} - - standardDateFormats = []string{ - "2006/01/02", - "2006-01-02", - time.RFC1123, - time.RFC1123Z, - time.RFC3339, - time.RFC3339Nano, - time.RFC822, - time.RFC822Z, - time.RFC850, - time.RubyDate, - time.UnixDate, - time.Kitchen, - time.ANSIC, - time.Stamp, - time.StampMilli, - time.StampMicro, - time.StampNano, - } - dateFormatsWithShortMonth = []string{ - formatyyyyMMMdd, - formatMMMddyyyy, - formatddMMMyyyy, - formatMMMdd, - formatddMMM, - formatMMMyyyy, - formatyyyyMMM, - } - dateFormatsWithLongMonth = []string{ - formatyyyyMMMMdd, - formatMMMMddyyyy, - formatddMMMMyyyy, - formatMMMMdd, - formatddMMMM, - formatMMMMyyyy, - formatyyyyMMMM, - } - dateFormatsNumeric = []string{ - formatMddyyyy, - formatMMddyyyy, - formatddMMyyyy, - formatddMyyyy, - formatyyyyMMdd, - formatyyyyMdd, - formatMMdd, - formatMdd, - formatddMM, - formatddM, - formatMMyyyy, - formatMyyyy, - formatyyyyMM, - formatyyyyM, - } - dateFormatsNumericYearFirst = []string{ - formatyyyyMM, - formatyyyyM, - formatyyyyMMdd, - formatyyyyMdd, - formatMddyyyy, - formatMMddyyyy, - formatddMMyyyy, - formatddMyyyy, - formatMMyyyy, - formatMyyyy, - formatMMdd, - formatMdd, - formatddMM, - formatddM, - } - timeFormats = []string{ - formatHHmmss, - formatHHmm, - formathhmmssa, - formathhmma, - formathmmssa, - formathmma, - } - timeZoneFormats = []string{ - formatzzz, - formatZZZ, - formatZZZZ, - } -) - -func init() { - timezoneRegex.Longest() - timezoneNumericRegex.Longest() - separatorRegex.Longest() -} - -// Parse attempts to parse this string as a timestamp, returning an error -// if it cannot. Example inputs: "July 9 1977", "07/9/1977 5:03pm". -// Assumes UTC if a timezone is not provided. -func Parse(s string) (time.Time, error) { - d, _, err := ParseAndGetLayout(s) - return d, err -} - -// MustParse is like Parse except it panics if the string is not parseable. -func MustParse(s string) time.Time { - d, err := Parse(s) - if err != nil { - panic(err) - } - return d -} - -// ParseInLocation is like Parse except it uses the given location when parsing the date. -func ParseInLocation(s string, loc *time.Location) (time.Time, error) { - _, l, err := ParseAndGetLayout(s) - if err != nil { - return time.Time{}, err - } - t, err := time.ParseInLocation(l, s, loc) - return t, err -} - -// ParseAndGetLayout attempts to parse this string as a timestamp -// and if successful, returns the timestamp and the layout of the -// string. -func ParseAndGetLayout(date string) (time.Time, string, error) { - if len(strings.TrimSpace(date)) == 0 { - return time.Time{}, "", errors.New("Empty string cannot be parsed to date") - } - // Check standard date formats first. - for _, f := range standardDateFormats { - if t, err := time.Parse(f, date); err == nil { - return t, f, nil - } - } - s := strings.ToLower(date) - layout := &bytes.Buffer{} - prefix := getPrefix(s) - layout.WriteString(prefix) - s = strings.TrimPrefix(s, prefix) - - // Check for day of week. - for _, d := range daysOfWeek { - if strings.HasPrefix(s, d) { - s = strings.TrimPrefix(s, d) - layout.WriteString(formatEEEE) - } - } - for _, d := range daysOfWeekShort { - if strings.HasPrefix(s, d) { - s = strings.TrimPrefix(s, d) - layout.WriteString(formatEEE) - } - } - - // Get rid of prefix and suffix. - prefix = getPrefix(s) - separators := separatorRegex.FindAllStringSubmatch(s, -1) - if len(prefix) > 0 { - s = strings.TrimPrefix(s, prefix) - layout.WriteString(prefix) - separators = separators[1:] - } - var suffix string - if len(separators) > 0 && strings.HasSuffix(s, separators[len(separators)-1][0]) { - suffix = separators[len(separators)-1][0] - s = strings.TrimSuffix(s, suffix) - separators = separators[:len(separators)-1] - } - - // Narrow down formats needed to check. - // TODO: Make more efficient by checking fewer formats variations. - var formats []string - containsTime := containsTime(date) - containsTimezone := containsTimezone(date) - var onlyTime bool - if containsLongMonth(s) { - formats = dateFormatsWithLongMonth - } else if containsShortMonth(s) { - formats = dateFormatsWithShortMonth - } else if (len(separators) <= 3 && containsTime && containsTimezone) || (len(separators) <= 2 && containsTime) { - if containsTimezone { - formats = getCombinations(timeFormats, timeZoneFormats, false) - } else { - formats = timeFormats - } - onlyTime = true - } else if len(separators) == 0 { - // If the date is all munged together, assume year is first rather than month. - formats = dateFormatsNumericYearFirst - } else { - formats = dateFormatsNumeric - } - if containsTimezone { - // time.Parse only accepts uppercase timezones names, so need to convert back. - if tz := timezoneRegex.FindStringSubmatch(date); len(tz) > 0 { - s = strings.Replace(s, strings.ToLower(tz[0]), tz[0], -1) - } - } - - // Check possible formats. - for _, f := range formats { - variations := getVariations(f, containsTime && !onlyTime, containsTimezone && !onlyTime) - var correct string - for _, v := range variations { - if strings.Contains(v, ":") { - v = strings.Replace(v, ":", ".", -1) - } - l := formatWithSeparators(v, separators) - if _, err := time.Parse(l, s); err == nil { - correct = l - break - } - } - if len(correct) > 0 { - layout.WriteString(correct) - break - } - } - layout.WriteString(suffix) - - // Return Date and format. - date = strings.Replace(date, "AM", "am", -1) - date = strings.Replace(date, "PM", "pm", -1) - t, err := time.Parse(layout.String(), date) - if err != nil { - return time.Time{}, "", err - } - return t, layout.String(), err -} - -// Layout returns the layout of this date, appropriate for use -// with the Go time package, for example "Jan 02 2006". -// See http://golang.org/pkg/time/ for more examples. -func Layout(s string) string { - _, l, err := ParseAndGetLayout(s) - if err == nil { - return l - } - return "" -} - -// LayoutUnicode returns the layout of this date according to the -// Unicode standard: http://www.unicode.org/reports/tr35/tr35-19.html#Date_Format_Patterns -// NOT TESTED. -func LayoutUnicode(s string) string { - _, l, err := ParseAndGetLayout(s) - if err == nil { - return ConvertGoLayoutToUnicode(l) - } - return "" -} - -// ConvertGoLayoutToUnicode converts the given time layout string -// to on using the Unicode standard prescribed in -// http://www.unicode.org/reports/tr35/tr35-19.html#Date_Format_Patterns -// NOT TESTED. -func ConvertGoLayoutToUnicode(layout string) string { - // Year - layout = strings.Replace(layout, "20", "yy", -1) - layout = strings.Replace(layout, "06", "yy", -1) - - // Month - layout = strings.Replace(layout, "January", "MMMM", -1) - layout = strings.Replace(layout, "Jan", "MMM", -1) - layout = strings.Replace(layout, "01", "MM", -1) - layout = strings.Replace(layout, "1", "M", -1) - - // Day - layout = strings.Replace(layout, "02", "dd", -1) - layout = strings.Replace(layout, "_2", "d", -1) - - // Weekday - layout = strings.Replace(layout, "Mon", "EEE", -1) - layout = strings.Replace(layout, "Monday", "EEEE", -1) - - // Hour - layout = strings.Replace(layout, "03", "hh", -1) - layout = strings.Replace(layout, "3", "h", -1) - layout = strings.Replace(layout, "15", "HH", -1) - layout = strings.Replace(layout, "PM", "a", -1) - - // Minute - layout = strings.Replace(layout, "04", "mm", -1) - - // Second - layout = strings.Replace(layout, "05", "ss", -1) - - // Timezone - layout = strings.Replace(layout, "MST", "zzz", -1) - layout = strings.Replace(layout, "-0700", "ZZZ", -1) - layout = strings.Replace(layout, "GMT-07:00", "ZZZZ", -1) - - return layout -} - -// Private Methods - -func getVariations(f string, includeTime, includeTimezone bool) []string { - var v []string - if strings.Contains(f, "__") { - v = []string{strings.Replace(f, "__", "20", 1), strings.Replace(f, "__", "", 1)} - } else { - v = []string{f} - } - l := len(v) - for i := 0; i < l; i++ { - if strings.Contains(v[i], "_") { - v = append(v, strings.Replace(v[i], "_", "0", -1)) - } - } - if includeTime { - if includeTimezone { - times := getCombinations(timeFormats, timeZoneFormats, false) - v = getCombinations(v, times, true) - } else { - v = getCombinations(v, timeFormats, true) - } - } - return v -} - -func getCombinations(a, b []string, switchOrder bool) []string { - var res []string - for _, s := range a { - for _, s2 := range b { - res = append(res, fmt.Sprintf("%s.%s", s, s2)) - if switchOrder { - res = append(res, fmt.Sprintf("%s.%s", s2, s)) - } - } - } - return res -} - -func getPrefix(s string) string { - firstMatch := separatorRegex.FindStringSubmatchIndex(s) - if len(firstMatch) == 0 { - return "" - } - if firstMatch[0] == 0 { - return s[:firstMatch[1]] - } - return "" -} - -func containsShortMonth(s string) bool { - for _, m := range monthsShort { - if strings.Contains(s, m) { - return true - } - } - return false -} - -func containsLongMonth(s string) bool { - for _, m := range months { - if strings.Contains(s, m) { - return true - } - } - return false -} - -func containsTime(s string) bool { - return strings.Contains(s, ":") -} - -func containsTimezone(s string) bool { - return (timezoneRegex.MatchString(s) || timezoneNumericRegex.MatchString(s)) && - !(strings.Contains(s, "SEPT") || strings.Contains(s, "OCT") || strings.Contains(s, "SAT")) -} - -func formatWithSeparators(f string, sep [][]string) string { - if len(sep) == 0 { - return strings.Replace(f, ".", "", -1) - } - for i := 0; i < len(sep); i++ { - s := sep[i][0] - if s == "." { - s = "." - } - f = strings.Replace(f, ".", s, 1) - } - return strings.Replace(f, ".", ".", -1) -} diff --git a/internal/github.com/joyt/godate/godate_test.go b/internal/github.com/joyt/godate/godate_test.go deleted file mode 100644 index 581326d3..00000000 --- a/internal/github.com/joyt/godate/godate_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package date - -import ( - "testing" - "time" -) - -func TestDates(t *testing.T) { - // Invalid dates. - tests := []string{ - "", - "HI", - "20938258384738", - "23.44", - } - for _, date := range tests { - if d, err := Parse(date); err == nil { - t.Errorf("Input %q was parsed to a date %v", date, d) - } - } - - // Full date, no ambiguity. - correct := time.Date(1989, 6, 25, 0, 0, 0, 0, time.UTC) - tests = []string{ - "25 Jun 1989", - "Jun 25 1989", - "Jun 25 '89", - "June 25, 1989", - "June 25, '89", - "25 Jun 1989", - "25 Jun '89", - - "6/25/89", - "06/25/89", - "6/25/1989", - "06/25/1989", - "25/6/89", - "25/6/1989", - "25/06/89", - "25/06/1989", - - "6-25-89", - "06-25-89", - "6-25-1989", - "06-25-1989", - "25-6-89", - "25-6-1989", - "25-06-89", - "25-06-1989", - - "19890625", - "890625", - } - - for _, date := range tests { - d, err := Parse(date) - if err != nil { - t.Errorf("Error prasing %q: %v", date, err) - } else if d != correct { - t.Errorf("Dates for %q did not match:\n%v (actual)\n%v (expected)", date, d, correct) - } - } - - // Just month and year - correct = time.Date(1989, 6, 1, 0, 0, 0, 0, time.UTC) - tests = []string{ - "Jun 1989", - "Jun-1989", - "Jun '89", - "June 1989", - "June '89", - "6/89", - "06/89", - "6-89", - "06-89", - "198906", - "8906", - "0689", - } - - for _, date := range tests { - d, err := Parse(date) - if err != nil { - t.Errorf("Error prasing %s: %v", date, err) - } else if d != correct { - t.Errorf("Dates for %q did not match:\n%v (actual)\n%v (expected)", date, d, correct) - } - } - - // Just month and day - correct = time.Date(0, 6, 25, 0, 0, 0, 0, time.UTC) - tests = []string{ - "Jun 25", - "Jun-25", - "June 25", - "6/25", - "06/25", - "6-25", - } - - for _, date := range tests { - d, err := Parse(date) - if err != nil { - t.Errorf("Error prasing %q: %v", date, err) - } else if d != correct { - t.Errorf("Dates for %q did not match:\n%v (actual)\n%v (expected)", date, d, correct) - } - } - - // Just month and year, ambiguous - correct = time.Date(2009, 6, 1, 0, 0, 0, 0, time.UTC) - tests = []string{ - "Jun 2009", - "Jun-2009", - "June 2009", - "200906", - } - - for _, date := range tests { - d, err := Parse(date) - if err != nil { - t.Errorf("Error prasing %q: %v", date, err) - } else if d != correct { - t.Errorf("Dates for %q did not match:\n%v (actual)\n%v (expected)", date, d, correct) - } - } - - // Full date with time at minute resolution. - correct = time.Date(1989, 6, 25, 15, 45, 0, 0, time.UTC) - tests = []string{ - "Jun 25 1989 15:45", - "Jun 25 1989 3:45PM", - "Jun 25 1989 03:45PM", - "6/25/1989 15:45", - "6/25/1989 03:45PM", - "6/25/1989 3:45PM", - } - - for _, date := range tests { - d, err := Parse(date) - if err != nil { - t.Errorf("Error prasing %q: %v", date, err) - } else if d != correct { - t.Errorf("Dates for %q did not match:\n%v (actual)\n%v (expected)", date, d, correct) - } - } - - // TODO: Make more test cases for time of day, timezones. -} - -func TestConvert(t *testing.T) { - // TODO: Make test cases for time of day, timezones. - tests := map[string]string{ - "Jan 02 2006": "MMM dd yyyy", - "January 02, 2006": "MMMM dd, yyyy", - "01/02/2006": "MM/dd/yyyy", - "_2/1/06": "d/M/yy", - "01-2006": "MM-yyyy", - "Jan-2006": "MMM-yyyy", - "Mon 02 Jan '06": "EEE dd MMM 'yy", - } - for f, correct := range tests { - if uf := ConvertGoLayoutToUnicode(f); uf != correct { - t.Errorf("Failed to convert %q to %q, got %q instead", f, correct, uf) - } - } -} diff --git a/parse.go b/parse.go index bdaca734..65f7697b 100644 --- a/parse.go +++ b/parse.go @@ -9,7 +9,7 @@ import ( "sort" "strings" - date "github.com/howeyc/ledger/internal/github.com/joyt/godate" + date "github.com/joyt/godate" "github.com/marcmak/calc/calc" )