mirror of
https://github.com/kataras/iris.git
synced 2025-12-18 02:17:05 +00:00
minor improvements on x/jsonx.ISO8601 time parser
note that many new features are coming that I'm designing for the past 2-3 months, this is a commit with just one hotfix
This commit is contained in:
@@ -32,11 +32,31 @@ const (
|
||||
// ISO8601Layout holds the time layout for the the javascript iso time.
|
||||
// Read more at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString.
|
||||
ISO8601Layout = "2006-01-02T15:04:05"
|
||||
// ISO8601ZLayout same as ISO8601Layout but with the timezone suffix.
|
||||
ISO8601ZLayout = "2006-01-02T15:04:05Z"
|
||||
// ISO8601ZUTCOffsetLayout ISO 8601 format, with full time and zone with UTC offset.
|
||||
// ISO8601LayoutWithTimezone same as ISO8601Layout but with the timezone suffix.
|
||||
ISO8601LayoutWithTimezone = "2006-01-02T15:04:05Z"
|
||||
|
||||
// To match Go’s standard time layout that pads zeroes for microseconds, you can use the format 2006-01-02T15:04:05.000000Z07:00.
|
||||
// This layout uses 0s instead of 9s for the fractional second part, which ensures that the microseconds are
|
||||
// always represented with six digits, padding with leading zeroes if necessary.
|
||||
// ISO8601ZUTCOffsetLayoutWithMicroseconds = "2006-01-02T15:04:05.000000Z07:00"
|
||||
// ISO8601ZUTCOffsetLayoutWithMicroseconds ISO 8601 format, with full time and zone with UTC offset.
|
||||
// Example: 2022-08-10T03:21:00.000000+03:00, 2023-02-04T09:48:14+00:00, 2022-08-09T00:00:00.000000.
|
||||
ISO8601ZUTCOffsetLayout = "2006-01-02T15:04:05.999999Z07:00" // -07:00
|
||||
ISO8601ZUTCOffsetLayoutWithMicroseconds = "2006-01-02T15:04:05.999999Z07:00"
|
||||
// ISO8601ZUTCOffsetLayoutWithoutMicroseconds ISO 8601 format, with full time and zone with UTC offset without microsecond precision.
|
||||
ISO8601ZUTCOffsetLayoutWithoutMicroseconds = "2006-01-02T15:04:05Z07:00"
|
||||
/*
|
||||
The difference between the two time layouts "2006-01-02T15:04:05Z07:00" and "2006-01-02T15:04:05-07:00" is the presence of the Z character:
|
||||
|
||||
"2006-01-02T15:04:05Z07:00": The Z indicates that the time is in UTC (Coordinated Universal Time) if there’s no offset specified.
|
||||
When an offset is present, as in +03:00, it indicates the time is in a timezone that is 3 hours ahead of UTC.
|
||||
The Z is combined with the offset (07:00), which can be positive or negative to represent the timezone difference from UTC.
|
||||
|
||||
"2006-01-02T15:04:05-07:00": This layout does not have the Z character and directly uses the offset (-07:00).
|
||||
It’s more straightforward and indicates that the time is in a timezone that is 7 hours behind UTC.
|
||||
In summary, the Z in the first layout serves as a placeholder for UTC and is used when the time might be in UTC or might have an offset.
|
||||
The second layout is used when you’re directly specifying the offset without any reference to UTC.
|
||||
Both layouts can parse the timestamp "2024-04-08T04:47:10+03:00" correctly, as they include placeholders for the timezone offset.
|
||||
*/
|
||||
)
|
||||
|
||||
// ISO8601 describes a time compatible with javascript time format.
|
||||
@@ -55,30 +75,17 @@ func ParseISO8601(s string) (ISO8601, error) {
|
||||
err error
|
||||
)
|
||||
|
||||
if idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 18 /* should have some distance, with and without milliseconds */ {
|
||||
length := parseSignedOffset(s[idx:])
|
||||
// Check if the string contains a timezone offset after the 'T' character.
|
||||
hasOffset := strings.Contains(s, "Z") || (strings.Index(s, "+") > strings.Index(s, "T")) || (strings.Index(s, "-") > strings.Index(s, "T"))
|
||||
|
||||
if idx+1 > idx+length || len(s) <= idx+length+1 {
|
||||
return ISO8601{}, fmt.Errorf("ISO8601: invalid timezone format: %s", s[idx:])
|
||||
}
|
||||
|
||||
offsetText := s[idx+1 : idx+length]
|
||||
offset, parseErr := strconv.Atoi(offsetText)
|
||||
if parseErr != nil {
|
||||
return ISO8601{}, fmt.Errorf("ISO8601: %w", parseErr)
|
||||
}
|
||||
|
||||
// E.g. offset of +0300 is returned as 10800 which is - (3 * 60 * 60).
|
||||
secondsEastUTC := offset * 60 * 60
|
||||
|
||||
if loc, ok := fixedEastUTCLocations[secondsEastUTC]; ok { // Specific (fixed) zone.
|
||||
tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayout, s, loc)
|
||||
} else { // Local or UTC.
|
||||
tt, err = time.Parse(ISO8601ZUTCOffsetLayout, s)
|
||||
}
|
||||
} else if s[len(s)-1] == 'Z' {
|
||||
tt, err = time.Parse(ISO8601ZLayout, s)
|
||||
} else {
|
||||
switch {
|
||||
case strings.HasSuffix(s, "Z"):
|
||||
tt, err = time.Parse(ISO8601LayoutWithTimezone, s)
|
||||
case hasOffset && strings.Contains(s, "."):
|
||||
tt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithMicroseconds, s)
|
||||
case hasOffset:
|
||||
tt, err = parseWithOffset(s)
|
||||
default:
|
||||
tt, err = time.Parse(ISO8601Layout, s)
|
||||
}
|
||||
|
||||
@@ -87,6 +94,97 @@ func ParseISO8601(s string) (ISO8601, error) {
|
||||
}
|
||||
|
||||
return ISO8601(tt), nil
|
||||
|
||||
/*
|
||||
if idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 18 { // should have some distance, with and without milliseconds
|
||||
length := parseSignedOffset(s[idx:])
|
||||
|
||||
if idx+1 > idx+length || len(s) <= idx+length+1 {
|
||||
return ISO8601{}, fmt.Errorf("ISO8601: invalid timezone format: %s", s[idx:])
|
||||
}
|
||||
|
||||
offsetText := s[idx+1 : idx+length]
|
||||
offset, parseErr := strconv.Atoi(offsetText)
|
||||
if parseErr != nil {
|
||||
return ISO8601{}, fmt.Errorf("ISO8601: %w", parseErr)
|
||||
}
|
||||
|
||||
// E.g. offset of +0300 is returned as 10800 which is - (3 * 60 * 60).
|
||||
secondsEastUTC := offset * 60 * 60
|
||||
|
||||
// fmt.Printf("parsing %s with offset %s, secondsEastUTC: %d, using time layout: %s\n", s, offsetText, secondsEastUTC, ISO8601ZUTCOffsetLayoutWithMicroseconds)
|
||||
if loc, ok := fixedEastUTCLocations[secondsEastUTC]; ok { // Specific (fixed) zone.
|
||||
if strings.Contains(s, ".") {
|
||||
tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithMicroseconds, s, loc)
|
||||
} else {
|
||||
tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayout, s, loc)
|
||||
}
|
||||
} else { // Local or UTC.
|
||||
if strings.Contains(s, ".") {
|
||||
tt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithMicroseconds, s)
|
||||
} else {
|
||||
tt, err = time.Parse(ISO8601ZUTCOffsetLayout, s)
|
||||
}
|
||||
}
|
||||
} else if s[len(s)-1] == 'Z' {
|
||||
tt, err = time.Parse(ISO8601ZLayout, s)
|
||||
} else {
|
||||
tt, err = time.Parse(ISO8601Layout, s)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ISO8601{}, fmt.Errorf("ISO8601: %w", err)
|
||||
}
|
||||
return ISO8601(tt), nil
|
||||
*/
|
||||
}
|
||||
|
||||
func parseWithOffset(s string) (time.Time, error) {
|
||||
idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc)
|
||||
if idx == -1 {
|
||||
return time.Time{}, fmt.Errorf("ISO8601: missing timezone offset")
|
||||
}
|
||||
|
||||
offsetText := s[idx:]
|
||||
secondsEastUTC, err := parseOffsetToSeconds(offsetText)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
loc, ok := fixedEastUTCLocations[secondsEastUTC]
|
||||
if !ok {
|
||||
loc = time.FixedZone("", secondsEastUTC)
|
||||
}
|
||||
|
||||
return time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s, loc)
|
||||
}
|
||||
|
||||
func parseOffsetToSeconds(offsetText string) (int, error) {
|
||||
if len(offsetText) < 6 {
|
||||
return 0, fmt.Errorf("ISO8601: invalid timezone offset length: %s", offsetText)
|
||||
}
|
||||
|
||||
sign := offsetText[0]
|
||||
if sign != '-' && sign != '+' {
|
||||
return 0, fmt.Errorf("ISO8601: invalid timezone offset sign: %c", sign)
|
||||
}
|
||||
|
||||
hours, err := strconv.Atoi(offsetText[1:3])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ISO8601: %w", err)
|
||||
}
|
||||
|
||||
minutes, err := strconv.Atoi(offsetText[4:6])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ISO8601: %w", err)
|
||||
}
|
||||
|
||||
secondsEastUTC := (hours*60 + minutes) * 60
|
||||
if sign == '-' {
|
||||
secondsEastUTC = -secondsEastUTC
|
||||
}
|
||||
|
||||
return secondsEastUTC, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the "b" into ISO8601 time.
|
||||
|
||||
Reference in New Issue
Block a user