mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 22:10:33 +00:00
Add Week-Of-Year Implementation to Times Module (#17223)
* initial * more tests * Apply suggestions from code review idiomatize Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * test iron age dates * add examples * fix typo * consistent param mention * add since pragrams * add changelog * Update lib/pure/times.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * fix examples * fix negative years * add getWeeksInYear tests * add back fix dropped by rebase * week-year tuple api * add changelog * fix doc tags * add docstrings * fix typos Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
This commit is contained in:
@@ -27,6 +27,12 @@
|
||||
|
||||
- Sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback.
|
||||
|
||||
- Added `IsoWeekRange`, a range type to represent the number of weeks in an ISO week-based year.
|
||||
- Added `IsoYear`, a distinct int type to prevent bugs from confusing the week-based year and the regular year.
|
||||
- Added `initDateTime` in `times` to create a datetime from a weekday, and ISO 8601 week number and week-based year.
|
||||
- Added `getIsoWeekAndYear` in `times` to get an ISO week number along with the corresponding ISO week-based year from a datetime.
|
||||
- Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year.
|
||||
|
||||
## Language changes
|
||||
|
||||
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
|
||||
|
||||
@@ -288,6 +288,13 @@ type
|
||||
YeardayRange* = range[0..365]
|
||||
NanosecondRange* = range[0..999_999_999]
|
||||
|
||||
IsoWeekRange* = range[1 .. 53]
|
||||
## An ISO 8601 calendar week number.
|
||||
IsoYear* = distinct int
|
||||
## An ISO 8601 calendar year number.
|
||||
##
|
||||
## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3.
|
||||
|
||||
Time* = object ## Represents a point in time.
|
||||
seconds: int64
|
||||
nanosecond: NanosecondRange
|
||||
@@ -529,6 +536,54 @@ proc getDaysInYear*(year: int): int =
|
||||
doAssert getDaysInYear(2001) == 365
|
||||
result = 365 + (if isLeapYear(year): 1 else: 0)
|
||||
|
||||
proc `==`*(a, b: IsoYear): bool {.borrow.}
|
||||
proc `$`*(p: IsoYear): string {.borrow.}
|
||||
|
||||
proc getWeeksInIsoYear*(y: IsoYear): IsoWeekRange {.since: (1, 5).} =
|
||||
## Returns the number of weeks in the specified ISO 8601 week-based year, which can be
|
||||
## either 53 or 52.
|
||||
runnableExamples:
|
||||
assert getWeeksInIsoYear(IsoYear(2000)) == 52
|
||||
assert getWeeksInIsoYear(IsoYear(2001)) == 53
|
||||
|
||||
var y = int(y)
|
||||
|
||||
# support negative years
|
||||
y = if y < 0: 400 + y mod 400 else: y
|
||||
|
||||
# source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
|
||||
let p = (y + (y div 4) - (y div 100) + (y div 400)) mod 7
|
||||
let y1 = y - 1
|
||||
let p1 = (y1 + (y1 div 4) - (y1 div 100) + (y1 div 400)) mod 7
|
||||
if p == 4 or p1 == 3: 53 else: 52
|
||||
|
||||
proc getIsoWeekAndYear*(dt: DateTime):
|
||||
tuple[isoweek: IsoWeekRange, isoyear: IsoYear] {.since: (1, 5).} =
|
||||
## Returns the ISO 8601 week and year.
|
||||
##
|
||||
## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3.
|
||||
runnableExamples:
|
||||
assert getIsoWeekAndYear(initDateTime(21, mApr, 2018, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2018.IsoYear)
|
||||
block:
|
||||
let (w, y) = getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00))
|
||||
assert w == 01.IsoWeekRange
|
||||
assert y == 2020.IsoYear
|
||||
assert getIsoWeekAndYear(initDateTime(13, mSep, 2020, 00, 00, 00)) == (isoweek: 37.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
block:
|
||||
let (w, y) = getIsoWeekAndYear(initDateTime(2, mJan, 2021, 00, 00, 00))
|
||||
assert w.int > 52
|
||||
assert w.int < 54
|
||||
assert y.int mod 100 == 20
|
||||
|
||||
# source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
|
||||
var w = (dt.yearday.int - dt.weekday.int + 10) div 7
|
||||
if w < 1:
|
||||
(isoweek: getWeeksInIsoYear(IsoYear(dt.year - 1)), isoyear: IsoYear(dt.year - 1))
|
||||
elif (w > getWeeksInIsoYear(IsoYear(dt.year))):
|
||||
(isoweek: IsoWeekRange(1), isoyear: IsoYear(dt.year + 1))
|
||||
else:
|
||||
(isoweek: IsoWeekRange(w), isoyear: IsoYear(dt.year))
|
||||
|
||||
proc stringifyUnit(value: int | int64, unit: TimeUnit): string =
|
||||
## Stringify time unit with it's name, lowercased
|
||||
let strUnit = $unit
|
||||
@@ -2578,6 +2633,33 @@ proc `+=`*(t: var Time, b: TimeInterval) =
|
||||
proc `-=`*(t: var Time, b: TimeInterval) =
|
||||
t = t - b
|
||||
|
||||
#
|
||||
# Day of year
|
||||
#
|
||||
|
||||
proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
|
||||
hour: HourRange, minute: MinuteRange, second: SecondRange,
|
||||
nanosecond: NanosecondRange,
|
||||
zone: Timezone = local()): DateTime {.since: (1, 5).} =
|
||||
## Create a new `DateTime <#DateTime>`_ from a weekday and an ISO 8601 week number and year
|
||||
## in the specified timezone.
|
||||
##
|
||||
## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3.
|
||||
runnableExamples:
|
||||
assert initDateTime(21, mApr, 2018, 00, 00, 00) == initDateTime(dSat, 16, 2018.IsoYear, 00, 00, 00)
|
||||
assert initDateTime(30, mDec, 2019, 00, 00, 00) == initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00)
|
||||
assert initDateTime(13, mSep, 2020, 00, 00, 00) == initDateTime(dSun, 37, 2020.IsoYear, 00, 00, 00)
|
||||
assert initDateTime(2, mJan, 2021, 00, 00, 00) == initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00)
|
||||
|
||||
# source https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
|
||||
let d = isoweek * 7 + weekday.int - initDateTime(4, mJan, isoyear.int, 00, 00, 00).weekday.int - 4
|
||||
initDateTime(1, mJan, isoyear.int, hour, minute, second, nanosecond, zone) + initTimeInterval(days=d)
|
||||
|
||||
proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
|
||||
hour: HourRange, minute: MinuteRange, second: SecondRange,
|
||||
zone: Timezone = local()): DateTime {.since: (1, 5).} =
|
||||
initDateTime(weekday, isoweek, isoyear, hour, minute, second, 0, zone)
|
||||
|
||||
#
|
||||
# Other
|
||||
#
|
||||
|
||||
@@ -646,3 +646,98 @@ block: # ttimes
|
||||
doAssert initDuration(milliseconds = 500).inMilliseconds == 500
|
||||
doAssert initDuration(milliseconds = -500).inMilliseconds == -500
|
||||
doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999
|
||||
|
||||
block: # getIsoWeekAndYear
|
||||
doAssert getIsoWeekAndYear(initDateTime(04, mNov, 2019, 00, 00, 00)) == (isoweek: 45.IsoWeekRange, isoyear: 2019.IsoYear)
|
||||
doAssert initDateTime(dMon, 45, 2019.IsoYear, 00, 00, 00) == initDateTime(04, mNov, 2019, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(28, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear)
|
||||
doAssert initDateTime(dSat, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(28, mDec, 2019, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(29, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear)
|
||||
doAssert initDateTime(dSun, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(29, mDec, 2019, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(30, mDec, 2019, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dTue, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2019, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dWed, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dThu, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(05, mApr, 2020, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dSun, 14, 2020.IsoYear, 00, 00, 00) == initDateTime(05, mApr, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(06, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dMon, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(06, mApr, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(10, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dFri, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(10, mApr, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(12, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dSun, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(12, mApr, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(13, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dMon, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(13, mApr, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(15, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dThu, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(16, mApr, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(17, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dFri, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(17, mJul, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(19, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dSun, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(19, mJul, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(20, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dMon, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(20, mJul, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(23, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dThu, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(23, mJul, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2020, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dThu, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2020, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dFri, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2021, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2021, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(03, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
|
||||
doAssert initDateTime(dSun, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(03, mJan, 2021, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(04, mJan, 2021, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2021.IsoYear)
|
||||
doAssert initDateTime(dMon, 01, 2021.IsoYear, 00, 00, 00) == initDateTime(04, mJan, 2021, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 00, 00, 00)) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear)
|
||||
doAssert initDateTime(dMon, 05, 2021.IsoYear, 00, 00, 00) == initDateTime(01, mFeb, 2021, 00, 00, 00)
|
||||
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear)
|
||||
doAssert initDateTime(dMon, 05, 2021.IsoYear, 01, 02, 03, 400_000_000, staticTz(hours=1)) == initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))
|
||||
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0001.IsoYear)
|
||||
doAssert initDateTime(dSun, 13, 0001.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0001, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0000, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0000.IsoYear)
|
||||
doAssert initDateTime(dSat, 13, 0000.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0000, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: (-0001).IsoYear)
|
||||
doAssert initDateTime(dThu, 13, (-0001).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0001, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0002, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0002).IsoYear)
|
||||
doAssert initDateTime(dWed, 14, (-0002).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0002, 00, 00, 00)
|
||||
doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0753, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0753).IsoYear)
|
||||
doAssert initDateTime(dMon, 14, (-0753).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0753, 00, 00, 00)
|
||||
|
||||
block: # getWeeksInIsoYear
|
||||
doAssert getWeeksInIsoYear((-0014).IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear((-0013).IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear((-0012).IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear((-0009).IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear((-0008).IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear((-0007).IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear((-0003).IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear((-0002).IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear((-0001).IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear(0003.IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear(0004.IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear(0005.IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear(1653.IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear(1654.IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear(1655.IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear(1997.IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear(1998.IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear(1999.IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear(2008.IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear(2009.IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear(2010.IsoYear) == 52
|
||||
|
||||
doAssert getWeeksInIsoYear(2014.IsoYear) == 52
|
||||
doAssert getWeeksInIsoYear(2015.IsoYear) == 53
|
||||
doAssert getWeeksInIsoYear(2016.IsoYear) == 52
|
||||
|
||||
Reference in New Issue
Block a user