diff --git a/ftp.go b/ftp.go index 831fb98..35b7106 100644 --- a/ftp.go +++ b/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) } diff --git a/parse.go b/parse.go index db63378..884e3b6 100644 --- a/parse.go +++ b/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 } diff --git a/parse_test.go b/parse_test.go index 01afd95..93edd42 100644 --- a/parse_test.go +++ b/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)