diff --git a/clock/clock.go b/clock/clock.go index a2083019..99de649f 100644 --- a/clock/clock.go +++ b/clock/clock.go @@ -83,6 +83,7 @@ func (c Clock) DurationSinceMidnight() time.Duration { // Add returns a new Clock offset from this clock specified hour, minute, second and millisecond. // The parameters can be negative. +// // If required, use Mod24() to correct any overflow or underflow. func (c Clock) Add(h, m, s, ms int) Clock { hx := Clock(h) * Hour @@ -93,7 +94,10 @@ func (c Clock) Add(h, m, s, ms int) Clock { // AddDuration returns a new Clock offset from this clock by a duration. // The parameters can be negative. +// // If required, use Mod24() to correct any overflow or underflow. +// +// AddDuration is also useful for adding period.Period values. func (c Clock) AddDuration(d time.Duration) Clock { return c + Clock(d/time.Millisecond) } diff --git a/period/doc.go b/period/doc.go index 4e0eaa05..b564972f 100644 --- a/period/doc.go +++ b/period/doc.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. // Package period provides functionality for periods of time using ISO-8601 conventions. -// This deals with years, months, weeks and days. +// This deals with years, months, weeks/days, hours, minutes and seconds. +// // Because of the vagaries of calendar systems, the meaning of year lengths, month lengths // and even day lengths depends on context. So a period is not necessarily a fixed duration // of time in terms of seconds. @@ -12,14 +13,32 @@ // // Example representations: // +// * "P2Y" is two years; +// +// * "P6M" is six months; +// // * "P4D" is four days; // +// * "P1W" is one week (seven days); +// +// * "PT3H" is three hours. +// +// * "PT20M" is twnety minutes. +// +// * "PT30S" is thirty seconds. +// +// These can be combined, for example: +// // * "P3Y6M4W1D" is three years, 6 months, 4 weeks and one day. // // * "P2DT12H" is 2 days and 12 hours. // -// * "PT30S" is 30 seconds. +// Also, decimal fractions are supported to one decimal place. To comply with +// the standard, only the last non-zero component is allowed to have a fraction). +// For example // // * "P2.5Y" is 2.5 years. // +// * "PT12M7.5S" is 12 minutes and 7.5 seconds. +// package period diff --git a/period/parse.go b/period/parse.go index 16e6ec78..30b3a693 100644 --- a/period/parse.go +++ b/period/parse.go @@ -24,6 +24,11 @@ func MustParse(value string) Period { // // In addition, a plus or minus sign can precede the period, e.g. "-P10D" // +// The value is normalised, e.g. multiple of 12 months become years so "P24M" +// is the same as "P2Y". However, this is done without loss of precision, so +// for example whole numbers of days do not contribute to the months tally +// because the number of days per month is variable. +// // The zero value can be represented in several ways: all of the following // are equivalent: "P0Y", "P0M", "P0W", "P0D", "PT0H", PT0M", PT0S", and "P0". // The canonical zero is "P0D". diff --git a/period/period.go b/period/period.go index bf722c9d..604dbfc1 100644 --- a/period/period.go +++ b/period/period.go @@ -313,12 +313,18 @@ func (period Period) YearsFloat() float32 { // Months gets the whole number of months in the period. // The result is the number of months and does not include any other field. +// +// Note that after normalisation, whole multiple of 12 months are added to +// the number of years, so the number of months will be reduced correspondingly. func (period Period) Months() int { return int(period.MonthsFloat()) } // MonthsFloat gets the number of months in the period. // The result is the number of months and does not include any other field. +// +// Note that after normalisation, whole multiple of 12 months are added to +// the number of years, so the number of months will be reduced correspondingly. func (period Period) MonthsFloat() float32 { return float32(period.months) / 10 } @@ -338,6 +344,9 @@ func (period Period) DaysFloat() float32 { // Weeks calculates the number of whole weeks from the number of days. If the result // would contain a fraction, it is truncated. // The result is the number of weeks and does not include any other field. +// +// Note that weeks are synthetic: they are internally represented using days. +// See ModuloDays(), which returns the number of days excluding whole weeks. func (period Period) Weeks() int { return int(period.days) / 70 } @@ -373,24 +382,36 @@ func (period Period) HoursFloat() float32 { // Minutes gets the whole number of minutes in the period. // The result is the number of minutes and does not include any other field. +// +// Note that after normalisation, whole multiple of 60 minutes are added to +// the number of hours, so the number of minutes will be reduced correspondingly. func (period Period) Minutes() int { return int(period.MinutesFloat()) } // MinutesFloat gets the number of minutes in the period. // The result is the number of minutes and does not include any other field. +// +// Note that after normalisation, whole multiple of 60 minutes are added to +// the number of hours, so the number of minutes will be reduced correspondingly. func (period Period) MinutesFloat() float32 { return float32(period.minutes) / 10 } // Seconds gets the whole number of seconds in the period. // The result is the number of seconds and does not include any other field. +// +// Note that after normalisation, whole multiple of 60 seconds are added to +// the number of minutes, so the number of seconds will be reduced correspondingly. func (period Period) Seconds() int { return int(period.SecondsFloat()) } // SecondsFloat gets the number of seconds in the period. // The result is the number of seconds and does not include any other field. +// +// Note that after normalisation, whole multiple of 60 seconds are added to +// the number of minutes, so the number of seconds will be reduced correspondingly. func (period Period) SecondsFloat() float32 { return float32(period.seconds) / 10 } @@ -496,6 +517,8 @@ func (period Period) TotalMonthsApprox() int { // Additionally, in imprecise mode: // Multiples of 24 hours become days. // Multiples of approx. 30.4 days become months. +// +// Note that leap seconds are disregarded: every minute is assumed to have 60 seconds. func (period Period) Normalise(precise bool) Period { const limit = 32670 - (32670 / 60) diff --git a/period/period_test.go b/period/period_test.go index abffd75d..99863536 100644 --- a/period/period_test.go +++ b/period/period_test.go @@ -131,11 +131,14 @@ func TestPeriodIntComponents(t *testing.T) { {"-P1W", 0, 0, -1, -7, 0, 0, 0, 0}, {"P6M", 0, 6, 0, 0, 0, 0, 0, 0}, {"-P6M", 0, -6, 0, 0, 0, 0, 0, 0}, + {"P12M", 1, 0, 0, 0, 0, 0, 0, 0}, + {"-P12M", -1, -0, 0, 0, 0, 0, 0, 0}, {"P39D", 0, 0, 5, 39, 4, 0, 0, 0}, {"-P39D", 0, 0, -5, -39, -4, 0, 0, 0}, {"P4D", 0, 0, 0, 4, 4, 0, 0, 0}, {"-P4D", 0, 0, 0, -4, -4, 0, 0, 0}, {"PT12H", 0, 0, 0, 0, 0, 12, 0, 0}, + {"PT60M", 0, 0, 0, 0, 0, 1, 0, 0}, {"PT30M", 0, 0, 0, 0, 0, 0, 30, 0}, {"PT5S", 0, 0, 0, 0, 0, 0, 0, 5}, } @@ -185,6 +188,8 @@ func TestPeriodFloatComponents(t *testing.T) { {"P1.1M", 0, 1.1, 0, 0, 0, 0, 0, 0}, {"P6M", 0, 6, 0, 0, 0, 0, 0, 0}, {"-P6M", 0, -6, 0, 0, 0, 0, 0, 0}, + {"P12M", 1, 0, 0, 0, 0, 0, 0, 0}, + {"-P12M", -1, 0, 0, 0, 0, 0, 0, 0}, {"P39D", 0, 0, 5.571429, 39, 4, 0, 0, 0}, {"-P39D", 0, 0, -5.571429, -39, -4, 0, 0, 0}, {"P4D", 0, 0, 0.5714286, 4, 4, 0, 0, 0},