Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DateTime week of year formats and getter #19214

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion lib/pure/times.nim
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@
| `UTC-5 -> -050000`
`g` Era: AD or BC | `300 AD -> AD`
| `300 BC -> BC`
`w` The week number of the year (0-53). | `01/01/2021 -> 0`
| `12/31/2021 -> 52`
`ww` Same as above but always two digits, 0 is prepended if the week is one digit. | `01/01/2021 -> 00`
| `12/31/2021 -> 52`
`fff` Milliseconds display | `1000000 nanoseconds -> 1`
`ffffff` Microseconds display | `1000000 nanoseconds -> 1000`
`fffffffff` Nanoseconds display | `1000000 nanoseconds -> 1000000`
Expand Down Expand Up @@ -288,6 +292,8 @@ type
YeardayRange* = range[0..365]
NanosecondRange* = range[0..999_999_999]

YearWeekRange = range[0 .. 52]

Time* = object ## Represents a point in time.
seconds: int64
nanosecond: NanosecondRange
Expand Down Expand Up @@ -1036,6 +1042,21 @@ proc timezone*(dt: DateTime): Timezone {.inline.} =
assertDateTimeInitialized(dt)
dt.timezone

proc week*(dt: DateTime): YearWeekRange =
# See:
# * https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
# * https://forum.nim-lang.org/t/6320

runnableExamples:
doAssert dateTime(2021, mJan, 1).week == 0
doAssert dateTime(2021, mDec, 31).week == 52

let dayOfYear = dt.yearday.int + 1
let dayOfWeek = dt.weekday.int + 1

((10 + dayOfYear - dayOfWeek) / 7).int


proc utcOffset*(dt: DateTime): int {.inline.} =
## The offset in seconds west of UTC, including
## any offset due to DST. Note that the sign of
Expand Down Expand Up @@ -1487,7 +1508,8 @@ type
UUUU
z, zz, zzz, zzzz
ZZZ, ZZZZ
g
g,
w, ww

# This is a special value used to mark literal format values.
# See the doc comment for `TimeFormat.patterns`.
Expand Down Expand Up @@ -1642,6 +1664,8 @@ proc stringToPattern(str: string): FormatPattern =
of "ZZZ": result = ZZZ
of "ZZZZ": result = ZZZZ
of "g": result = g
of "w": result = w
of "ww": result = ww
else: raise newException(TimeFormatParseError,
"'" & str & "' is not a valid pattern")

Expand Down Expand Up @@ -1773,6 +1797,10 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string,
else: assert false
of g:
result.add if dt.year < 1: "BC" else: "AD"
of w:
result.add $dt.week
of ww:
result.add dt.week.intToStr(2)
of Lit: assert false # Can't happen

proc parsePattern(input: string, pattern: FormatPattern, i: var int,
Expand Down Expand Up @@ -1949,6 +1977,12 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
i.inc 2
else:
result = false
of w:
let weekOfYear = takeInt(1..2)
result = weekOfYear in YearWeekRange
of ww:
let weekOfYear = takeInt(2..2)
result = weekOfYear in YearWeekRange
of Lit: doAssert false, "Can't happen"

proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
Expand Down
21 changes: 21 additions & 0 deletions tests/stdlib/ttimes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,9 @@ block: # ttimes
check dt.format("yyyy") == "+12346"
check dt.format("uuuu") == "-12345"

check dt.format("w") == "0"
check dt.format("ww") == "00"

expect ValueError:
discard initTimeFormat("'")

Expand Down Expand Up @@ -490,6 +493,15 @@ block: # ttimes
discard parse("foo'bar", "'foo''''bar'")
discard parse("'", "''")

check parse("01/01/0001 1", "MM/dd/yyyy w").year == 1
check parse("01/01/0001 01", "MM/dd/yyyy ww").year == 1

expect TimeParseError:
discard parse("01/01/0001 99", "MM/dd/yyyy ww")

expect TimeParseError:
discard parse("01/01/0001 1", "MM/dd/yyyy ww")

parseTestExcp("2000 A", "yyyy g")

block: # parse locale
Expand Down Expand Up @@ -646,3 +658,12 @@ block: # ttimes
doAssert initDuration(milliseconds = 500).inMilliseconds == 500
doAssert initDuration(milliseconds = -500).inMilliseconds == -500
doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999

block: # week
check dateTime(2021, mJan, 1).week == 0
check dateTime(2021, mJan, 2).week == 0
check dateTime(2021, mJan, 3).week == 0
check dateTime(2021, mJan, 4).week == 1
check dateTime(2021, mDec, 29).week == 52
check dateTime(2021, mDec, 30).week == 52
check dateTime(2021, mDec, 31).week == 52