Handling UTC Date Formats in Go
As Trendyol Indexing Team, we recently completed the development of a Go project that collected data from multiple microservices. However, We encountered an issue where the incoming date formats were causing parse errors. After investigating the issue, we found that the data was not always sent in the same layout. In this article, we’ll take a look at the challenge we faced and how we solved it as a team.
In the project, there is a function that converts the given date to a time object. Unfortunately, we faced an issue where some of the dates were causing parse errors. After inspection, we found that the incoming date format was different from the expected format. The incoming format was as follows:
- 2023–01–31T11:50:42.12Z
- 2023–01–31T11:50:42.1Z
After reviewing the code, we discovered that it was expecting a format with three digits of milliseconds after the seconds and the letter “Z”. Only the following format was parsing successfully
- 2023–01–31T11:50:42.129Z
Old Function:
const UtcDateFormat = "2006-01-02T15:04:05.000Z"
func ParseToTimeWithUtc(date string) (time.Time, error) {
d, err := time.Parse(UtcDateFormat, date)
if err != nil {
logger.Logger().Error(fmt.Sprintf("%s can not be parse msg %s", date, err.Error()))
return time.UnixMilli(0), err
}
return d, nil
}
We needed a function that could parse all three incoming dates. To achieve this, we decided to use a standard instead of a static format. In Java, we had been using the ISO 8601 standard. But, we couldn’t find a similar layout defined in the ‘time’ package of Go. A little research led us to the RFC 3339 layout, which met our requirements.
Therefore, we resolved the error by passing the value ‘time.RFC3339’ to the parse function in our code block. With this change, our function is now able to parse all three incoming dates.
d, err := time.Parse(time.RFC3339, date)
We also updated the unit test as shown below. We wrote the test using the table test style. You can reach my friend’s article describing our team’s experience with table testing from this link.
tests := []struct {
date string
expected time.Time
wantErr assert.ErrorAssertionFunc
}{
{"2022-03-09T03:30:11.123Z", time.Date(2022, time.March, 9, 3, 30, 11, m(123), time.UTC), assert.NoError},
{"2022-03-09T03:30:11.12Z", time.Date(2022, time.March, 9, 3, 30, 11, m(120), time.UTC), assert.NoError},
{"2022-03-09T03:30:11.1Z", time.Date(2022, time.March, 9, 3, 30, 11, m(100), time.UTC), assert.NoError},
{"2022-03-09T03:30:11Z", time.Date(2022, time.March, 9, 3, 30, 11, m(0), time.UTC), assert.NoError},
{"2022-03-09T03:30:11.816253Z", time.Date(2022, time.March, 9, 3, 30, 11, 816253000, time.UTC), assert.NoError},
{"2022-03-09T03:30:11.Z", time.UnixMilli(0), assert.Error},
{"2022-03-09T03:30:11", time.UnixMilli(0), assert.Error},
{"invalid", time.UnixMilli(0), assert.Error},
}
If you’re interested in joining our team, you can apply for the role of backend developer or any of our current open positions.
References: