Browse Source

Merge pull request #116 from freman/master

Make it possible to specify what timezone to parse timestamps in
Julien Laffaye 7 years ago
parent
commit
dab2053398
3 changed files with 26 additions and 22 deletions
  1. 5 1
      ftp.go
  2. 18 18
      parse.go
  3. 3 3
      parse_test.go

+ 5 - 1
ftp.go

@@ -30,6 +30,9 @@ type ServerConn struct {
 	// Do not use EPSV mode
 	DisableEPSV bool
 
+	// Timezone that the server is in
+	Location *time.Location
+
 	conn          *textproto.Conn
 	host          string
 	timeout       time.Duration
@@ -83,6 +86,7 @@ func DialTimeout(addr string, timeout time.Duration) (*ServerConn, error) {
 		host:     remoteAddr.IP.String(),
 		timeout:  timeout,
 		features: make(map[string]string),
+		Location: time.UTC,
 	}
 
 	_, _, err = c.conn.ReadResponse(StatusReady)
@@ -374,7 +378,7 @@ func (c *ServerConn) List(path string) (entries []*Entry, err error) {
 	scanner := bufio.NewScanner(r)
 	now := time.Now()
 	for scanner.Scan() {
-		entry, err := parser(scanner.Text(), now)
+		entry, err := parser(scanner.Text(), now, c.Location)
 		if err == nil {
 			entries = append(entries, entry)
 		}

+ 18 - 18
parse.go

@@ -10,7 +10,7 @@ import (
 
 var errUnsupportedListLine = errors.New("Unsupported LIST line")
 
-type parseFunc func(string, time.Time) (*Entry, error)
+type parseFunc func(string, time.Time, *time.Location) (*Entry, error)
 
 var listLineParsers = []parseFunc{
 	parseRFC3659ListLine,
@@ -25,7 +25,7 @@ var dirTimeFormats = []string{
 }
 
 // parseRFC3659ListLine parses the style of directory line defined in RFC 3659.
-func parseRFC3659ListLine(line string, now time.Time) (*Entry, error) {
+func parseRFC3659ListLine(line string, now time.Time, loc *time.Location) (*Entry, error) {
 	iSemicolon := strings.Index(line, ";")
 	iWhitespace := strings.Index(line, " ")
 
@@ -49,7 +49,7 @@ func parseRFC3659ListLine(line string, now time.Time) (*Entry, error) {
 		switch key {
 		case "modify":
 			var err error
-			e.Time, err = time.Parse("20060102150405", value)
+			e.Time, err = time.ParseInLocation("20060102150405", value, loc)
 			if err != nil {
 				return nil, err
 			}
@@ -69,7 +69,7 @@ func parseRFC3659ListLine(line string, now time.Time) (*Entry, error) {
 
 // parseLsListLine parses a directory line in a format based on the output of
 // the UNIX ls command.
-func parseLsListLine(line string, now time.Time) (*Entry, error) {
+func parseLsListLine(line string, now time.Time, loc *time.Location) (*Entry, error) {
 
 	// Has the first field a length of 10 bytes?
 	if strings.IndexByte(line, ' ') != 10 {
@@ -88,7 +88,7 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 			Type: EntryTypeFolder,
 			Name: scanner.Remaining(),
 		}
-		if err := e.setTime(fields[3:6], now); err != nil {
+		if err := e.setTime(fields[3:6], now, loc); err != nil {
 			return nil, err
 		}
 
@@ -105,7 +105,7 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 		if err := e.setSize(fields[2]); err != nil {
 			return nil, errUnsupportedListLine
 		}
-		if err := e.setTime(fields[4:7], now); err != nil {
+		if err := e.setTime(fields[4:7], now, loc); err != nil {
 			return nil, err
 		}
 
@@ -135,7 +135,7 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 		return nil, errors.New("Unknown entry type")
 	}
 
-	if err := e.setTime(fields[5:8], now); err != nil {
+	if err := e.setTime(fields[5:8], now, loc); err != nil {
 		return nil, err
 	}
 
@@ -144,14 +144,14 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 
 // parseDirListLine parses a directory line in a format based on the output of
 // the MS-DOS DIR command.
-func parseDirListLine(line string, now time.Time) (*Entry, error) {
+func parseDirListLine(line string, now time.Time, loc *time.Location) (*Entry, error) {
 	e := &Entry{}
 	var err error
 
 	// Try various time formats that DIR might use, and stop when one works.
 	for _, format := range dirTimeFormats {
 		if len(line) > len(format) {
-			e.Time, err = time.Parse(format, line[:len(format)])
+			e.Time, err = time.ParseInLocation(format, line[:len(format)], loc)
 			if err == nil {
 				line = line[len(format):]
 				break
@@ -188,7 +188,7 @@ func parseDirListLine(line string, now time.Time) (*Entry, error) {
 // by hostedftp.com
 // -r--------   0 user group     65222236 Feb 24 00:39 UABlacklistingWeek8.csv
 // (The link count is inexplicably 0)
-func parseHostedFTPLine(line string, now time.Time) (*Entry, error) {
+func parseHostedFTPLine(line string, now time.Time, loc *time.Location) (*Entry, error) {
 	// Has the first field a length of 10 bytes?
 	if strings.IndexByte(line, ' ') != 10 {
 		return nil, errUnsupportedListLine
@@ -202,14 +202,14 @@ func parseHostedFTPLine(line string, now time.Time) (*Entry, error) {
 	}
 
 	// Set link count to 1 and attempt to parse as Unix.
-	return parseLsListLine(fields[0]+" 1 "+scanner.Remaining(), now)
+	return parseLsListLine(fields[0]+" 1 "+scanner.Remaining(), now, loc)
 }
 
 // parseListLine parses the various non-standard format returned by the LIST
 // FTP command.
-func parseListLine(line string, now time.Time) (*Entry, error) {
+func parseListLine(line string, now time.Time, loc *time.Location) (*Entry, error) {
 	for _, f := range listLineParsers {
-		e, err := f(line, now)
+		e, err := f(line, now, loc)
 		if err != errUnsupportedListLine {
 			return e, err
 		}
@@ -222,11 +222,11 @@ func (e *Entry) setSize(str string) (err error) {
 	return
 }
 
-func (e *Entry) setTime(fields []string, now time.Time) (err error) {
+func (e *Entry) setTime(fields []string, now time.Time, loc *time.Location) (err error) {
 	if strings.Contains(fields[2], ":") { // contains time
 		thisYear, _, _ := now.Date()
-		timeStr := fmt.Sprintf("%s %s %d %s GMT", fields[1], fields[0], thisYear, fields[2])
-		e.Time, err = time.Parse("_2 Jan 2006 15:04 MST", timeStr)
+		timeStr := fmt.Sprintf("%s %s %d %s", fields[1], fields[0], thisYear, fields[2])
+		e.Time, err = time.ParseInLocation("_2 Jan 2006 15:04", timeStr, loc)
 
 		/*
 			On unix, `info ls` shows:
@@ -248,8 +248,8 @@ func (e *Entry) setTime(fields []string, now time.Time) (err error) {
 		if len(fields[2]) != 4 {
 			return errors.New("Invalid year format in time string")
 		}
-		timeStr := fmt.Sprintf("%s %s %s 00:00 GMT", fields[1], fields[0], fields[2])
-		e.Time, err = time.Parse("_2 Jan 2006 15:04 MST", timeStr)
+		timeStr := fmt.Sprintf("%s %s %s 00:00", fields[1], fields[0], fields[2])
+		e.Time, err = time.ParseInLocation("_2 Jan 2006 15:04", timeStr, loc)
 	}
 	return
 }

+ 3 - 3
parse_test.go

@@ -84,7 +84,7 @@ var listTestsFail = []unsupportedLine{
 
 func TestParseValidListLine(t *testing.T) {
 	for _, lt := range listTests {
-		entry, err := parseListLine(lt.line, now)
+		entry, err := parseListLine(lt.line, now, time.UTC)
 		if err != nil {
 			t.Errorf("parseListLine(%v) returned err = %v", lt.line, err)
 			continue
@@ -106,7 +106,7 @@ func TestParseValidListLine(t *testing.T) {
 
 func TestParseUnsupportedListLine(t *testing.T) {
 	for _, lt := range listTestsFail {
-		_, err := parseListLine(lt.line, now)
+		_, err := parseListLine(lt.line, now, time.UTC)
 		if err == nil {
 			t.Errorf("parseListLine(%v) expected to fail", lt.line)
 		}
@@ -136,7 +136,7 @@ func TestSettime(t *testing.T) {
 
 	for _, test := range tests {
 		entry := &Entry{}
-		entry.setTime(strings.Fields(test.line), now)
+		entry.setTime(strings.Fields(test.line), now, time.UTC)
 
 		if !entry.Time.Equal(test.expected) {
 			t.Errorf("setTime(%v).Time = %v, want %v", test.line, entry.Time, test.expected)