Merge pull request #116 from freman/master
Make it possible to specify what timezone to parse timestamps in
This commit is contained in:
		
						commit
						dab2053398
					
				
							
								
								
									
										6
									
								
								ftp.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								ftp.go
									
									
									
									
									
								
							@ -30,6 +30,9 @@ type ServerConn struct {
 | 
				
			|||||||
	// Do not use EPSV mode
 | 
						// Do not use EPSV mode
 | 
				
			||||||
	DisableEPSV bool
 | 
						DisableEPSV bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Timezone that the server is in
 | 
				
			||||||
 | 
						Location *time.Location
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	conn          *textproto.Conn
 | 
						conn          *textproto.Conn
 | 
				
			||||||
	host          string
 | 
						host          string
 | 
				
			||||||
	timeout       time.Duration
 | 
						timeout       time.Duration
 | 
				
			||||||
@ -83,6 +86,7 @@ func DialTimeout(addr string, timeout time.Duration) (*ServerConn, error) {
 | 
				
			|||||||
		host:     remoteAddr.IP.String(),
 | 
							host:     remoteAddr.IP.String(),
 | 
				
			||||||
		timeout:  timeout,
 | 
							timeout:  timeout,
 | 
				
			||||||
		features: make(map[string]string),
 | 
							features: make(map[string]string),
 | 
				
			||||||
 | 
							Location: time.UTC,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, _, err = c.conn.ReadResponse(StatusReady)
 | 
						_, _, err = c.conn.ReadResponse(StatusReady)
 | 
				
			||||||
@ -374,7 +378,7 @@ func (c *ServerConn) List(path string) (entries []*Entry, err error) {
 | 
				
			|||||||
	scanner := bufio.NewScanner(r)
 | 
						scanner := bufio.NewScanner(r)
 | 
				
			||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	for scanner.Scan() {
 | 
						for scanner.Scan() {
 | 
				
			||||||
		entry, err := parser(scanner.Text(), now)
 | 
							entry, err := parser(scanner.Text(), now, c.Location)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			entries = append(entries, entry)
 | 
								entries = append(entries, entry)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										36
									
								
								parse.go
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								parse.go
									
									
									
									
									
								
							@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var errUnsupportedListLine = errors.New("Unsupported LIST line")
 | 
					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{
 | 
					var listLineParsers = []parseFunc{
 | 
				
			||||||
	parseRFC3659ListLine,
 | 
						parseRFC3659ListLine,
 | 
				
			||||||
@ -25,7 +25,7 @@ var dirTimeFormats = []string{
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// parseRFC3659ListLine parses the style of directory line defined in RFC 3659.
 | 
					// 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, ";")
 | 
						iSemicolon := strings.Index(line, ";")
 | 
				
			||||||
	iWhitespace := strings.Index(line, " ")
 | 
						iWhitespace := strings.Index(line, " ")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,7 +49,7 @@ func parseRFC3659ListLine(line string, now time.Time) (*Entry, error) {
 | 
				
			|||||||
		switch key {
 | 
							switch key {
 | 
				
			||||||
		case "modify":
 | 
							case "modify":
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			e.Time, err = time.Parse("20060102150405", value)
 | 
								e.Time, err = time.ParseInLocation("20060102150405", value, loc)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									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
 | 
					// parseLsListLine parses a directory line in a format based on the output of
 | 
				
			||||||
// the UNIX ls command.
 | 
					// 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?
 | 
						// Has the first field a length of 10 bytes?
 | 
				
			||||||
	if strings.IndexByte(line, ' ') != 10 {
 | 
						if strings.IndexByte(line, ' ') != 10 {
 | 
				
			||||||
@ -88,7 +88,7 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 | 
				
			|||||||
			Type: EntryTypeFolder,
 | 
								Type: EntryTypeFolder,
 | 
				
			||||||
			Name: scanner.Remaining(),
 | 
								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
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -105,7 +105,7 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 | 
				
			|||||||
		if err := e.setSize(fields[2]); err != nil {
 | 
							if err := e.setSize(fields[2]); err != nil {
 | 
				
			||||||
			return nil, errUnsupportedListLine
 | 
								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
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -135,7 +135,7 @@ func parseLsListLine(line string, now time.Time) (*Entry, error) {
 | 
				
			|||||||
		return nil, errors.New("Unknown entry type")
 | 
							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
 | 
							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
 | 
					// parseDirListLine parses a directory line in a format based on the output of
 | 
				
			||||||
// the MS-DOS DIR command.
 | 
					// 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{}
 | 
						e := &Entry{}
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Try various time formats that DIR might use, and stop when one works.
 | 
						// Try various time formats that DIR might use, and stop when one works.
 | 
				
			||||||
	for _, format := range dirTimeFormats {
 | 
						for _, format := range dirTimeFormats {
 | 
				
			||||||
		if len(line) > len(format) {
 | 
							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 {
 | 
								if err == nil {
 | 
				
			||||||
				line = line[len(format):]
 | 
									line = line[len(format):]
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
@ -188,7 +188,7 @@ func parseDirListLine(line string, now time.Time) (*Entry, error) {
 | 
				
			|||||||
// by hostedftp.com
 | 
					// by hostedftp.com
 | 
				
			||||||
// -r--------   0 user group     65222236 Feb 24 00:39 UABlacklistingWeek8.csv
 | 
					// -r--------   0 user group     65222236 Feb 24 00:39 UABlacklistingWeek8.csv
 | 
				
			||||||
// (The link count is inexplicably 0)
 | 
					// (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?
 | 
						// Has the first field a length of 10 bytes?
 | 
				
			||||||
	if strings.IndexByte(line, ' ') != 10 {
 | 
						if strings.IndexByte(line, ' ') != 10 {
 | 
				
			||||||
		return nil, errUnsupportedListLine
 | 
							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.
 | 
						// 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
 | 
					// parseListLine parses the various non-standard format returned by the LIST
 | 
				
			||||||
// FTP command.
 | 
					// 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 {
 | 
						for _, f := range listLineParsers {
 | 
				
			||||||
		e, err := f(line, now)
 | 
							e, err := f(line, now, loc)
 | 
				
			||||||
		if err != errUnsupportedListLine {
 | 
							if err != errUnsupportedListLine {
 | 
				
			||||||
			return e, err
 | 
								return e, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -222,11 +222,11 @@ func (e *Entry) setSize(str string) (err error) {
 | 
				
			|||||||
	return
 | 
						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
 | 
						if strings.Contains(fields[2], ":") { // contains time
 | 
				
			||||||
		thisYear, _, _ := now.Date()
 | 
							thisYear, _, _ := now.Date()
 | 
				
			||||||
		timeStr := fmt.Sprintf("%s %s %d %s GMT", fields[1], fields[0], thisYear, fields[2])
 | 
							timeStr := fmt.Sprintf("%s %s %d %s", fields[1], fields[0], thisYear, fields[2])
 | 
				
			||||||
		e.Time, err = time.Parse("_2 Jan 2006 15:04 MST", timeStr)
 | 
							e.Time, err = time.ParseInLocation("_2 Jan 2006 15:04", timeStr, loc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			On unix, `info ls` shows:
 | 
								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 {
 | 
							if len(fields[2]) != 4 {
 | 
				
			||||||
			return errors.New("Invalid year format in time string")
 | 
								return errors.New("Invalid year format in time string")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		timeStr := fmt.Sprintf("%s %s %s 00:00 GMT", fields[1], fields[0], fields[2])
 | 
							timeStr := fmt.Sprintf("%s %s %s 00:00", fields[1], fields[0], fields[2])
 | 
				
			||||||
		e.Time, err = time.Parse("_2 Jan 2006 15:04 MST", timeStr)
 | 
							e.Time, err = time.ParseInLocation("_2 Jan 2006 15:04", timeStr, loc)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -84,7 +84,7 @@ var listTestsFail = []unsupportedLine{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestParseValidListLine(t *testing.T) {
 | 
					func TestParseValidListLine(t *testing.T) {
 | 
				
			||||||
	for _, lt := range listTests {
 | 
						for _, lt := range listTests {
 | 
				
			||||||
		entry, err := parseListLine(lt.line, now)
 | 
							entry, err := parseListLine(lt.line, now, time.UTC)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Errorf("parseListLine(%v) returned err = %v", lt.line, err)
 | 
								t.Errorf("parseListLine(%v) returned err = %v", lt.line, err)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
@ -106,7 +106,7 @@ func TestParseValidListLine(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestParseUnsupportedListLine(t *testing.T) {
 | 
					func TestParseUnsupportedListLine(t *testing.T) {
 | 
				
			||||||
	for _, lt := range listTestsFail {
 | 
						for _, lt := range listTestsFail {
 | 
				
			||||||
		_, err := parseListLine(lt.line, now)
 | 
							_, err := parseListLine(lt.line, now, time.UTC)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			t.Errorf("parseListLine(%v) expected to fail", lt.line)
 | 
								t.Errorf("parseListLine(%v) expected to fail", lt.line)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -136,7 +136,7 @@ func TestSettime(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, test := range tests {
 | 
						for _, test := range tests {
 | 
				
			||||||
		entry := &Entry{}
 | 
							entry := &Entry{}
 | 
				
			||||||
		entry.setTime(strings.Fields(test.line), now)
 | 
							entry.setTime(strings.Fields(test.line), now, time.UTC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !entry.Time.Equal(test.expected) {
 | 
							if !entry.Time.Equal(test.expected) {
 | 
				
			||||||
			t.Errorf("setTime(%v).Time = %v, want %v", test.line, entry.Time, test.expected)
 | 
								t.Errorf("setTime(%v).Time = %v, want %v", test.line, entry.Time, test.expected)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user