fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
83
julia-0.6.3/share/julia/base/dates/Dates.jl
Normal file
83
julia-0.6.3/share/julia/base/dates/Dates.jl
Normal file
@@ -0,0 +1,83 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
"""
|
||||
Dates
|
||||
|
||||
The `Dates` module provides `Date`, `DateTime`, `Time` types, and related functions.
|
||||
|
||||
The types are not aware of time zones, based on UT seconds
|
||||
(86400 seconds a day, avoiding leap seconds), and
|
||||
use the proleptic Gregorian calendar, as specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
For time zone functionality, see the TimeZones.jl package.
|
||||
|
||||
```jldoctest
|
||||
julia> dt = DateTime(2017,12,31,23,59,59,999)
|
||||
2017-12-31T23:59:59.999
|
||||
|
||||
julia> d1 = Date(Dates.Month(12), Dates.Year(2017))
|
||||
2017-12-01
|
||||
|
||||
julia> d2 = Date("2017-12-31", Dates.DateFormat("y-m-d"))
|
||||
2017-12-31
|
||||
|
||||
julia> Dates.yearmonthday(d2)
|
||||
(2017, 12, 31)
|
||||
|
||||
julia> d2-d1
|
||||
30 days
|
||||
```
|
||||
|
||||
Please see the manual section on [`Date`](@ref) and [`DateTime`](@ref)
|
||||
for more information.
|
||||
"""
|
||||
module Dates
|
||||
|
||||
importall ..Base.Operators
|
||||
import ..Base.broadcast
|
||||
|
||||
using Base.Iterators
|
||||
|
||||
include("types.jl")
|
||||
include("periods.jl")
|
||||
include("accessors.jl")
|
||||
include("query.jl")
|
||||
include("arithmetic.jl")
|
||||
include("conversions.jl")
|
||||
include("ranges.jl")
|
||||
include("adjusters.jl")
|
||||
include("rounding.jl")
|
||||
include("io.jl")
|
||||
include("parse.jl")
|
||||
|
||||
export Period, DatePeriod, TimePeriod,
|
||||
Year, Month, Week, Day, Hour, Minute, Second, Millisecond,
|
||||
Microsecond, Nanosecond,
|
||||
TimeZone, UTC, TimeType, DateTime, Date, Time,
|
||||
# periods.jl
|
||||
canonicalize,
|
||||
# accessors.jl
|
||||
yearmonthday, yearmonth, monthday, year, month, week, day,
|
||||
hour, minute, second, millisecond, dayofmonth,
|
||||
microsecond, nanosecond,
|
||||
# query.jl
|
||||
dayofweek, isleapyear, daysinmonth, daysinyear, dayofyear, dayname, dayabbr,
|
||||
dayofweekofmonth, daysofweekinmonth, monthname, monthabbr,
|
||||
quarterofyear, dayofquarter,
|
||||
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday,
|
||||
Mon, Tue, Wed, Thu, Fri, Sat, Sun,
|
||||
January, February, March, April, May, June,
|
||||
July, August, September, October, November, December,
|
||||
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec,
|
||||
# conversions.jl
|
||||
unix2datetime, datetime2unix, now, today,
|
||||
rata2datetime, datetime2rata, julian2datetime, datetime2julian,
|
||||
# adjusters.jl
|
||||
firstdayofweek, lastdayofweek,
|
||||
firstdayofmonth, lastdayofmonth,
|
||||
firstdayofyear, lastdayofyear,
|
||||
firstdayofquarter, lastdayofquarter,
|
||||
adjust, tonext, toprev, tofirst, tolast,
|
||||
# io.jl
|
||||
ISODateTimeFormat, ISODateFormat, DateFormat, RFC1123Format, @dateformat_str
|
||||
|
||||
end # module
|
||||
144
julia-0.6.3/share/julia/base/dates/accessors.jl
Normal file
144
julia-0.6.3/share/julia/base/dates/accessors.jl
Normal file
@@ -0,0 +1,144 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Convert # of Rata Die days to proleptic Gregorian calendar y,m,d,w
|
||||
# Reference: http://mysite.verizon.net/aesir_research/date/date0.htm
|
||||
function yearmonthday(days)
|
||||
z = days + 306; h = 100z - 25; a = fld(h, 3652425); b = a - fld(a, 4)
|
||||
y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153)
|
||||
d = c - div(153m - 457, 5); return m > 12 ? (y + 1, m - 12, d) : (y, m, d)
|
||||
end
|
||||
function year(days)
|
||||
z = days + 306; h = 100z - 25; a = fld(h, 3652425); b = a - fld(a, 4)
|
||||
y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153)
|
||||
return m > 12 ? y + 1 : y
|
||||
end
|
||||
function yearmonth(days)
|
||||
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4)
|
||||
y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153)
|
||||
return m > 12 ? (y + 1, m - 12) : (y, m)
|
||||
end
|
||||
function month(days)
|
||||
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4)
|
||||
y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153)
|
||||
return m > 12 ? m - 12 : m
|
||||
end
|
||||
function monthday(days)
|
||||
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4)
|
||||
y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153)
|
||||
d = c - div(153m - 457, 5); return m > 12 ? (m - 12, d) : (m, d)
|
||||
end
|
||||
function day(days)
|
||||
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4)
|
||||
y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153)
|
||||
return c - div(153m - 457, 5)
|
||||
end
|
||||
# https://en.wikipedia.org/wiki/Talk:ISO_week_date#Algorithms
|
||||
const WEEK_INDEX = (15, 23, 3, 11)
|
||||
function week(days)
|
||||
w = div(abs(days - 1), 7) % 20871
|
||||
c, w = divrem((w + (w >= 10435)), 5218)
|
||||
w = (w * 28 + WEEK_INDEX[c + 1]) % 1461
|
||||
return div(w, 28) + 1
|
||||
end
|
||||
|
||||
# Accessor functions
|
||||
value(dt::TimeType) = dt.instant.periods.value
|
||||
value(t::Time) = t.instant.value
|
||||
days(dt::Date) = value(dt)
|
||||
days(dt::DateTime) = fld(value(dt), 86400000)
|
||||
year(dt::TimeType) = year(days(dt))
|
||||
month(dt::TimeType) = month(days(dt))
|
||||
week(dt::TimeType) = week(days(dt))
|
||||
day(dt::TimeType) = day(days(dt))
|
||||
hour(dt::DateTime) = mod(fld(value(dt), 3600000), 24)
|
||||
minute(dt::DateTime) = mod(fld(value(dt), 60000), 60)
|
||||
second(dt::DateTime) = mod(fld(value(dt), 1000), 60)
|
||||
millisecond(dt::DateTime) = mod(value(dt), 1000)
|
||||
hour(t::Time) = mod(fld(value(t), 3600000000000), Int64(24))
|
||||
minute(t::Time) = mod(fld(value(t), 60000000000), Int64(60))
|
||||
second(t::Time) = mod(fld(value(t), 1000000000), Int64(60))
|
||||
millisecond(t::Time) = mod(fld(value(t), Int64(1000000)), Int64(1000))
|
||||
microsecond(t::Time) = mod(fld(value(t), Int64(1000)), Int64(1000))
|
||||
nanosecond(t::Time) = mod(value(t), Int64(1000))
|
||||
|
||||
dayofmonth(dt::TimeType) = day(dt)
|
||||
|
||||
yearmonth(dt::TimeType) = yearmonth(days(dt))
|
||||
monthday(dt::TimeType) = monthday(days(dt))
|
||||
yearmonthday(dt::TimeType) = yearmonthday(days(dt))
|
||||
|
||||
# Documentation for exported accessors
|
||||
for func in (:year, :month)
|
||||
name = string(func)
|
||||
@eval begin
|
||||
@doc """
|
||||
$($name)(dt::TimeType) -> Int64
|
||||
|
||||
The $($name) of a `Date` or `DateTime` as an [`Int64`](@ref).
|
||||
""" $func(dt::TimeType)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
week(dt::TimeType) -> Int64
|
||||
|
||||
Return the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date) of a `Date` or
|
||||
`DateTime` as an [`Int64`](@ref). Note that the first week of a year is the week that
|
||||
contains the first Thursday of the year which can result in dates prior to January 4th
|
||||
being in the last week of the previous year. For example `week(Date(2005,1,1))` is the 53rd
|
||||
week of 2004.
|
||||
"""
|
||||
week(dt::TimeType)
|
||||
|
||||
for func in (:day, :dayofmonth)
|
||||
name = string(func)
|
||||
@eval begin
|
||||
@doc """
|
||||
$($name)(dt::TimeType) -> Int64
|
||||
|
||||
The day of month of a `Date` or `DateTime` as an [`Int64`](@ref).
|
||||
""" $func(dt::TimeType)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
hour(dt::DateTime) -> Int64
|
||||
|
||||
The hour of day of a `DateTime` as an [`Int64`](@ref).
|
||||
"""
|
||||
hour(dt::DateTime)
|
||||
|
||||
for func in (:minute, :second, :millisecond)
|
||||
name = string(func)
|
||||
@eval begin
|
||||
@doc """
|
||||
$($name)(dt::DateTime) -> Int64
|
||||
|
||||
The $($name) of a `DateTime` as an [`Int64`](@ref).
|
||||
""" $func(dt::DateTime)
|
||||
end
|
||||
end
|
||||
|
||||
for parts in (["year", "month"], ["month", "day"], ["year", "month", "day"])
|
||||
name = join(parts)
|
||||
func = Symbol(name)
|
||||
@eval begin
|
||||
@doc """
|
||||
$($name)(dt::TimeType) -> ($(join(repeated(Int64, length($parts)), ", ")))
|
||||
|
||||
Simultaneously return the $(join($parts, ", ", " and ")) parts of a `Date` or
|
||||
`DateTime`.
|
||||
""" $func(dt::TimeType)
|
||||
end
|
||||
end
|
||||
|
||||
for func in (:hour, :minute, :second, :millisecond, :microsecond, :nanosecond)
|
||||
name = string(func)
|
||||
@eval begin
|
||||
@doc """
|
||||
$($name)(t::Time) -> Int64
|
||||
|
||||
The $($name) of a `Time` as an [`Int64`](@ref).
|
||||
""" $func(t::Time)
|
||||
end
|
||||
end
|
||||
307
julia-0.6.3/share/julia/base/dates/adjusters.jl
Normal file
307
julia-0.6.3/share/julia/base/dates/adjusters.jl
Normal file
@@ -0,0 +1,307 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
### truncation
|
||||
Base.trunc(dt::Date, p::Type{Year}) = Date(UTD(totaldays(year(dt), 1, 1)))
|
||||
Base.trunc(dt::Date, p::Type{Month}) = firstdayofmonth(dt)
|
||||
Base.trunc(dt::Date, p::Type{Day}) = dt
|
||||
|
||||
Base.trunc(dt::DateTime, p::Type{Year}) = DateTime(trunc(Date(dt), Year))
|
||||
Base.trunc(dt::DateTime, p::Type{Month}) = DateTime(trunc(Date(dt), Month))
|
||||
Base.trunc(dt::DateTime, p::Type{Day}) = DateTime(Date(dt))
|
||||
Base.trunc(dt::DateTime, p::Type{Hour}) = dt - Minute(dt) - Second(dt) - Millisecond(dt)
|
||||
Base.trunc(dt::DateTime, p::Type{Minute}) = dt - Second(dt) - Millisecond(dt)
|
||||
Base.trunc(dt::DateTime, p::Type{Second}) = dt - Millisecond(dt)
|
||||
Base.trunc(dt::DateTime, p::Type{Millisecond}) = dt
|
||||
|
||||
Base.trunc(t::Time, p::Type{Hour}) = Time(Hour(t))
|
||||
Base.trunc(t::Time, p::Type{Minute}) = Time(Hour(t), Minute(t))
|
||||
Base.trunc(t::Time, p::Type{Second}) = Time(Hour(t), Minute(t), Second(t))
|
||||
Base.trunc(t::Time, p::Type{Millisecond}) = t - Microsecond(t) - Nanosecond(t)
|
||||
Base.trunc(t::Time, p::Type{Microsecond}) = t - Nanosecond(t)
|
||||
Base.trunc(t::Time, p::Type{Nanosecond}) = t
|
||||
|
||||
"""
|
||||
trunc(dt::TimeType, ::Type{Period}) -> TimeType
|
||||
|
||||
Truncates the value of `dt` according to the provided `Period` type. E.g. if `dt` is
|
||||
`1996-01-01T12:30:00`, then `trunc(dt,Day) == 1996-01-01T00:00:00`.
|
||||
"""
|
||||
Dates.trunc(::Dates.TimeType, ::Type{Dates.Period})
|
||||
|
||||
# Adjusters
|
||||
"""
|
||||
firstdayofweek(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the Monday of its week.
|
||||
"""
|
||||
function firstdayofweek end
|
||||
|
||||
firstdayofweek(dt::Date) = Date(UTD(value(dt) - dayofweek(dt) + 1))
|
||||
firstdayofweek(dt::DateTime) = DateTime(firstdayofweek(Date(dt)))
|
||||
|
||||
"""
|
||||
lastdayofweek(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the Sunday of its week.
|
||||
"""
|
||||
function lastdayofweek end
|
||||
|
||||
lastdayofweek(dt::Date) = Date(UTD(value(dt) + (7 - dayofweek(dt))))
|
||||
lastdayofweek(dt::DateTime) = DateTime(lastdayofweek(Date(dt)))
|
||||
|
||||
"""
|
||||
firstdayofmonth(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the first day of its month.
|
||||
"""
|
||||
function firstdayofmonth end
|
||||
|
||||
firstdayofmonth(dt::Date) = Date(UTD(value(dt) - day(dt) + 1))
|
||||
firstdayofmonth(dt::DateTime) = DateTime(firstdayofmonth(Date(dt)))
|
||||
|
||||
"""
|
||||
lastdayofmonth(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the last day of its month.
|
||||
"""
|
||||
function lastdayofmonth end
|
||||
|
||||
function lastdayofmonth(dt::Date)
|
||||
y, m, d = yearmonthday(dt)
|
||||
return Date(UTD(value(dt) + daysinmonth(y, m) - d))
|
||||
end
|
||||
lastdayofmonth(dt::DateTime) = DateTime(lastdayofmonth(Date(dt)))
|
||||
|
||||
"""
|
||||
firstdayofyear(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the first day of its year.
|
||||
"""
|
||||
function firstdayofyear end
|
||||
|
||||
firstdayofyear(dt::Date) = Date(UTD(value(dt) - dayofyear(dt) + 1))
|
||||
firstdayofyear(dt::DateTime) = DateTime(firstdayofyear(Date(dt)))
|
||||
|
||||
"""
|
||||
lastdayofyear(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the last day of its year.
|
||||
"""
|
||||
function lastdayofyear end
|
||||
|
||||
function lastdayofyear(dt::Date)
|
||||
y, m, d = yearmonthday(dt)
|
||||
return Date(UTD(value(dt) + daysinyear(y) - dayofyear(y, m, d)))
|
||||
end
|
||||
lastdayofyear(dt::DateTime) = DateTime(lastdayofyear(Date(dt)))
|
||||
|
||||
"""
|
||||
firstdayofquarter(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the first day of its quarter.
|
||||
"""
|
||||
function firstdayofquarter end
|
||||
|
||||
function firstdayofquarter(dt::Date)
|
||||
y,m = yearmonth(dt)
|
||||
mm = m < 4 ? 1 : m < 7 ? 4 : m < 10 ? 7 : 10
|
||||
return Date(y, mm, 1)
|
||||
end
|
||||
firstdayofquarter(dt::DateTime) = DateTime(firstdayofquarter(Date(dt)))
|
||||
|
||||
"""
|
||||
lastdayofquarter(dt::TimeType) -> TimeType
|
||||
|
||||
Adjusts `dt` to the last day of its quarter.
|
||||
"""
|
||||
function lastdayofquarter end
|
||||
|
||||
function lastdayofquarter(dt::Date)
|
||||
y,m = yearmonth(dt)
|
||||
mm, d = m < 4 ? (3, 31) : m < 7 ? (6, 30) : m < 10 ? (9, 30) : (12, 31)
|
||||
return Date(y, mm, d)
|
||||
end
|
||||
lastdayofquarter(dt::DateTime) = DateTime(lastdayofquarter(Date(dt)))
|
||||
|
||||
# Temporal Adjusters
|
||||
struct DateFunction
|
||||
f::Function
|
||||
# validate boolean, single-arg inner constructor
|
||||
function DateFunction(f::ANY, dt::TimeType)
|
||||
isa(f(dt), Bool) || throw(ArgumentError("Provided function must take a single TimeType argument and return true or false"))
|
||||
return new(f)
|
||||
end
|
||||
end
|
||||
Base.show(io::IO, df::DateFunction) = println(io, df.f)
|
||||
|
||||
# Core adjuster
|
||||
function adjust(df::DateFunction, start, step, limit)
|
||||
for i = 1:limit
|
||||
df.f(start) && return start
|
||||
start += step
|
||||
end
|
||||
throw(ArgumentError("Adjustment limit reached: $limit iterations"))
|
||||
end
|
||||
|
||||
function adjust(func::Function, start; step::Period=Day(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:adjust, func, "func,start", negate)
|
||||
return adjust(DateFunction(func, start), start, step, limit)
|
||||
end
|
||||
|
||||
# Constructors using DateFunctions
|
||||
|
||||
"""
|
||||
Date(f::Function, y[, m, d]; step=Day(1), limit=10000) -> Date
|
||||
|
||||
Create a `Date` through the adjuster API. The starting point will be constructed from the
|
||||
provided `y, m, d` arguments, and will be adjusted until `f::Function` returns `true`.
|
||||
The step size in adjusting can be provided manually through the `step` keyword.
|
||||
`limit` provides a limit to the max number of iterations the adjustment API will
|
||||
pursue before throwing an error (given that `f::Function` is never satisfied).
|
||||
"""
|
||||
function Date(func::Function, y, m=1, d=1; step::Period=Day(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:Date, func, "func,y,m,d", negate)
|
||||
return adjust(DateFunction(func, Date(y, m, d)), Date(y, m, d), step, limit)
|
||||
end
|
||||
|
||||
"""
|
||||
DateTime(f::Function, y[, m, d, h, mi, s]; step=Day(1), limit=10000) -> DateTime
|
||||
|
||||
Create a `DateTime` through the adjuster API. The starting point will be constructed from
|
||||
the provided `y, m, d...` arguments, and will be adjusted until `f::Function` returns
|
||||
`true`. The step size in adjusting can be provided manually through the `step` keyword.
|
||||
`limit` provides a limit to the max number of iterations the adjustment API will
|
||||
pursue before throwing an error (in the case that `f::Function` is never satisfied).
|
||||
"""
|
||||
DateTime(::Function, args...)
|
||||
|
||||
function DateTime(func::Function, y, m=1; step::Period=Day(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:DateTime, func, "func,y,m", negate)
|
||||
return adjust(DateFunction(func, DateTime(y, m)), DateTime(y, m), step, limit)
|
||||
end
|
||||
function DateTime(func::Function, y, m, d; step::Period=Hour(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:DateTime, func, "func,y,m,d", negate)
|
||||
return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d), step, limit)
|
||||
end
|
||||
function DateTime(func::Function, y, m, d, h; step::Period=Minute(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:DateTime, func, "func,y,m,d,h", negate)
|
||||
return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d, h), step, limit)
|
||||
end
|
||||
function DateTime(func::Function, y, m, d, h, mi; step::Period=Second(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:DateTime, func, "func,y,m,d,h,mi", negate)
|
||||
return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d, h, mi), step, limit)
|
||||
end
|
||||
function DateTime(func::Function, y, m, d, h, mi, s; step::Period=Millisecond(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:DateTime, func, "func,y,m,d,h,mi,s", negate)
|
||||
return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d, h, mi, s), step, limit)
|
||||
end
|
||||
|
||||
"""
|
||||
Time(f::Function, h, mi=0; step::Period=Second(1), limit::Int=10000)
|
||||
Time(f::Function, h, mi, s; step::Period=Millisecond(1), limit::Int=10000)
|
||||
Time(f::Function, h, mi, s, ms; step::Period=Microsecond(1), limit::Int=10000)
|
||||
Time(f::Function, h, mi, s, ms, us; step::Period=Nanosecond(1), limit::Int=10000)
|
||||
|
||||
Create a `Time` through the adjuster API. The starting point will be constructed from the
|
||||
provided `h, mi, s, ms, us` arguments, and will be adjusted until `f::Function` returns `true`.
|
||||
The step size in adjusting can be provided manually through the `step` keyword. `limit`
|
||||
provides a limit to the max number of iterations the adjustment API will pursue before
|
||||
throwing an error (in the case that `f::Function` is never satisfied). Note that the default step
|
||||
will adjust to allow for greater precision for the given arguments; i.e. if hour, minute, and second
|
||||
arguments are provided, the default step will be `Millisecond(1)` instead of `Second(1)`.
|
||||
"""
|
||||
Time(::Function, args...)
|
||||
|
||||
function Time(func::Function, h, mi=0; step::Period=Second(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:Time, func, "func,h,mi", negate)
|
||||
return adjust(DateFunction(func, Time(h, mi)), Time(h, mi), step, limit)
|
||||
end
|
||||
function Time(func::Function, h, mi, s; step::Period=Millisecond(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:Time, func, "func,h,mi,s", negate)
|
||||
return adjust(DateFunction(func, Time(h, mi, s)), Time(h, mi, s), step, limit)
|
||||
end
|
||||
function Time(func::Function, h, mi, s, ms; step::Period=Microsecond(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:Time, func, "func,h,mi,s,ms", negate)
|
||||
return adjust(DateFunction(func, Time(h, mi, s, ms)), Time(h, mi, s, ms), step, limit)
|
||||
end
|
||||
function Time(func::Function, h, mi, s, ms, us; step::Period=Nanosecond(1), negate=nothing, limit::Int=10000)
|
||||
func = deprecate_negate(:Time, func, "func,h,mi,s,ms,us", negate)
|
||||
return adjust(DateFunction(func, Time(h, mi, s, ms, us)), Time(h, mi, s, ms, us), step, limit)
|
||||
end
|
||||
|
||||
# Return the next TimeType that falls on dow
|
||||
ISDAYOFWEEK = Dict(Mon => DateFunction(ismonday, Date(0)),
|
||||
Tue => DateFunction(istuesday, Date(0)),
|
||||
Wed => DateFunction(iswednesday, Date(0)),
|
||||
Thu => DateFunction(isthursday, Date(0)),
|
||||
Fri => DateFunction(isfriday, Date(0)),
|
||||
Sat => DateFunction(issaturday, Date(0)),
|
||||
Sun => DateFunction(issunday, Date(0)))
|
||||
|
||||
# "same" indicates whether the current date can be considered or not
|
||||
"""
|
||||
tonext(dt::TimeType, dow::Int; same::Bool=false) -> TimeType
|
||||
|
||||
Adjusts `dt` to the next day of week corresponding to `dow` with `1 = Monday, 2 = Tuesday,
|
||||
etc`. Setting `same=true` allows the current `dt` to be considered as the next `dow`,
|
||||
allowing for no adjustment to occur.
|
||||
"""
|
||||
tonext(dt::TimeType, dow::Int; same::Bool=false) = adjust(ISDAYOFWEEK[dow], same ? dt : dt + Day(1), Day(1), 7)
|
||||
|
||||
# Return the next TimeType where func evals true using step in incrementing
|
||||
"""
|
||||
tonext(func::Function, dt::TimeType; step=Day(1), limit=10000, same=false) -> TimeType
|
||||
|
||||
Adjusts `dt` by iterating at most `limit` iterations by `step` increments until `func`
|
||||
returns `true`. `func` must take a single `TimeType` argument and return a [`Bool`](@ref).
|
||||
`same` allows `dt` to be considered in satisfying `func`.
|
||||
"""
|
||||
function tonext(func::Function, dt::TimeType; step::Period=Day(1), negate=nothing, limit::Int=10000, same::Bool=false)
|
||||
func = deprecate_negate(:tonext, func, "func,dt", negate)
|
||||
return adjust(DateFunction(func, dt), same ? dt : dt + step, step, limit)
|
||||
end
|
||||
|
||||
"""
|
||||
toprev(dt::TimeType, dow::Int; same::Bool=false) -> TimeType
|
||||
|
||||
Adjusts `dt` to the previous day of week corresponding to `dow` with `1 = Monday, 2 =
|
||||
Tuesday, etc`. Setting `same=true` allows the current `dt` to be considered as the previous
|
||||
`dow`, allowing for no adjustment to occur.
|
||||
"""
|
||||
toprev(dt::TimeType, dow::Int; same::Bool=false) = adjust(ISDAYOFWEEK[dow], same ? dt : dt + Day(-1), Day(-1), 7)
|
||||
|
||||
"""
|
||||
toprev(func::Function, dt::TimeType; step=Day(-1), limit=10000, same=false) -> TimeType
|
||||
|
||||
Adjusts `dt` by iterating at most `limit` iterations by `step` increments until `func`
|
||||
returns `true`. `func` must take a single `TimeType` argument and return a [`Bool`](@ref).
|
||||
`same` allows `dt` to be considered in satisfying `func`.
|
||||
"""
|
||||
function toprev(func::Function, dt::TimeType; step::Period=Day(-1), negate=nothing, limit::Int=10000, same::Bool=false)
|
||||
func = deprecate_negate(:toprev, func, "func,dt", negate)
|
||||
return adjust(DateFunction(func, dt), same ? dt : dt + step, step, limit)
|
||||
end
|
||||
|
||||
# Return the first TimeType that falls on dow in the Month or Year
|
||||
"""
|
||||
tofirst(dt::TimeType, dow::Int; of=Month) -> TimeType
|
||||
|
||||
Adjusts `dt` to the first `dow` of its month. Alternatively, `of=Year` will adjust to the
|
||||
first `dow` of the year.
|
||||
"""
|
||||
function tofirst(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month)
|
||||
dt = of <: Month ? firstdayofmonth(dt) : firstdayofyear(dt)
|
||||
return adjust(ISDAYOFWEEK[dow], dt, Day(1), 366)
|
||||
end
|
||||
|
||||
# Return the last TimeType that falls on dow in the Month or Year
|
||||
"""
|
||||
tolast(dt::TimeType, dow::Int; of=Month) -> TimeType
|
||||
|
||||
Adjusts `dt` to the last `dow` of its month. Alternatively, `of=Year` will adjust to the
|
||||
last `dow` of the year.
|
||||
"""
|
||||
function tolast(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month)
|
||||
dt = of <: Month ? lastdayofmonth(dt) : lastdayofyear(dt)
|
||||
return adjust(ISDAYOFWEEK[dow], dt, Day(-1), 366)
|
||||
end
|
||||
98
julia-0.6.3/share/julia/base/dates/arithmetic.jl
Normal file
98
julia-0.6.3/share/julia/base/dates/arithmetic.jl
Normal file
@@ -0,0 +1,98 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Instant arithmetic
|
||||
(+)(x::Instant) = x
|
||||
(-)(x::T, y::T) where {T<:Instant} = x.periods - y.periods
|
||||
|
||||
# TimeType arithmetic
|
||||
(+)(x::TimeType) = x
|
||||
(-)(x::T, y::T) where {T<:TimeType} = x.instant - y.instant
|
||||
|
||||
# Date-Time arithmetic
|
||||
"""
|
||||
dt::Date + t::Time -> DateTime
|
||||
|
||||
The addition of a `Date` with a `Time` produces a `DateTime`. The hour, minute, second, and millisecond parts of
|
||||
the `Time` are used along with the year, month, and day of the `Date` to create the new `DateTime`.
|
||||
Non-zero microseconds or nanoseconds in the `Time` type will result in an `InexactError` being thrown.
|
||||
"""
|
||||
function (+)(dt::Date, t::Time)
|
||||
(microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError())
|
||||
y, m, d = yearmonthday(dt)
|
||||
return DateTime(y, m, d, hour(t), minute(t), second(t), millisecond(t))
|
||||
end
|
||||
(+)(t::Time, dt::Date) = dt + t
|
||||
|
||||
# TimeType-Year arithmetic
|
||||
function (+)(dt::DateTime, y::Year)
|
||||
oy, m, d = yearmonthday(dt); ny = oy + value(y); ld = daysinmonth(ny, m)
|
||||
return DateTime(ny, m, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt))
|
||||
end
|
||||
function (+)(dt::Date,y::Year)
|
||||
oy, m, d = yearmonthday(dt); ny = oy + value(y); ld = daysinmonth(ny, m)
|
||||
return Date(ny, m, d <= ld ? d : ld)
|
||||
end
|
||||
function (-)(dt::DateTime,y::Year)
|
||||
oy, m, d = yearmonthday(dt); ny = oy - value(y); ld = daysinmonth(ny, m)
|
||||
return DateTime(ny, m, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt))
|
||||
end
|
||||
function (-)(dt::Date,y::Year)
|
||||
oy, m, d = yearmonthday(dt); ny = oy - value(y); ld = daysinmonth(ny, m)
|
||||
return Date(ny, m, d <= ld ? d : ld)
|
||||
end
|
||||
|
||||
# TimeType-Month arithmetic
|
||||
# monthwrap adds two months with wraparound behavior (i.e. 12 + 1 == 1)
|
||||
monthwrap(m1, m2) = (v = mod1(m1 + m2, 12); return v < 0 ? 12 + v : v)
|
||||
# yearwrap takes a starting year/month and a month to add and returns
|
||||
# the resulting year with wraparound behavior (i.e. 2000-12 + 1 == 2001)
|
||||
yearwrap(y, m1, m2) = y + fld(m1 + m2 - 1, 12)
|
||||
|
||||
function (+)(dt::DateTime, z::Month)
|
||||
y,m,d = yearmonthday(dt)
|
||||
ny = yearwrap(y, m, value(z))
|
||||
mm = monthwrap(m, value(z)); ld = daysinmonth(ny, mm)
|
||||
return DateTime(ny, mm, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt))
|
||||
end
|
||||
function (+)(dt::Date, z::Month)
|
||||
y,m,d = yearmonthday(dt)
|
||||
ny = yearwrap(y, m, value(z))
|
||||
mm = monthwrap(m, value(z)); ld = daysinmonth(ny, mm)
|
||||
return Date(ny, mm, d <= ld ? d : ld)
|
||||
end
|
||||
function (-)(dt::DateTime, z::Month)
|
||||
y,m,d = yearmonthday(dt)
|
||||
ny = yearwrap(y, m, -value(z))
|
||||
mm = monthwrap(m, -value(z)); ld = daysinmonth(ny, mm)
|
||||
return DateTime(ny, mm, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt))
|
||||
end
|
||||
function (-)(dt::Date, z::Month)
|
||||
y,m,d = yearmonthday(dt)
|
||||
ny = yearwrap(y, m, -value(z))
|
||||
mm = monthwrap(m, -value(z)); ld = daysinmonth(ny, mm)
|
||||
return Date(ny, mm, d <= ld ? d : ld)
|
||||
end
|
||||
(+)(x::Date, y::Week) = return Date(UTD(value(x) + 7 * value(y)))
|
||||
(-)(x::Date, y::Week) = return Date(UTD(value(x) - 7 * value(y)))
|
||||
(+)(x::Date, y::Day) = return Date(UTD(value(x) + value(y)))
|
||||
(-)(x::Date, y::Day) = return Date(UTD(value(x) - value(y)))
|
||||
(+)(x::DateTime, y::Period) = return DateTime(UTM(value(x) + toms(y)))
|
||||
(-)(x::DateTime, y::Period) = return DateTime(UTM(value(x) - toms(y)))
|
||||
(+)(x::Time, y::TimePeriod) = return Time(Nanosecond(value(x) + tons(y)))
|
||||
(-)(x::Time, y::TimePeriod) = return Time(Nanosecond(value(x) - tons(y)))
|
||||
(+)(y::Period, x::TimeType) = x + y
|
||||
|
||||
(+)(x::AbstractArray{<:TimeType}, y::GeneralPeriod) = x .+ y
|
||||
(+)(x::StridedArray{<:GeneralPeriod}, y::TimeType) = x .+ y
|
||||
(+)(y::GeneralPeriod, x::AbstractArray{<:TimeType}) = x .+ y
|
||||
(+)(y::TimeType, x::StridedArray{<:GeneralPeriod}) = x .+ y
|
||||
(-)(x::AbstractArray{<:TimeType}, y::GeneralPeriod) = x .- y
|
||||
(-)(x::StridedArray{<:GeneralPeriod}, y::TimeType) = x .- y
|
||||
|
||||
# TimeType, AbstractArray{TimeType}
|
||||
(-)(x::AbstractArray{T}, y::T) where {T<:TimeType} = x .- y
|
||||
(-)(y::T, x::AbstractArray{T}) where {T<:TimeType} = y .- x
|
||||
|
||||
# AbstractArray{TimeType}, AbstractArray{TimeType}
|
||||
(-)(x::OrdinalRange{T}, y::OrdinalRange{T}) where {T<:TimeType} = collect(x) - collect(y)
|
||||
(-)(x::Range{T}, y::Range{T}) where {T<:TimeType} = collect(x) - collect(y)
|
||||
121
julia-0.6.3/share/julia/base/dates/conversions.jl
Normal file
121
julia-0.6.3/share/julia/base/dates/conversions.jl
Normal file
@@ -0,0 +1,121 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Conversion/Promotion
|
||||
|
||||
"""
|
||||
Date(dt::DateTime) -> Date
|
||||
|
||||
Converts a `DateTime` to a `Date`. The hour, minute, second, and millisecond parts of
|
||||
the `DateTime` are truncated, so only the year, month and day parts are used in
|
||||
construction.
|
||||
"""
|
||||
Date(dt::TimeType) = convert(Date, dt)
|
||||
|
||||
"""
|
||||
DateTime(dt::Date) -> DateTime
|
||||
|
||||
Converts a `Date` to a `DateTime`. The hour, minute, second, and millisecond parts of
|
||||
the new `DateTime` are assumed to be zero.
|
||||
"""
|
||||
DateTime(dt::TimeType) = convert(DateTime, dt)
|
||||
|
||||
"""
|
||||
Time(dt::DateTime) -> Time
|
||||
|
||||
Converts a `DateTime` to a `Time`. The hour, minute, second, and millisecond parts of
|
||||
the `DateTime` are used to create the new `Time`. Microsecond and nanoseconds are zero by default.
|
||||
"""
|
||||
Time(dt::DateTime) = convert(Time, dt)
|
||||
|
||||
Base.convert(::Type{DateTime}, dt::Date) = DateTime(UTM(value(dt) * 86400000))
|
||||
Base.convert(::Type{Date}, dt::DateTime) = Date(UTD(days(dt)))
|
||||
Base.convert(::Type{Time}, dt::DateTime) = Time(Nanosecond((value(dt) % 86400000) * 1000000))
|
||||
|
||||
Base.convert(::Type{DateTime},x::Millisecond) = DateTime(Dates.UTInstant(x)) # Converts Rata Die milliseconds to a DateTime
|
||||
Base.convert(::Type{Millisecond},dt::DateTime) = Millisecond(value(dt)) # Converts DateTime to Rata Die milliseconds
|
||||
Base.convert(::Type{Date},x::Day) = Date(Dates.UTInstant(x)) # Converts Rata Die days to a Date
|
||||
Base.convert(::Type{Day},dt::Date) = Day(value(dt)) # Converts Date to Rata Die days
|
||||
|
||||
### External Conversions
|
||||
const UNIXEPOCH = value(DateTime(1970)) #Rata Die milliseconds for 1970-01-01T00:00:00
|
||||
|
||||
"""
|
||||
unix2datetime(x) -> DateTime
|
||||
|
||||
Takes the number of seconds since unix epoch `1970-01-01T00:00:00` and converts to the
|
||||
corresponding `DateTime`.
|
||||
"""
|
||||
function unix2datetime(x)
|
||||
rata = UNIXEPOCH + round(Int64, Int64(1000) * x)
|
||||
return DateTime(UTM(rata))
|
||||
end
|
||||
"""
|
||||
datetime2unix(dt::DateTime) -> Float64
|
||||
|
||||
Takes the given `DateTime` and returns the number of seconds
|
||||
since the unix epoch `1970-01-01T00:00:00` as a [`Float64`](@ref).
|
||||
"""
|
||||
datetime2unix(dt::DateTime) = (value(dt) - UNIXEPOCH) / 1000.0
|
||||
|
||||
"""
|
||||
now() -> DateTime
|
||||
|
||||
Returns a `DateTime` corresponding to the user's system time including the system timezone
|
||||
locale.
|
||||
"""
|
||||
function now()
|
||||
tv = Libc.TimeVal()
|
||||
tm = Libc.TmStruct(tv.sec)
|
||||
return DateTime(tm.year + 1900, tm.month + 1, tm.mday, tm.hour, tm.min, tm.sec, div(tv.usec, 1000))
|
||||
end
|
||||
|
||||
"""
|
||||
today() -> Date
|
||||
|
||||
Returns the date portion of `now()`.
|
||||
"""
|
||||
today() = Date(now())
|
||||
|
||||
"""
|
||||
now(::Type{UTC}) -> DateTime
|
||||
|
||||
Returns a `DateTime` corresponding to the user's system time as UTC/GMT.
|
||||
"""
|
||||
now(::Type{UTC}) = unix2datetime(time())
|
||||
|
||||
"""
|
||||
rata2datetime(days) -> DateTime
|
||||
|
||||
Takes the number of Rata Die days since epoch `0000-12-31T00:00:00` and returns the
|
||||
corresponding `DateTime`.
|
||||
"""
|
||||
rata2datetime(days) = DateTime(yearmonthday(days)...)
|
||||
|
||||
"""
|
||||
datetime2rata(dt::TimeType) -> Int64
|
||||
|
||||
Returns the number of Rata Die days since epoch from the given `Date` or `DateTime`.
|
||||
"""
|
||||
datetime2rata(dt::TimeType) = days(dt)
|
||||
|
||||
# Julian conversions
|
||||
const JULIANEPOCH = value(DateTime(-4713, 11, 24, 12))
|
||||
|
||||
"""
|
||||
julian2datetime(julian_days) -> DateTime
|
||||
|
||||
Takes the number of Julian calendar days since epoch `-4713-11-24T12:00:00` and returns the
|
||||
corresponding `DateTime`.
|
||||
"""
|
||||
function julian2datetime(f)
|
||||
rata = JULIANEPOCH + round(Int64, Int64(86400000) * f)
|
||||
return DateTime(UTM(rata))
|
||||
end
|
||||
|
||||
"""
|
||||
datetime2julian(dt::DateTime) -> Float64
|
||||
|
||||
Takes the given `DateTime` and returns the number of Julian calendar days since the julian
|
||||
epoch `-4713-11-24T12:00:00` as a [`Float64`](@ref).
|
||||
"""
|
||||
datetime2julian(dt::DateTime) = (value(dt) - JULIANEPOCH) / 86400000.0
|
||||
547
julia-0.6.3/share/julia/base/dates/io.jl
Normal file
547
julia-0.6.3/share/julia/base/dates/io.jl
Normal file
@@ -0,0 +1,547 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
"""
|
||||
AbstractDateToken
|
||||
|
||||
A token used in parsing or formatting a date time string. Each subtype must
|
||||
define the tryparsenext and format methods.
|
||||
|
||||
"""
|
||||
abstract type AbstractDateToken end
|
||||
|
||||
"""
|
||||
tryparsenext(tok::AbstractDateToken, str::String, i::Int, len::Int, locale::DateLocale)
|
||||
|
||||
`tryparsenext` parses for the `tok` token in `str` starting at index `i`.
|
||||
`len` is the length of the string. parsing can be optionally based on the
|
||||
`locale`. If a `tryparsenext` method does not need a locale, it can leave
|
||||
the argument out in the method definition.
|
||||
|
||||
Returns a tuple of 2 elements `(res, idx)`, where:
|
||||
|
||||
* `res` is a `Nullable{T}` - the result of the parsing, null if parsing failed.
|
||||
* `idx` is an `Int` - if parsing failed, the index at which it failed; if
|
||||
parsing succeeded, `idx` is the index _after_ the index at which parsing ended.
|
||||
"""
|
||||
function tryparsenext end
|
||||
|
||||
"""
|
||||
format(io::IO, tok::AbstractDateToken, dt::TimeType, locale)
|
||||
|
||||
Format the `tok` token from `dt` and write it to `io`. The formatting can
|
||||
be based on `locale`.
|
||||
|
||||
All subtypes of `AbstractDateToken` must define this method in order
|
||||
to be able to print a Date / DateTime object according to a `DateFormat`
|
||||
containing that token.
|
||||
"""
|
||||
function format end
|
||||
|
||||
# fallback to tryparsenext/format methods that don't care about locale
|
||||
@inline function tryparsenext(d::AbstractDateToken, str, i, len, locale)
|
||||
tryparsenext(d, str, i, len)
|
||||
end
|
||||
|
||||
function Base.string(t::Time)
|
||||
h, mi, s = hour(t), minute(t), second(t)
|
||||
hh = lpad(h, 2, "0")
|
||||
mii = lpad(mi, 2, "0")
|
||||
ss = lpad(s, 2, "0")
|
||||
nss = tons(Millisecond(t)) + tons(Microsecond(t)) + tons(Nanosecond(t))
|
||||
ns = nss == 0 ? "" : rstrip(@sprintf("%.9f", nss / 1e+9)[2:end], '0')
|
||||
return "$hh:$mii:$ss$ns"
|
||||
end
|
||||
|
||||
Base.show(io::IO, x::Time) = print(io, string(x))
|
||||
|
||||
@inline function format(io, d::AbstractDateToken, dt, locale)
|
||||
format(io, d, dt)
|
||||
end
|
||||
|
||||
# Information for parsing and formatting date time values.
|
||||
struct DateFormat{S, T<:Tuple}
|
||||
tokens::T
|
||||
locale::DateLocale
|
||||
end
|
||||
|
||||
### Token types ###
|
||||
|
||||
struct DatePart{letter} <: AbstractDateToken
|
||||
width::Int
|
||||
fixed::Bool
|
||||
end
|
||||
|
||||
@inline min_width(d::DatePart) = d.fixed ? d.width : 1
|
||||
@inline max_width(d::DatePart) = d.fixed ? d.width : 0
|
||||
|
||||
function _show_content(io::IO, d::DatePart{c}) where c
|
||||
for i = 1:d.width
|
||||
write(io, c)
|
||||
end
|
||||
end
|
||||
|
||||
function Base.show(io::IO, d::DatePart{c}) where c
|
||||
write(io, "DatePart(")
|
||||
_show_content(io, d)
|
||||
write(io, ")")
|
||||
end
|
||||
|
||||
### Parse tokens
|
||||
|
||||
for c in "yYmdHMS"
|
||||
@eval begin
|
||||
@inline function tryparsenext(d::DatePart{$c}, str, i, len)
|
||||
tryparsenext_base10(str, i, len, min_width(d), max_width(d))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for (tok, fn) in zip("uUeE", [monthabbr_to_value, monthname_to_value, dayabbr_to_value, dayname_to_value])
|
||||
@eval @inline function tryparsenext(d::DatePart{$tok}, str, i, len, locale)
|
||||
word, i = tryparsenext_word(str, i, len, locale, max_width(d))
|
||||
val = isnull(word) ? 0 : $fn(get(word), locale)
|
||||
if val == 0
|
||||
return Nullable{Int64}(), i
|
||||
else
|
||||
return Nullable{Int64}(val), i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@inline function tryparsenext(d::DatePart{'s'}, str, i, len)
|
||||
ms, ii = tryparsenext_base10(str, i, len, min_width(d), max_width(d))
|
||||
if !isnull(ms)
|
||||
val = get(ms)
|
||||
len = ii - i
|
||||
if len > 3
|
||||
val, r = divrem(val, Int64(10) ^ (len - 3))
|
||||
r == 0 || throw(InexactError())
|
||||
else
|
||||
val *= Int64(10) ^ (3 - len)
|
||||
end
|
||||
ms = Nullable{Int64}(val)
|
||||
end
|
||||
return ms, ii
|
||||
end
|
||||
|
||||
### Format tokens
|
||||
|
||||
for (c, fn) in zip("YmdHMS", [year, month, day, hour, minute, second])
|
||||
@eval function format(io, d::DatePart{$c}, dt)
|
||||
write(io, dec($fn(dt), d.width))
|
||||
end
|
||||
end
|
||||
|
||||
for (tok, fn) in zip("uU", [monthabbr, monthname])
|
||||
@eval function format(io, d::DatePart{$tok}, dt, locale)
|
||||
write(io, $fn(month(dt), locale))
|
||||
end
|
||||
end
|
||||
|
||||
for (tok, fn) in zip("eE", [dayabbr, dayname])
|
||||
@eval function format(io, ::DatePart{$tok}, dt, locale)
|
||||
write(io, $fn(dayofweek(dt), locale))
|
||||
end
|
||||
end
|
||||
|
||||
@inline function format(io, d::DatePart{'y'}, dt)
|
||||
y = year(dt)
|
||||
n = d.width
|
||||
|
||||
# the last n digits of y
|
||||
# will be 0 padded if y has less than n digits
|
||||
str = dec(y, n)
|
||||
l = endof(str)
|
||||
if l == n
|
||||
# fast path
|
||||
write(io, str)
|
||||
else
|
||||
write(io, SubString(str, l - (n - 1), l))
|
||||
end
|
||||
end
|
||||
|
||||
function format(io, d::DatePart{'s'}, dt)
|
||||
ms = millisecond(dt)
|
||||
if ms % 100 == 0
|
||||
str = dec(div(ms, 100), 1)
|
||||
elseif ms % 10 == 0
|
||||
str = dec(div(ms, 10), 2)
|
||||
else
|
||||
str = dec(ms, 3)
|
||||
end
|
||||
|
||||
write(io, rpad(str, d.width, '0'))
|
||||
end
|
||||
|
||||
### Delimiters
|
||||
|
||||
struct Delim{T, length} <: AbstractDateToken
|
||||
d::T
|
||||
end
|
||||
|
||||
Delim(d::Char) = Delim{Char, 1}(d)
|
||||
Delim(d::String) = Delim{String, length(d)}(d)
|
||||
|
||||
@inline function tryparsenext(d::Delim{Char, N}, str, i::Int, len) where N
|
||||
R = Nullable{Bool}
|
||||
for j=1:N
|
||||
i > len && return (R(), i)
|
||||
c, i = next(str, i)
|
||||
c != d.d && return (R(), i)
|
||||
end
|
||||
return R(true), i
|
||||
end
|
||||
|
||||
@inline function tryparsenext(d::Delim{String, N}, str, i::Int, len) where N
|
||||
R = Nullable{Bool}
|
||||
i1 = i
|
||||
i2 = start(d.d)
|
||||
for j = 1:N
|
||||
if i1 > len
|
||||
return R(), i1
|
||||
end
|
||||
c1, i1 = next(str, i1)
|
||||
c2, i2 = next(d.d, i2)
|
||||
if c1 != c2
|
||||
return R(), i1
|
||||
end
|
||||
end
|
||||
return R(true), i1
|
||||
end
|
||||
|
||||
@inline function format(io, d::Delim, dt, locale)
|
||||
write(io, d.d)
|
||||
end
|
||||
|
||||
function _show_content(io::IO, d::Delim{Char, N}) where N
|
||||
if d.d in keys(CONVERSION_SPECIFIERS)
|
||||
for i = 1:N
|
||||
write(io, '\\', d.d)
|
||||
end
|
||||
else
|
||||
for i = 1:N
|
||||
write(io, d.d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _show_content(io::IO, d::Delim)
|
||||
for c in d.d
|
||||
if c in keys(CONVERSION_SPECIFIERS)
|
||||
write(io, '\\')
|
||||
end
|
||||
write(io, c)
|
||||
end
|
||||
end
|
||||
|
||||
function Base.show(io::IO, d::Delim)
|
||||
write(io, "Delim(")
|
||||
_show_content(io, d)
|
||||
write(io, ")")
|
||||
end
|
||||
|
||||
### DateFormat construction
|
||||
|
||||
abstract type DayOfWeekToken end # special addition to Period types
|
||||
|
||||
# Map conversion specifiers or character codes to tokens.
|
||||
# Note: Allow addition of new character codes added by packages
|
||||
const CONVERSION_SPECIFIERS = Dict{Char, Type}(
|
||||
'y' => Year,
|
||||
'Y' => Year,
|
||||
'm' => Month,
|
||||
'u' => Month,
|
||||
'U' => Month,
|
||||
'e' => DayOfWeekToken,
|
||||
'E' => DayOfWeekToken,
|
||||
'd' => Day,
|
||||
'H' => Hour,
|
||||
'M' => Minute,
|
||||
'S' => Second,
|
||||
's' => Millisecond,
|
||||
)
|
||||
|
||||
# Default values are needed when a conversion specifier is used in a DateFormat for parsing
|
||||
# and we have reached the end of the input string.
|
||||
# Note: Allow `Any` value as a default to support extensibility
|
||||
const CONVERSION_DEFAULTS = Dict{Type, Any}(
|
||||
Year => Int64(1),
|
||||
Month => Int64(1),
|
||||
DayOfWeekToken => Int64(0),
|
||||
Day => Int64(1),
|
||||
Hour => Int64(0),
|
||||
Minute => Int64(0),
|
||||
Second => Int64(0),
|
||||
Millisecond => Int64(0),
|
||||
)
|
||||
|
||||
# Specifies the required fields in order to parse a TimeType
|
||||
# Note: Allows for addition of new TimeTypes
|
||||
const CONVERSION_TRANSLATIONS = Dict{Type{<:TimeType}, Tuple}(
|
||||
Date => (Year, Month, Day),
|
||||
DateTime => (Year, Month, Day, Hour, Minute, Second, Millisecond),
|
||||
)
|
||||
|
||||
"""
|
||||
DateFormat(format::AbstractString, locale="english") -> DateFormat
|
||||
|
||||
Construct a date formatting object that can be used for parsing date strings or
|
||||
formatting a date object as a string. The following character codes can be used to construct the `format`
|
||||
string:
|
||||
|
||||
| Code | Matches | Comment |
|
||||
|:-----------|:----------|:-------------------------------------------------------------|
|
||||
| `y` | 1996, 96 | Returns year of 1996, 0096 |
|
||||
| `Y` | 1996, 96 | Returns year of 1996, 0096. Equivalent to `y` |
|
||||
| `m` | 1, 01 | Matches 1 or 2-digit months |
|
||||
| `u` | Jan | Matches abbreviated months according to the `locale` keyword |
|
||||
| `U` | January | Matches full month names according to the `locale` keyword |
|
||||
| `d` | 1, 01 | Matches 1 or 2-digit days |
|
||||
| `H` | 00 | Matches hours |
|
||||
| `M` | 00 | Matches minutes |
|
||||
| `S` | 00 | Matches seconds |
|
||||
| `s` | .500 | Matches milliseconds |
|
||||
| `e` | Mon, Tues | Matches abbreviated days of the week |
|
||||
| `E` | Monday | Matches full name days of the week |
|
||||
| `yyyymmdd` | 19960101 | Matches fixed-width year, month, and day |
|
||||
|
||||
Characters not listed above are normally treated as delimiters between date and time slots.
|
||||
For example a `dt` string of "1996-01-15T00:00:00.0" would have a `format` string like
|
||||
"y-m-dTH:M:S.s". If you need to use a code character as a delimiter you can escape it using
|
||||
backslash. The date "1995y01m" would have the format "y\\ym\\m".
|
||||
|
||||
Creating a DateFormat object is expensive. Whenever possible, create it once and use it many times
|
||||
or try the `dateformat""` string macro. Using this macro creates the DateFormat object once at
|
||||
macro expansion time and reuses it later. see [`@dateformat_str`](@ref).
|
||||
|
||||
See [`DateTime`](@ref) and [`format`](@ref) for how to use a DateFormat object to parse and write Date strings
|
||||
respectively.
|
||||
"""
|
||||
function DateFormat(f::AbstractString, locale::DateLocale=ENGLISH)
|
||||
tokens = AbstractDateToken[]
|
||||
prev = ()
|
||||
prev_offset = 1
|
||||
|
||||
letters = String(collect(keys(CONVERSION_SPECIFIERS)))
|
||||
for m in eachmatch(Regex("(?<!\\\\)([\\Q$letters\\E])\\1*"), f)
|
||||
tran = replace(f[prev_offset:m.offset - 1], r"\\(.)", s"\1")
|
||||
|
||||
if !isempty(prev)
|
||||
letter, width = prev
|
||||
typ = CONVERSION_SPECIFIERS[letter]
|
||||
|
||||
push!(tokens, DatePart{letter}(width, isempty(tran)))
|
||||
end
|
||||
|
||||
if !isempty(tran)
|
||||
push!(tokens, Delim(length(tran) == 1 ? first(tran) : tran))
|
||||
end
|
||||
|
||||
letter = f[m.offset]
|
||||
width = length(m.match)
|
||||
|
||||
prev = (letter, width)
|
||||
prev_offset = m.offset + width
|
||||
end
|
||||
|
||||
tran = replace(f[prev_offset:endof(f)], r"\\(.)", s"\1")
|
||||
|
||||
if !isempty(prev)
|
||||
letter, width = prev
|
||||
typ = CONVERSION_SPECIFIERS[letter]
|
||||
|
||||
push!(tokens, DatePart{letter}(width, false))
|
||||
end
|
||||
|
||||
if !isempty(tran)
|
||||
push!(tokens, Delim(length(tran) == 1 ? first(tran) : tran))
|
||||
end
|
||||
|
||||
tokens_tuple = (tokens...)
|
||||
return DateFormat{Symbol(f),typeof(tokens_tuple)}(tokens_tuple, locale)
|
||||
end
|
||||
|
||||
function DateFormat(f::AbstractString, locale::AbstractString)
|
||||
DateFormat(f, LOCALES[locale])
|
||||
end
|
||||
|
||||
function Base.show(io::IO, df::DateFormat)
|
||||
write(io, "dateformat\"")
|
||||
for t in df.tokens
|
||||
_show_content(io, t)
|
||||
end
|
||||
write(io, '"')
|
||||
end
|
||||
|
||||
"""
|
||||
dateformat"Y-m-d H:M:S"
|
||||
|
||||
Create a [`DateFormat`](@ref) object. Similar to `DateFormat("Y-m-d H:M:S")`
|
||||
but creates the DateFormat object once during macro expansion.
|
||||
|
||||
See [`DateFormat`](@ref) for details about format specifiers.
|
||||
"""
|
||||
macro dateformat_str(str)
|
||||
DateFormat(str)
|
||||
end
|
||||
|
||||
# Standard formats
|
||||
const ISODateTimeFormat = DateFormat("yyyy-mm-dd\\THH:MM:SS.s")
|
||||
const ISODateFormat = DateFormat("yyyy-mm-dd")
|
||||
const RFC1123Format = DateFormat("e, dd u yyyy HH:MM:SS")
|
||||
|
||||
default_format(::Type{DateTime}) = ISODateTimeFormat
|
||||
default_format(::Type{Date}) = ISODateFormat
|
||||
|
||||
### API
|
||||
|
||||
const Locale = Union{DateLocale, String}
|
||||
|
||||
"""
|
||||
DateTime(dt::AbstractString, format::AbstractString; locale="english") -> DateTime
|
||||
|
||||
Construct a `DateTime` by parsing the `dt` date string following the pattern given in
|
||||
the `format` string.
|
||||
|
||||
This method creates a `DateFormat` object each time it is called. If you are parsing many
|
||||
date strings of the same format, consider creating a [`DateFormat`](@ref) object once and using
|
||||
that as the second argument instead.
|
||||
"""
|
||||
function DateTime(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH)
|
||||
parse(DateTime, dt, DateFormat(format, locale))
|
||||
end
|
||||
|
||||
"""
|
||||
DateTime(dt::AbstractString, df::DateFormat) -> DateTime
|
||||
|
||||
Construct a `DateTime` by parsing the `dt` date string following the pattern given in
|
||||
the [`DateFormat`](@ref) object. Similar to
|
||||
`DateTime(::AbstractString, ::AbstractString)` but more efficient when repeatedly parsing
|
||||
similarly formatted date strings with a pre-created `DateFormat` object.
|
||||
"""
|
||||
DateTime(dt::AbstractString, df::DateFormat=ISODateTimeFormat) = parse(DateTime, dt, df)
|
||||
|
||||
"""
|
||||
Date(dt::AbstractString, format::AbstractString; locale="english") -> Date
|
||||
|
||||
Construct a `Date` object by parsing a `dt` date string following the pattern given in the
|
||||
`format` string. Follows the same conventions as
|
||||
`DateTime(::AbstractString, ::AbstractString)`.
|
||||
"""
|
||||
function Date(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH)
|
||||
parse(Date, dt, DateFormat(format, locale))
|
||||
end
|
||||
|
||||
"""
|
||||
Date(dt::AbstractString, df::DateFormat) -> Date
|
||||
|
||||
Parse a date from a date string `dt` using a `DateFormat` object `df`.
|
||||
"""
|
||||
Date(dt::AbstractString,df::DateFormat=ISODateFormat) = parse(Date, dt, df)
|
||||
|
||||
@generated function format{S, T}(io::IO, dt::TimeType, fmt::DateFormat{S, T})
|
||||
N = nfields(T)
|
||||
quote
|
||||
ts = fmt.tokens
|
||||
loc = fmt.locale
|
||||
Base.@nexprs $N i -> format(io, ts[i], dt, loc)
|
||||
end
|
||||
end
|
||||
|
||||
function format(dt::TimeType, fmt::DateFormat, bufsize=12)
|
||||
# preallocate to reduce resizing
|
||||
io = IOBuffer(Vector{UInt8}(bufsize), true, true)
|
||||
format(io, dt, fmt)
|
||||
String(io.data[1:io.ptr - 1])
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
format(dt::TimeType, format::AbstractString; locale="english") -> AbstractString
|
||||
|
||||
Construct a string by using a `TimeType` object and applying the provided `format`. The
|
||||
following character codes can be used to construct the `format` string:
|
||||
|
||||
| Code | Examples | Comment |
|
||||
|:-----------|:----------|:-------------------------------------------------------------|
|
||||
| `y` | 6 | Numeric year with a fixed width |
|
||||
| `Y` | 1996 | Numeric year with a minimum width |
|
||||
| `m` | 1, 12 | Numeric month with a minimum width |
|
||||
| `u` | Jan | Month name shortened to 3-chars according to the `locale` |
|
||||
| `U` | January | Full month name according to the `locale` keyword |
|
||||
| `d` | 1, 31 | Day of the month with a minimum width |
|
||||
| `H` | 0, 23 | Hour (24-hour clock) with a minimum width |
|
||||
| `M` | 0, 59 | Minute with a minimum width |
|
||||
| `S` | 0, 59 | Second with a minimum width |
|
||||
| `s` | 000, 500 | Millisecond with a minimum width of 3 |
|
||||
| `e` | Mon, Tue | Abbreviated days of the week |
|
||||
| `E` | Monday | Full day of week name |
|
||||
|
||||
The number of sequential code characters indicate the width of the code. A format of
|
||||
`yyyy-mm` specifies that the code `y` should have a width of four while `m` a width of two.
|
||||
Codes that yield numeric digits have an associated mode: fixed-width or minimum-width.
|
||||
The fixed-width mode left-pads the value with zeros when it is shorter than the specified
|
||||
width and truncates the value when longer. Minimum-width mode works the same as fixed-width
|
||||
except that it does not truncate values longer than the width.
|
||||
|
||||
When creating a `format` you can use any non-code characters as a separator. For example to
|
||||
generate the string "1996-01-15T00:00:00" you could use `format`: "yyyy-mm-ddTHH:MM:SS".
|
||||
Note that if you need to use a code character as a literal you can use the escape character
|
||||
backslash. The string "1996y01m" can be produced with the format "yyyy\\ymm\\m".
|
||||
"""
|
||||
function format(dt::TimeType, f::AbstractString; locale::Locale=ENGLISH)
|
||||
format(dt, DateFormat(f, locale))
|
||||
end
|
||||
|
||||
# show
|
||||
|
||||
function Base.show(io::IO, dt::DateTime)
|
||||
if millisecond(dt) == 0
|
||||
format(io, dt, dateformat"YYYY-mm-dd\THH:MM:SS")
|
||||
else
|
||||
format(io, dt, dateformat"YYYY-mm-dd\THH:MM:SS.s")
|
||||
end
|
||||
end
|
||||
|
||||
function Base.show(io::IO, dt::Date)
|
||||
format(io, dt, dateformat"YYYY-mm-dd")
|
||||
end
|
||||
|
||||
function Base.string(dt::DateTime)
|
||||
if millisecond(dt) == 0
|
||||
format(dt, dateformat"YYYY-mm-dd\THH:MM:SS", 24)
|
||||
else
|
||||
format(dt, dateformat"YYYY-mm-dd\THH:MM:SS.s", 26)
|
||||
end
|
||||
end
|
||||
|
||||
function Base.string(dt::Date)
|
||||
# don't use format - bypassing IOBuffer creation
|
||||
# saves a bit of time here.
|
||||
y,m,d = yearmonthday(value(dt))
|
||||
yy = y < 0 ? @sprintf("%05i", y) : lpad(y, 4, "0")
|
||||
mm = lpad(m, 2, "0")
|
||||
dd = lpad(d, 2, "0")
|
||||
return "$yy-$mm-$dd"
|
||||
end
|
||||
|
||||
# vectorized
|
||||
function DateTime(Y::AbstractArray{<:AbstractString}, f::AbstractString; locale::Locale=ENGLISH)
|
||||
DateTime(Y, DateFormat(f, locale))
|
||||
end
|
||||
function DateTime(Y::AbstractArray{<:AbstractString}, df::DateFormat=ISODateTimeFormat)
|
||||
return reshape(DateTime[parse(DateTime, y, df) for y in Y], size(Y))
|
||||
end
|
||||
function Date(Y::AbstractArray{<:AbstractString}, f::AbstractString; locale::Locale=ENGLISH)
|
||||
Date(Y, DateFormat(f, locale))
|
||||
end
|
||||
function Date(Y::AbstractArray{<:AbstractString}, df::DateFormat=ISODateFormat)
|
||||
return reshape(Date[Date(parse(Date, y, df)) for y in Y], size(Y))
|
||||
end
|
||||
|
||||
function format(Y::AbstractArray{<:TimeType}, f::AbstractString; locale::Locale=ENGLISH)
|
||||
format(Y, DateFormat(f, locale))
|
||||
end
|
||||
function format(Y::AbstractArray{T}, df::DateFormat=default_format(T)) where T<:TimeType
|
||||
return reshape([format(y, df) for y in Y], size(Y))
|
||||
end
|
||||
311
julia-0.6.3/share/julia/base/dates/parse.jl
Normal file
311
julia-0.6.3/share/julia/base/dates/parse.jl
Normal file
@@ -0,0 +1,311 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
### Parsing utilities
|
||||
|
||||
_directives(::Type{DateFormat{S,T}}) where {S,T} = T.parameters
|
||||
|
||||
character_codes(df::Type{DateFormat{S,T}}) where {S,T} = character_codes(_directives(df))
|
||||
function character_codes(directives::SimpleVector)
|
||||
letters = sizehint!(Char[], length(directives))
|
||||
for (i, directive) in enumerate(directives)
|
||||
if directive <: DatePart
|
||||
letter = first(directive.parameters)
|
||||
push!(letters, letter)
|
||||
end
|
||||
end
|
||||
return letters
|
||||
end
|
||||
|
||||
genvar(t::DataType) = Symbol(lowercase(string(Base.datatype_name(t))))
|
||||
|
||||
"""
|
||||
tryparsenext_core(str::AbstractString, pos::Int, len::Int, df::DateFormat, raise=false)
|
||||
|
||||
Parses the string according to the directives within the DateFormat. Parsing will start at
|
||||
character index `pos` and will stop when all directives are used or we have parsed up to
|
||||
the end of the string, `len`. When a directive cannot be parsed the returned value tuple
|
||||
will be null if `raise` is false otherwise an exception will be thrown.
|
||||
|
||||
Returns a 3-element tuple `(values, pos, num_parsed)`:
|
||||
* `values::Nullable{Tuple}`: A tuple which contains a value for each `DatePart` within the
|
||||
`DateFormat` in the order in which they occur. If the string ends before we finish parsing
|
||||
all the directives the missing values will be filled in with default values.
|
||||
* `pos::Int`: The character index at which parsing stopped.
|
||||
* `num_parsed::Int`: The number of values which were parsed and stored within `values`.
|
||||
Useful for distinguishing parsed values from default values.
|
||||
"""
|
||||
@generated function tryparsenext_core(str::AbstractString, pos::Int, len::Int,
|
||||
df::DateFormat, raise::Bool=false)
|
||||
directives = _directives(df)
|
||||
letters = character_codes(directives)
|
||||
|
||||
tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters]
|
||||
value_names = Symbol[genvar(t) for t in tokens]
|
||||
value_defaults = Tuple(CONVERSION_DEFAULTS[t] for t in tokens)
|
||||
R = typeof(value_defaults)
|
||||
|
||||
# Pre-assign variables to defaults. Allows us to use `@goto done` without worrying about
|
||||
# unassigned variables.
|
||||
assign_defaults = Expr[
|
||||
quote
|
||||
$name = $default
|
||||
end
|
||||
for (name, default) in zip(value_names, value_defaults)
|
||||
]
|
||||
|
||||
vi = 1
|
||||
parsers = Expr[
|
||||
begin
|
||||
if directives[i] <: DatePart
|
||||
name = value_names[vi]
|
||||
nullable = Symbol(:nullable_, name)
|
||||
vi += 1
|
||||
quote
|
||||
pos > len && @goto done
|
||||
$nullable, next_pos = tryparsenext(directives[$i], str, pos, len, locale)
|
||||
isnull($nullable) && @goto error
|
||||
$name = unsafe_get($nullable)
|
||||
pos = next_pos
|
||||
num_parsed += 1
|
||||
directive_index += 1
|
||||
end
|
||||
else
|
||||
quote
|
||||
pos > len && @goto done
|
||||
nullable_delim, next_pos = tryparsenext(directives[$i], str, pos, len, locale)
|
||||
isnull(nullable_delim) && @goto error
|
||||
pos = next_pos
|
||||
directive_index += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
for i in 1:length(directives)
|
||||
]
|
||||
|
||||
quote
|
||||
directives = df.tokens
|
||||
locale::DateLocale = df.locale
|
||||
|
||||
num_parsed = 0
|
||||
directive_index = 1
|
||||
|
||||
$(assign_defaults...)
|
||||
$(parsers...)
|
||||
|
||||
pos > len || @goto error
|
||||
|
||||
@label done
|
||||
return Nullable{$R}($(Expr(:tuple, value_names...))), pos, num_parsed
|
||||
|
||||
@label error
|
||||
if raise
|
||||
if directive_index > length(directives)
|
||||
throw(ArgumentError("Found extra characters at the end of date time string"))
|
||||
else
|
||||
d = directives[directive_index]
|
||||
throw(ArgumentError("Unable to parse date time. Expected directive $d at char $pos"))
|
||||
end
|
||||
end
|
||||
return Nullable{$R}(), pos, 0
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
tryparsenext_internal(::Type{<:TimeType}, str, pos, len, df::DateFormat, raise=false)
|
||||
|
||||
Parses the string according to the directives within the DateFormat. The specified TimeType
|
||||
type determines the type of and order of tokens returned. If the given DateFormat or string
|
||||
does not provide a required token a default value will be used. When the string cannot be
|
||||
parsed the returned value tuple will be null if `raise` is false otherwise an exception will
|
||||
be thrown.
|
||||
|
||||
Returns a 2-element tuple `(values, pos)`:
|
||||
* `values::Nullable{Tuple}`: A tuple which contains a value for each token as specified by
|
||||
the passed in type.
|
||||
* `pos::Int`: The character index at which parsing stopped.
|
||||
"""
|
||||
@generated function tryparsenext_internal(::Type{T}, str::AbstractString, pos::Int, len::Int,
|
||||
df::DateFormat, raise::Bool=false) where T<:TimeType
|
||||
letters = character_codes(df)
|
||||
|
||||
tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters]
|
||||
value_names = Symbol[genvar(t) for t in tokens]
|
||||
|
||||
output_tokens = CONVERSION_TRANSLATIONS[T]
|
||||
output_names = Symbol[genvar(t) for t in output_tokens]
|
||||
output_defaults = Tuple(CONVERSION_DEFAULTS[t] for t in output_tokens)
|
||||
R = typeof(output_defaults)
|
||||
|
||||
# Pre-assign output variables to defaults. Ensures that all output variables are
|
||||
# assigned as the value tuple returned from `tryparsenext_core` may not include all
|
||||
# of the required variables.
|
||||
assign_defaults = Expr[
|
||||
quote
|
||||
$name = $default
|
||||
end
|
||||
for (name, default) in zip(output_names, output_defaults)
|
||||
]
|
||||
|
||||
# Unpacks the value tuple returned by `tryparsenext_core` into separate variables.
|
||||
value_tuple = Expr(:tuple, value_names...)
|
||||
|
||||
quote
|
||||
values, pos, num_parsed = tryparsenext_core(str, pos, len, df, raise)
|
||||
isnull(values) && return Nullable{$R}(), pos
|
||||
$(assign_defaults...)
|
||||
$value_tuple = unsafe_get(values)
|
||||
return Nullable{$R}($(Expr(:tuple, output_names...))), pos
|
||||
end
|
||||
end
|
||||
|
||||
@inline function tryparsenext_base10(str::AbstractString, i::Int, len::Int, min_width::Int=1, max_width::Int=0)
|
||||
i > len && (return Nullable{Int64}(), i)
|
||||
min_pos = min_width <= 0 ? i : i + min_width - 1
|
||||
max_pos = max_width <= 0 ? len : min(i + max_width - 1, len)
|
||||
d::Int64 = 0
|
||||
@inbounds while i <= max_pos
|
||||
c, ii = next(str, i)
|
||||
if '0' <= c <= '9'
|
||||
d = d * 10 + (c - '0')
|
||||
else
|
||||
break
|
||||
end
|
||||
i = ii
|
||||
end
|
||||
if i <= min_pos
|
||||
return Nullable{Int64}(), i
|
||||
else
|
||||
return Nullable{Int64}(d), i
|
||||
end
|
||||
end
|
||||
|
||||
@inline function tryparsenext_word(str::AbstractString, i, len, locale, maxchars=0)
|
||||
word_start, word_end = i, 0
|
||||
max_pos = maxchars <= 0 ? len : min(chr2ind(str, ind2chr(str,i) + maxchars - 1), len)
|
||||
@inbounds while i <= max_pos
|
||||
c, ii = next(str, i)
|
||||
if isalpha(c)
|
||||
word_end = i
|
||||
else
|
||||
break
|
||||
end
|
||||
i = ii
|
||||
end
|
||||
if word_end == 0
|
||||
return Nullable{SubString}(), i
|
||||
else
|
||||
return Nullable{SubString}(SubString(str, word_start, word_end)), i
|
||||
end
|
||||
end
|
||||
|
||||
function Base.parse(::Type{DateTime}, s::AbstractString, df::typeof(ISODateTimeFormat))
|
||||
i, end_pos = start(s), endof(s)
|
||||
|
||||
dm = dd = Int64(1)
|
||||
th = tm = ts = tms = Int64(0)
|
||||
|
||||
nv, i = tryparsenext_base10(s, i, end_pos, 1)
|
||||
dy = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
i > end_pos && @goto error
|
||||
|
||||
c, i = next(s, i)
|
||||
c != '-' && @goto error
|
||||
i > end_pos && @goto done
|
||||
|
||||
nv, i = tryparsenext_base10(s, i, end_pos, 1, 2)
|
||||
dm = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
i > end_pos && @goto done
|
||||
|
||||
c, i = next(s, i)
|
||||
c != '-' && @goto error
|
||||
i > end_pos && @goto done
|
||||
|
||||
nv, i = tryparsenext_base10(s, i, end_pos, 1, 2)
|
||||
dd = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
i > end_pos && @goto done
|
||||
|
||||
c, i = next(s, i)
|
||||
c != 'T' && @goto error
|
||||
i > end_pos && @goto done
|
||||
|
||||
nv, i = tryparsenext_base10(s, i, end_pos, 1, 2)
|
||||
th = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
i > end_pos && @goto done
|
||||
|
||||
c, i = next(s, i)
|
||||
c != ':' && @goto error
|
||||
i > end_pos && @goto done
|
||||
|
||||
nv, i = tryparsenext_base10(s, i, end_pos, 1, 2)
|
||||
tm = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
i > end_pos && @goto done
|
||||
|
||||
c, i = next(s, i)
|
||||
c != ':' && @goto error
|
||||
i > end_pos && @goto done
|
||||
|
||||
nv, i = tryparsenext_base10(s, i, end_pos, 1, 2)
|
||||
ts = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
i > end_pos && @goto done
|
||||
|
||||
c, i = next(s, i)
|
||||
c != '.' && @goto error
|
||||
i > end_pos && @goto done
|
||||
|
||||
nv, j = tryparsenext_base10(s, i, end_pos, 1, 3)
|
||||
tms = isnull(nv) ? (@goto error) : unsafe_get(nv)
|
||||
tms *= 10 ^ (3 - (j - i))
|
||||
|
||||
j > end_pos || @goto error
|
||||
|
||||
@label done
|
||||
return DateTime(dy, dm, dd, th, tm, ts, tms)
|
||||
|
||||
@label error
|
||||
throw(ArgumentError("Invalid DateTime string"))
|
||||
end
|
||||
|
||||
function Base.parse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType
|
||||
pos, len = start(str), endof(str)
|
||||
values, pos = tryparsenext_internal(T, str, pos, len, df, true)
|
||||
T(unsafe_get(values)...)
|
||||
end
|
||||
|
||||
function Base.tryparse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType
|
||||
pos, len = start(str), endof(str)
|
||||
values, pos = tryparsenext_internal(T, str, pos, len, df, false)
|
||||
if isnull(values)
|
||||
Nullable{T}()
|
||||
elseif isnull(validargs(T, unsafe_get(values)...))
|
||||
# TODO: validargs gets called twice, since it's called again in the T constructor
|
||||
Nullable{T}(T(unsafe_get(values)...))
|
||||
else
|
||||
Nullable{T}()
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
parse_components(str::AbstractString, df::DateFormat) -> Array{Any}
|
||||
|
||||
Parse the string into its components according to the directives in the DateFormat.
|
||||
Each component will be a distinct type, typically a subtype of Period. The order of the
|
||||
components will match the order of the `DatePart` directives within the DateFormat. The
|
||||
number of components may be less than the total number of `DatePart`.
|
||||
"""
|
||||
@generated function parse_components(str::AbstractString, df::DateFormat)
|
||||
letters = character_codes(df)
|
||||
tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters]
|
||||
|
||||
quote
|
||||
pos, len = start(str), endof(str)
|
||||
values, pos, num_parsed = tryparsenext_core(str, pos, len, df, true)
|
||||
t = unsafe_get(values)
|
||||
types = $(Expr(:tuple, tokens...))
|
||||
result = Vector{Any}(num_parsed)
|
||||
for (i, typ) in enumerate(types)
|
||||
i > num_parsed && break
|
||||
result[i] = typ(t[i]) # Constructing types takes most of the time
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
472
julia-0.6.3/share/julia/base/dates/periods.jl
Normal file
472
julia-0.6.3/share/julia/base/dates/periods.jl
Normal file
@@ -0,0 +1,472 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
#Period types
|
||||
value(x::Period) = x.value
|
||||
|
||||
# The default constructors for Periods work well in almost all cases
|
||||
# P(x) = new((convert(Int64,x))
|
||||
# The following definitions are for Period-specific safety
|
||||
for period in (:Year, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond, :Microsecond, :Nanosecond)
|
||||
period_str = string(period)
|
||||
accessor_str = lowercase(period_str)
|
||||
# Convenience method for show()
|
||||
@eval _units(x::$period) = " " * $accessor_str * (abs(value(x)) == 1 ? "" : "s")
|
||||
# periodisless
|
||||
@eval periodisless(x::$period, y::$period) = value(x) < value(y)
|
||||
# AbstractString parsing (mainly for IO code)
|
||||
@eval $period(x::AbstractString) = $period(Base.parse(Int64, x))
|
||||
# Period accessors
|
||||
typs = period in (:Microsecond, :Nanosecond) ? ["Time"] :
|
||||
period in (:Hour, :Minute, :Second, :Millisecond) ? ["Time", "DateTime"] : ["Date", "DateTime"]
|
||||
reference = period == :Week ? " For details see [`$accessor_str(::Union{Date, DateTime})`](@ref)." : ""
|
||||
for typ_str in typs
|
||||
@eval begin
|
||||
@doc """
|
||||
$($period_str)(dt::$($typ_str)) -> $($period_str)
|
||||
|
||||
The $($accessor_str) part of a $($typ_str) as a `$($period_str)`.$($reference)
|
||||
""" $period(dt::$(Symbol(typ_str))) = $period($(Symbol(accessor_str))(dt))
|
||||
end
|
||||
end
|
||||
@eval begin
|
||||
@doc """
|
||||
$($period_str)(v)
|
||||
|
||||
Construct a `$($period_str)` object with the given `v` value. Input must be
|
||||
losslessly convertible to an [`Int64`](@ref).
|
||||
""" $period(v)
|
||||
end
|
||||
end
|
||||
|
||||
#Print/show/traits
|
||||
Base.string(x::Period) = string(value(x), _units(x))
|
||||
Base.show(io::IO,x::Period) = print(io, string(x))
|
||||
Base.zero(::Union{Type{P},P}) where {P<:Period} = P(0)
|
||||
Base.one(::Union{Type{P},P}) where {P<:Period} = 1 # see #16116
|
||||
Base.typemin(::Type{P}) where {P<:Period} = P(typemin(Int64))
|
||||
Base.typemax(::Type{P}) where {P<:Period} = P(typemax(Int64))
|
||||
|
||||
# Default values (as used by TimeTypes)
|
||||
"""
|
||||
default(p::Period) -> Period
|
||||
|
||||
Returns a sensible "default" value for the input Period by returning `T(1)` for Year,
|
||||
Month, and Day, and `T(0)` for Hour, Minute, Second, and Millisecond.
|
||||
"""
|
||||
function default end
|
||||
|
||||
default(p::Union{T,Type{T}}) where {T<:DatePeriod} = T(1)
|
||||
default(p::Union{T,Type{T}}) where {T<:TimePeriod} = T(0)
|
||||
|
||||
(-)(x::P) where {P<:Period} = P(-value(x))
|
||||
==(x::P, y::P) where {P<:Period} = value(x) == value(y)
|
||||
==(x::Period, y::Period) = (==)(promote(x, y)...)
|
||||
Base.isless(x::P, y::P) where {P<:Period} = isless(value(x), value(y))
|
||||
Base.isless(x::Period, y::Period) = isless(promote(x, y)...)
|
||||
|
||||
# Period Arithmetic, grouped by dimensionality:
|
||||
import Base: div, fld, mod, rem, gcd, lcm, +, -, *, /, %
|
||||
for op in (:+, :-, :lcm, :gcd)
|
||||
@eval ($op)(x::P, y::P) where {P<:Period} = P(($op)(value(x), value(y)))
|
||||
end
|
||||
|
||||
for op in (:/, :div, :fld)
|
||||
@eval begin
|
||||
($op)(x::P, y::P) where {P<:Period} = ($op)(value(x), value(y))
|
||||
($op)(x::P, y::Real) where {P<:Period} = P(($op)(value(x), Int64(y)))
|
||||
end
|
||||
end
|
||||
|
||||
for op in (:rem, :mod)
|
||||
@eval begin
|
||||
($op)(x::P, y::P) where {P<:Period} = P(($op)(value(x), value(y)))
|
||||
($op)(x::P, y::Real) where {P<:Period} = P(($op)(value(x), Int64(y)))
|
||||
end
|
||||
end
|
||||
|
||||
(*)(x::P, y::Real) where {P<:Period} = P(value(x) * Int64(y))
|
||||
(*)(y::Real, x::Period) = x * y
|
||||
for (op, Ty, Tz) in ((:*, Real, :P),
|
||||
(:/, :P, Float64), (:/, Real, :P))
|
||||
@eval begin
|
||||
function ($op){P<:Period}(X::StridedArray{P}, y::$Ty)
|
||||
Z = similar(X, $Tz)
|
||||
for (Idst, Isrc) in zip(eachindex(Z), eachindex(X))
|
||||
@inbounds Z[Idst] = ($op)(X[Isrc], y)
|
||||
end
|
||||
return Z
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# intfuncs
|
||||
Base.gcdx{T<:Period}(a::T, b::T) = ((g, x, y) = gcdx(value(a), value(b)); return T(g), x, y)
|
||||
Base.abs{T<:Period}(a::T) = T(abs(value(a)))
|
||||
|
||||
periodisless(::Period,::Year) = true
|
||||
periodisless(::Period,::Month) = true
|
||||
periodisless(::Year,::Month) = false
|
||||
periodisless(::Period,::Week) = true
|
||||
periodisless(::Year,::Week) = false
|
||||
periodisless(::Month,::Week) = false
|
||||
periodisless(::Period,::Day) = true
|
||||
periodisless(::Year,::Day) = false
|
||||
periodisless(::Month,::Day) = false
|
||||
periodisless(::Week,::Day) = false
|
||||
periodisless(::Period,::Hour) = false
|
||||
periodisless(::Minute,::Hour) = true
|
||||
periodisless(::Second,::Hour) = true
|
||||
periodisless(::Millisecond,::Hour) = true
|
||||
periodisless(::Microsecond,::Hour) = true
|
||||
periodisless(::Nanosecond,::Hour) = true
|
||||
periodisless(::Period,::Minute) = false
|
||||
periodisless(::Second,::Minute) = true
|
||||
periodisless(::Millisecond,::Minute) = true
|
||||
periodisless(::Microsecond,::Minute) = true
|
||||
periodisless(::Nanosecond,::Minute) = true
|
||||
periodisless(::Period,::Second) = false
|
||||
periodisless(::Millisecond,::Second) = true
|
||||
periodisless(::Microsecond,::Second) = true
|
||||
periodisless(::Nanosecond,::Second) = true
|
||||
periodisless(::Period,::Millisecond) = false
|
||||
periodisless(::Microsecond,::Millisecond) = true
|
||||
periodisless(::Nanosecond,::Millisecond) = true
|
||||
periodisless(::Period,::Microsecond) = false
|
||||
periodisless(::Nanosecond,::Microsecond) = true
|
||||
periodisless(::Period,::Nanosecond) = false
|
||||
|
||||
# return (next coarser period, conversion factor):
|
||||
coarserperiod{P<:Period}(::Type{P}) = (P, 1)
|
||||
coarserperiod(::Type{Nanosecond}) = (Microsecond, 1000)
|
||||
coarserperiod(::Type{Microsecond}) = (Millisecond, 1000)
|
||||
coarserperiod(::Type{Millisecond}) = (Second, 1000)
|
||||
coarserperiod(::Type{Second}) = (Minute, 60)
|
||||
coarserperiod(::Type{Minute}) = (Hour, 60)
|
||||
coarserperiod(::Type{Hour}) = (Day, 24)
|
||||
coarserperiod(::Type{Day}) = (Week, 7)
|
||||
coarserperiod(::Type{Month}) = (Year, 12)
|
||||
|
||||
# Stores multiple periods in greatest to least order by type, not values,
|
||||
# canonicalized to eliminate zero periods, merge equal period types,
|
||||
# and convert more-precise periods to less-precise periods when possible
|
||||
"""
|
||||
CompoundPeriod
|
||||
|
||||
A `CompoundPeriod` is useful for expressing time periods that are not a fixed multiple of
|
||||
smaller periods. For example, \"a year and a day\" is not a fixed number of days, but can
|
||||
be expressed using a `CompoundPeriod`. In fact, a `CompoundPeriod` is automatically
|
||||
generated by addition of different period types, e.g. `Year(1) + Day(1)` produces a
|
||||
`CompoundPeriod` result.
|
||||
"""
|
||||
mutable struct CompoundPeriod <: AbstractTime
|
||||
periods::Array{Period, 1}
|
||||
function CompoundPeriod(p::Vector{Period})
|
||||
n = length(p)
|
||||
if n > 1
|
||||
sort!(p, rev=true, lt=periodisless)
|
||||
# canonicalize p by merging equal period types and removing zeros
|
||||
i = j = 1
|
||||
while j <= n
|
||||
k = j + 1
|
||||
while k <= n
|
||||
if typeof(p[j]) == typeof(p[k])
|
||||
p[j] += p[k]
|
||||
k += 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if p[j] != zero(p[j])
|
||||
p[i] = p[j]
|
||||
i += 1
|
||||
end
|
||||
j = k
|
||||
end
|
||||
n = i - 1 # new length
|
||||
p = resize!(p, n)
|
||||
elseif n == 1 && value(p[1]) == 0
|
||||
p = Period[]
|
||||
end
|
||||
|
||||
return new(p)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
CompoundPeriod(periods) -> CompoundPeriod
|
||||
|
||||
Construct a `CompoundPeriod` from a `Vector` of `Period`s. All `Period`s of the same type
|
||||
will be added together.
|
||||
|
||||
# Examples
|
||||
```jldoctest
|
||||
julia> Dates.CompoundPeriod(Dates.Hour(12), Dates.Hour(13))
|
||||
25 hours
|
||||
|
||||
julia> Dates.CompoundPeriod(Dates.Hour(-1), Dates.Minute(1))
|
||||
-1 hour, 1 minute
|
||||
|
||||
julia> Dates.CompoundPeriod(Dates.Month(1), Dates.Week(-2))
|
||||
1 month, -2 weeks
|
||||
|
||||
julia> Dates.CompoundPeriod(Dates.Minute(50000))
|
||||
50000 minutes
|
||||
```
|
||||
"""
|
||||
CompoundPeriod(p::Vector{<:Period}) = CompoundPeriod(Vector{Period}(p))
|
||||
|
||||
CompoundPeriod(t::Time) = CompoundPeriod(Period[Hour(t), Minute(t), Second(t), Millisecond(t),
|
||||
Microsecond(t), Nanosecond(t)])
|
||||
|
||||
CompoundPeriod(p::Period...) = CompoundPeriod(Period[p...])
|
||||
|
||||
|
||||
"""
|
||||
canonicalize(::CompoundPeriod) -> CompoundPeriod
|
||||
|
||||
Reduces the `CompoundPeriod` into its canonical form by applying the following rules:
|
||||
|
||||
* Any `Period` large enough be partially representable by a coarser `Period` will be broken
|
||||
into multiple `Period`s (eg. `Hour(30)` becomes `Day(1) + Hour(6)`)
|
||||
* `Period`s with opposite signs will be combined when possible
|
||||
(eg. `Hour(1) - Day(1)` becomes `-Hour(23)`)
|
||||
|
||||
# Examples
|
||||
```jldoctest
|
||||
julia> Dates.canonicalize(Dates.CompoundPeriod(Dates.Hour(12), Dates.Hour(13)))
|
||||
1 day, 1 hour
|
||||
|
||||
julia> Dates.canonicalize(Dates.CompoundPeriod(Dates.Hour(-1), Dates.Minute(1)))
|
||||
-59 minutes
|
||||
|
||||
julia> Dates.canonicalize(Dates.CompoundPeriod(Dates.Month(1), Dates.Week(-2)))
|
||||
1 month, -2 weeks
|
||||
|
||||
julia> Dates.canonicalize(Dates.CompoundPeriod(Dates.Minute(50000)))
|
||||
4 weeks, 6 days, 17 hours, 20 minutes
|
||||
```
|
||||
"""
|
||||
function canonicalize(x::CompoundPeriod)
|
||||
# canonicalize Periods by pushing "overflow" into a coarser period.
|
||||
p = x.periods
|
||||
n = length(p)
|
||||
if n > 0
|
||||
pc = sizehint!(Period[], n)
|
||||
P = typeof(p[n])
|
||||
v = value(p[n])
|
||||
i = n - 1
|
||||
while true
|
||||
Pc, f = coarserperiod(P)
|
||||
if i > 0 && typeof(p[i]) == P
|
||||
v += value(p[i])
|
||||
i -= 1
|
||||
end
|
||||
v0 = f == 1 ? v : rem(v, f)
|
||||
v0 != 0 && push!(pc, P(v0))
|
||||
if v != v0
|
||||
P = Pc
|
||||
v = div(v - v0, f)
|
||||
elseif i > 0
|
||||
P = typeof(p[i])
|
||||
v = value(p[i])
|
||||
i -= 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
p = reverse!(pc)
|
||||
n = length(p)
|
||||
else
|
||||
return x
|
||||
end
|
||||
|
||||
# reduce the amount of mixed positive/negative Periods.
|
||||
if n > 0
|
||||
pc = sizehint!(Period[], n)
|
||||
i = n
|
||||
while i > 0
|
||||
j = i
|
||||
|
||||
# Determine sign of the largest period in this group which
|
||||
# can be converted into via coarserperiod.
|
||||
last = Union{}
|
||||
current = typeof(p[i])
|
||||
while i > 0 && current != last
|
||||
if typeof(p[i]) == current
|
||||
i -= 1
|
||||
end
|
||||
last, current = current, coarserperiod(current)[1]
|
||||
end
|
||||
s = sign(value(p[i + 1]))
|
||||
|
||||
# Adjust all the periods in the group based upon the
|
||||
# largest period sign.
|
||||
P = typeof(p[j])
|
||||
v = 0
|
||||
while j > i
|
||||
Pc, f = coarserperiod(P)
|
||||
if j > 0 && typeof(p[j]) == P
|
||||
v += value(p[j])
|
||||
j -= 1
|
||||
end
|
||||
v0 = f == 1 ? v : mod(v, f * s)
|
||||
v0 != 0 && push!(pc, P(v0))
|
||||
if v != v0
|
||||
P = Pc
|
||||
v = div(v - v0, f)
|
||||
elseif j > 0
|
||||
P = typeof(p[j])
|
||||
v = 0
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
p = reverse!(pc)
|
||||
end
|
||||
|
||||
return CompoundPeriod(p)
|
||||
end
|
||||
|
||||
Base.convert(::Type{CompoundPeriod}, x::Period) = CompoundPeriod(Period[x])
|
||||
function Base.string(x::CompoundPeriod)
|
||||
if isempty(x.periods)
|
||||
return "empty period"
|
||||
else
|
||||
s = ""
|
||||
for p in x.periods
|
||||
s *= ", " * string(p)
|
||||
end
|
||||
return s[3:end]
|
||||
end
|
||||
end
|
||||
Base.show(io::IO,x::CompoundPeriod) = print(io, string(x))
|
||||
|
||||
# E.g. Year(1) + Day(1)
|
||||
(+)(x::Period,y::Period) = CompoundPeriod(Period[x, y])
|
||||
(+)(x::CompoundPeriod, y::Period) = CompoundPeriod(vcat(x.periods, y))
|
||||
(+)(y::Period, x::CompoundPeriod) = x + y
|
||||
(+)(x::CompoundPeriod, y::CompoundPeriod) = CompoundPeriod(vcat(x.periods, y.periods))
|
||||
# E.g. Year(1) - Month(1)
|
||||
(-)(x::Period, y::Period) = CompoundPeriod(Period[x, -y])
|
||||
(-)(x::CompoundPeriod, y::Period) = CompoundPeriod(vcat(x.periods, -y))
|
||||
(-)(x::CompoundPeriod) = CompoundPeriod(-x.periods)
|
||||
(-)(y::Union{Period, CompoundPeriod}, x::CompoundPeriod) = (-x) + y
|
||||
|
||||
GeneralPeriod = Union{Period, CompoundPeriod}
|
||||
(+)(x::GeneralPeriod) = x
|
||||
(+)(x::StridedArray{<:GeneralPeriod}) = x
|
||||
|
||||
for op in (:+, :-)
|
||||
@eval begin
|
||||
($op)(x::GeneralPeriod, Y::StridedArray{<:GeneralPeriod}) = broadcast($op, x, Y)
|
||||
($op)(Y::StridedArray{<:GeneralPeriod}, x::GeneralPeriod) = broadcast($op, Y, x)
|
||||
($op)(X::StridedArray{<:GeneralPeriod}, Y::StridedArray{<:GeneralPeriod}) =
|
||||
reshape(CompoundPeriod[($op)(x, y) for (x, y) in zip(X, Y)], promote_shape(size(X), size(Y)))
|
||||
end
|
||||
end
|
||||
|
||||
(==)(x::CompoundPeriod, y::Period) = x == CompoundPeriod(y)
|
||||
(==)(x::Period, y::CompoundPeriod) = y == x
|
||||
(==)(x::CompoundPeriod, y::CompoundPeriod) = canonicalize(x).periods == canonicalize(y).periods
|
||||
|
||||
Base.isequal(x::CompoundPeriod, y::Period) = isequal(x, CompoundPeriod(y))
|
||||
Base.isequal(x::Period, y::CompoundPeriod) = isequal(y, x)
|
||||
Base.isequal(x::CompoundPeriod, y::CompoundPeriod) = x.periods == y.periods
|
||||
|
||||
# Capture TimeType+-Period methods
|
||||
(+)(a::TimeType, b::Period, c::Period) = (+)(a, b + c)
|
||||
(+)(a::TimeType, b::Period, c::Period, d::Period...) = (+)((+)(a, b + c), d...)
|
||||
|
||||
function (+)(x::TimeType, y::CompoundPeriod)
|
||||
for p in y.periods
|
||||
x += p
|
||||
end
|
||||
return x
|
||||
end
|
||||
(+)(x::CompoundPeriod, y::TimeType) = y + x
|
||||
|
||||
function (-)(x::TimeType, y::CompoundPeriod)
|
||||
for p in y.periods
|
||||
x -= p
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
# Fixed-value Periods (periods corresponding to a well-defined time interval,
|
||||
# as opposed to variable calendar intervals like Year).
|
||||
const FixedPeriod = Union{Week, Day, Hour, Minute, Second, Millisecond, Microsecond, Nanosecond}
|
||||
|
||||
# like div but throw an error if remainder is nonzero
|
||||
function divexact(x, y)
|
||||
q, r = divrem(x, y)
|
||||
r == 0 || throw(InexactError())
|
||||
return q
|
||||
end
|
||||
|
||||
# FixedPeriod conversions and promotion rules
|
||||
const fixedperiod_conversions = [(Week, 7), (Day, 24), (Hour, 60), (Minute, 60), (Second, 1000), (Millisecond, 1000), (Microsecond, 1000), (Nanosecond, 1)]
|
||||
for i = 1:length(fixedperiod_conversions)
|
||||
T, n = fixedperiod_conversions[i]
|
||||
N = Int64(1)
|
||||
for j = (i - 1):-1:1 # less-precise periods
|
||||
Tc, nc = fixedperiod_conversions[j]
|
||||
N *= nc
|
||||
vmax = typemax(Int64) ÷ N
|
||||
vmin = typemin(Int64) ÷ N
|
||||
@eval function Base.convert(::Type{$T}, x::$Tc)
|
||||
$vmin ≤ value(x) ≤ $vmax || throw(InexactError())
|
||||
return $T(value(x) * $N)
|
||||
end
|
||||
end
|
||||
N = n
|
||||
for j = (i + 1):length(fixedperiod_conversions) # more-precise periods
|
||||
Tc, nc = fixedperiod_conversions[j]
|
||||
@eval Base.convert(::Type{$T}, x::$Tc) = $T(divexact(value(x), $N))
|
||||
@eval Base.promote_rule(::Type{$T}, ::Type{$Tc}) = $Tc
|
||||
N *= nc
|
||||
end
|
||||
end
|
||||
|
||||
# other periods with fixed conversions but which aren't fixed time periods
|
||||
const OtherPeriod = Union{Month, Year}
|
||||
let vmax = typemax(Int64) ÷ 12, vmin = typemin(Int64) ÷ 12
|
||||
@eval function Base.convert(::Type{Month}, x::Year)
|
||||
$vmin ≤ value(x) ≤ $vmax || throw(InexactError())
|
||||
Month(value(x) * 12)
|
||||
end
|
||||
end
|
||||
Base.convert(::Type{Year}, x::Month) = Year(divexact(value(x), 12))
|
||||
Base.promote_rule(::Type{Year}, ::Type{Month}) = Month
|
||||
|
||||
# disallow comparing fixed to other periods
|
||||
(==)(x::FixedPeriod, y::OtherPeriod) = throw(MethodError(==, (x, y)))
|
||||
(==)(x::OtherPeriod, y::FixedPeriod) = throw(MethodError(==, (x, y)))
|
||||
|
||||
Base.isless(x::FixedPeriod, y::OtherPeriod) = throw(MethodError(isless, (x, y)))
|
||||
Base.isless(x::OtherPeriod, y::FixedPeriod) = throw(MethodError(isless, (x, y)))
|
||||
|
||||
# truncating conversions to milliseconds and days:
|
||||
toms(c::Nanosecond) = div(value(c), 1000000)
|
||||
toms(c::Microsecond) = div(value(c), 1000)
|
||||
toms(c::Millisecond) = value(c)
|
||||
toms(c::Second) = 1000 * value(c)
|
||||
toms(c::Minute) = 60000 * value(c)
|
||||
toms(c::Hour) = 3600000 * value(c)
|
||||
toms(c::Day) = 86400000 * value(c)
|
||||
toms(c::Week) = 604800000 * value(c)
|
||||
toms(c::Month) = 86400000.0 * 30.436875 * value(c)
|
||||
toms(c::Year) = 86400000.0 * 365.2425 * value(c)
|
||||
toms(c::CompoundPeriod) = isempty(c.periods) ? 0.0 : Float64(sum(toms, c.periods))
|
||||
tons(x) = toms(x) * 1000000
|
||||
tons(x::Microsecond) = value(x) * 1000
|
||||
tons(x::Nanosecond) = value(x)
|
||||
days(c::Millisecond) = div(value(c), 86400000)
|
||||
days(c::Second) = div(value(c), 86400)
|
||||
days(c::Minute) = div(value(c), 1440)
|
||||
days(c::Hour) = div(value(c), 24)
|
||||
days(c::Day) = value(c)
|
||||
days(c::Week) = 7 * value(c)
|
||||
days(c::Year) = 365.2425 * value(c)
|
||||
days(c::Month) = 30.436875 * value(c)
|
||||
days(c::CompoundPeriod) = isempty(c.periods) ? 0.0 : Float64(sum(days, c.periods))
|
||||
249
julia-0.6.3/share/julia/base/dates/query.jl
Normal file
249
julia-0.6.3/share/julia/base/dates/query.jl
Normal file
@@ -0,0 +1,249 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Date Locales
|
||||
|
||||
struct DateLocale
|
||||
months::Vector{String}
|
||||
months_abbr::Vector{String}
|
||||
days_of_week::Vector{String}
|
||||
days_of_week_abbr::Vector{String}
|
||||
month_value::Dict{String, Int}
|
||||
month_abbr_value::Dict{String, Int}
|
||||
day_of_week_value::Dict{String, Int}
|
||||
day_of_week_abbr_value::Dict{String, Int}
|
||||
end
|
||||
|
||||
function locale_dict(names::Vector{<:AbstractString})
|
||||
result = Dict{String, Int}()
|
||||
|
||||
# Keep both the common case-sensitive version of the name and an all lowercase
|
||||
# version for case-insensitive matches. Storing both allows us to avoid using the
|
||||
# lowercase function during parsing.
|
||||
for i in 1:length(names)
|
||||
name = names[i]
|
||||
result[name] = i
|
||||
result[lowercase(name)] = i
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
DateLocale(["January", "February",...], ["Jan", "Feb",...],
|
||||
["Monday", "Tuesday",...], ["Mon", "Tue",...])
|
||||
|
||||
Create a locale for parsing or printing textual month names.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `months::Vector`: 12 month names
|
||||
- `months_abbr::Vector`: 12 abbreviated month names
|
||||
- `days_of_week::Vector`: 7 days of week
|
||||
- `days_of_week_abbr::Vector`: 7 days of week abbreviated
|
||||
|
||||
This object is passed as the last argument to `tryparsenext` and `format`
|
||||
methods defined for each `AbstractDateToken` type.
|
||||
"""
|
||||
function DateLocale(months::Vector, months_abbr::Vector,
|
||||
days_of_week::Vector, days_of_week_abbr::Vector)
|
||||
DateLocale(
|
||||
months, months_abbr, days_of_week, days_of_week_abbr,
|
||||
locale_dict(months), locale_dict(months_abbr),
|
||||
locale_dict(days_of_week), locale_dict(days_of_week_abbr),
|
||||
)
|
||||
end
|
||||
|
||||
const ENGLISH = DateLocale(
|
||||
["January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December"],
|
||||
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
|
||||
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
)
|
||||
|
||||
const LOCALES = Dict{String, DateLocale}("english" => ENGLISH)
|
||||
|
||||
for (fn, field) in zip(
|
||||
[:dayname_to_value, :dayabbr_to_value, :monthname_to_value, :monthabbr_to_value],
|
||||
[:day_of_week_value, :day_of_week_abbr_value, :month_value, :month_abbr_value],
|
||||
)
|
||||
@eval @inline function $fn(word::AbstractString, locale::DateLocale)
|
||||
# Maximize performance by attempting to avoid the use of `lowercase` and trying
|
||||
# a case-sensitive lookup first
|
||||
value = get(locale.$field, word, 0)
|
||||
if value == 0
|
||||
value = get(locale.$field, lowercase(word), 0)
|
||||
end
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
# Date functions
|
||||
|
||||
### Core query functions
|
||||
|
||||
# Monday = 1....Sunday = 7
|
||||
dayofweek(days) = mod1(days, 7)
|
||||
|
||||
# Number of days in year
|
||||
"""
|
||||
daysinyear(dt::TimeType) -> Int
|
||||
|
||||
Returns 366 if the year of `dt` is a leap year, otherwise returns 365.
|
||||
"""
|
||||
daysinyear(y) = 365 + isleapyear(y)
|
||||
|
||||
# Day of the year
|
||||
const MONTHDAYS = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
|
||||
dayofyear(y, m, d) = MONTHDAYS[m] + d + (m > 2 && isleapyear(y))
|
||||
|
||||
### Days of the Week
|
||||
"""
|
||||
dayofweek(dt::TimeType) -> Int64
|
||||
|
||||
Returns the day of the week as an [`Int64`](@ref) with `1 = Monday, 2 = Tuesday, etc.`.
|
||||
"""
|
||||
dayofweek(dt::TimeType) = dayofweek(days(dt))
|
||||
|
||||
const Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = 1, 2, 3, 4, 5, 6, 7
|
||||
const Mon, Tue, Wed, Thu, Fri, Sat, Sun = 1, 2, 3, 4, 5, 6, 7
|
||||
|
||||
dayname(day::Integer, locale::DateLocale) = locale.days_of_week[day]
|
||||
dayabbr(day::Integer, locale::DateLocale) = locale.days_of_week_abbr[day]
|
||||
dayname(day::Integer; locale::AbstractString="english") = dayname(day, LOCALES[locale])
|
||||
dayabbr(day::Integer; locale::AbstractString="english") = dayabbr(day, LOCALES[locale])
|
||||
|
||||
"""
|
||||
dayname(dt::TimeType; locale="english") -> AbstractString
|
||||
|
||||
Return the full day name corresponding to the day of the week of the `Date` or `DateTime` in
|
||||
the given `locale`.
|
||||
"""
|
||||
function dayname(dt::TimeType;locale::AbstractString="english")
|
||||
dayname(dayofweek(dt); locale=locale)
|
||||
end
|
||||
|
||||
"""
|
||||
dayabbr(dt::TimeType; locale="english") -> AbstractString
|
||||
|
||||
Return the abbreviated name corresponding to the day of the week of the `Date` or `DateTime`
|
||||
in the given `locale`.
|
||||
"""
|
||||
function dayabbr(dt::TimeType;locale::AbstractString="english")
|
||||
dayabbr(dayofweek(dt); locale=locale)
|
||||
end
|
||||
|
||||
# Convenience methods for each day
|
||||
ismonday(dt::TimeType) = dayofweek(dt) == Mon
|
||||
istuesday(dt::TimeType) = dayofweek(dt) == Tue
|
||||
iswednesday(dt::TimeType) = dayofweek(dt) == Wed
|
||||
isthursday(dt::TimeType) = dayofweek(dt) == Thu
|
||||
isfriday(dt::TimeType) = dayofweek(dt) == Fri
|
||||
issaturday(dt::TimeType) = dayofweek(dt) == Sat
|
||||
issunday(dt::TimeType) = dayofweek(dt) == Sun
|
||||
|
||||
# i.e. 1st Monday? 2nd Monday? 3rd Wednesday? 5th Sunday?
|
||||
"""
|
||||
dayofweekofmonth(dt::TimeType) -> Int
|
||||
|
||||
For the day of week of `dt`, returns which number it is in `dt`'s month. So if the day of
|
||||
the week of `dt` is Monday, then `1 = First Monday of the month, 2 = Second Monday of the
|
||||
month, etc.` In the range 1:5.
|
||||
"""
|
||||
function dayofweekofmonth(dt::TimeType)
|
||||
d = day(dt)
|
||||
return d < 8 ? 1 : d < 15 ? 2 : d < 22 ? 3 : d < 29 ? 4 : 5
|
||||
end
|
||||
|
||||
# Total number of a day of week in the month
|
||||
# e.g. are there 4 or 5 Mondays in this month?
|
||||
const TWENTYNINE = IntSet([1, 8, 15, 22, 29])
|
||||
const THIRTY = IntSet([1, 2, 8, 9, 15, 16, 22, 23, 29, 30])
|
||||
const THIRTYONE = IntSet([1, 2, 3, 8, 9, 10, 15, 16, 17, 22, 23, 24, 29, 30, 31])
|
||||
|
||||
"""
|
||||
daysofweekinmonth(dt::TimeType) -> Int
|
||||
|
||||
For the day of week of `dt`, returns the total number of that day of the week in `dt`'s
|
||||
month. Returns 4 or 5. Useful in temporal expressions for specifying the last day of a week
|
||||
in a month by including `dayofweekofmonth(dt) == daysofweekinmonth(dt)` in the adjuster
|
||||
function.
|
||||
"""
|
||||
function daysofweekinmonth(dt::TimeType)
|
||||
y, m, d = yearmonthday(dt)
|
||||
ld = daysinmonth(y, m)
|
||||
return ld == 28 ? 4 : ld == 29 ? ((d in TWENTYNINE) ? 5 : 4) :
|
||||
ld == 30 ? ((d in THIRTY) ? 5 : 4) :
|
||||
(d in THIRTYONE) ? 5 : 4
|
||||
end
|
||||
|
||||
### Months
|
||||
const January, February, March, April, May, June = 1, 2, 3, 4, 5, 6
|
||||
const July, August, September, October, November, December = 7, 8, 9, 10, 11, 12
|
||||
const Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||
|
||||
monthname(month::Integer, locale::DateLocale) = locale.months[month]
|
||||
monthabbr(month::Integer, locale::DateLocale) = locale.months_abbr[month]
|
||||
monthname(month::Integer; locale::AbstractString="english") = monthname(month, LOCALES[locale])
|
||||
monthabbr(month::Integer; locale::AbstractString="english") = monthabbr(month, LOCALES[locale])
|
||||
|
||||
"""
|
||||
monthname(dt::TimeType; locale="english") -> AbstractString
|
||||
|
||||
Return the full name of the month of the `Date` or `DateTime` in the given `locale`.
|
||||
"""
|
||||
function monthname(dt::TimeType; locale::AbstractString="english")
|
||||
monthname(month(dt); locale=locale)
|
||||
end
|
||||
|
||||
"""
|
||||
monthabbr(dt::TimeType; locale="english") -> AbstractString
|
||||
|
||||
Return the abbreviated month name of the `Date` or `DateTime` in the given `locale`.
|
||||
"""
|
||||
function monthabbr(dt::TimeType; locale::AbstractString="english")
|
||||
monthabbr(month(dt); locale=locale)
|
||||
end
|
||||
|
||||
"""
|
||||
daysinmonth(dt::TimeType) -> Int
|
||||
|
||||
Returns the number of days in the month of `dt`. Value will be 28, 29, 30, or 31.
|
||||
"""
|
||||
daysinmonth(dt::TimeType) = ((y, m) = yearmonth(dt); return daysinmonth(y, m))
|
||||
|
||||
### Years
|
||||
"""
|
||||
isleapyear(dt::TimeType) -> Bool
|
||||
|
||||
Returns `true` if the year of `dt` is a leap year.
|
||||
"""
|
||||
isleapyear(dt::TimeType) = isleapyear(year(dt))
|
||||
|
||||
"""
|
||||
dayofyear(dt::TimeType) -> Int
|
||||
|
||||
Returns the day of the year for `dt` with January 1st being day 1.
|
||||
"""
|
||||
dayofyear(dt::TimeType) = ((y, m, d) = yearmonthday(dt); return dayofyear(y, m, d))
|
||||
|
||||
daysinyear(dt::TimeType) = 365 + isleapyear(dt)
|
||||
|
||||
### Quarters
|
||||
"""
|
||||
quarterofyear(dt::TimeType) -> Int
|
||||
|
||||
Returns the quarter that `dt` resides in. Range of value is 1:4.
|
||||
"""
|
||||
function quarterofyear(dt::TimeType)
|
||||
m = month(dt)
|
||||
return m < 4 ? 1 : m < 7 ? 2 : m < 10 ? 3 : 4
|
||||
end
|
||||
const QUARTERDAYS = (0, 90, 181, 273)
|
||||
|
||||
"""
|
||||
dayofquarter(dt::TimeType) -> Int
|
||||
|
||||
Returns the day of the current quarter of `dt`. Range of value is 1:92.
|
||||
"""
|
||||
dayofquarter(dt::TimeType) = dayofyear(dt) - QUARTERDAYS[quarterofyear(dt)]
|
||||
48
julia-0.6.3/share/julia/base/dates/ranges.jl
Normal file
48
julia-0.6.3/share/julia/base/dates/ranges.jl
Normal file
@@ -0,0 +1,48 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Date/DateTime Ranges
|
||||
|
||||
# Override default step; otherwise it would be Millisecond(1)
|
||||
Base.colon(start::T, stop::T) where {T<:DateTime} = StepRange(start, Day(1), stop)
|
||||
Base.colon(start::T, stop::T) where {T<:Date} = StepRange(start, Day(1), stop)
|
||||
Base.colon(start::T, stop::T) where {T<:Time} = StepRange(start, Second(1), stop)
|
||||
|
||||
Base.range(start::DateTime, len::Integer) = range(start, Day(1), len)
|
||||
Base.range(start::Date, len::Integer) = range(start, Day(1), len)
|
||||
|
||||
(::Type{StepRange{<:Dates.DatePeriod,<:Real}})(start, step, stop) =
|
||||
throw(ArgumentError("must specify step as a Period when constructing Dates ranges"))
|
||||
|
||||
# Given a start and end date, how many steps/periods are in between
|
||||
guess(a::DateTime, b::DateTime, c) = floor(Int64, (Int128(value(b)) - Int128(value(a))) / toms(c))
|
||||
guess(a::Date, b::Date, c) = Int64(div(value(b - a), days(c)))
|
||||
len(a::Time, b::Time, c) = Int64(div(value(b - a), tons(c)))
|
||||
function len(a, b, c)
|
||||
lo, hi, st = min(a, b), max(a, b), abs(c)
|
||||
i = guess(a, b, c) - 1
|
||||
while lo + st * i <= hi
|
||||
i += 1
|
||||
end
|
||||
return i - 1
|
||||
end
|
||||
Base.length(r::StepRange{<:TimeType}) = isempty(r) ? Int64(0) : len(r.start, r.stop, r.step) + 1
|
||||
# Period ranges hook into Int64 overflow detection
|
||||
Base.length(r::StepRange{<:Period}) = length(StepRange(value(r.start), value(r.step), value(r.stop)))
|
||||
|
||||
# Used to calculate the last valid date in the range given the start, stop, and step
|
||||
# last = stop - steprem(start, stop, step)
|
||||
Base.steprem(a::T, b::T, c) where {T<:TimeType} = b - (a + c * len(a, b, c))
|
||||
|
||||
import Base.in
|
||||
function in(x::T, r::StepRange{T}) where T<:TimeType
|
||||
n = len(first(r), x, step(r)) + 1
|
||||
n >= 1 && n <= length(r) && r[n] == x
|
||||
end
|
||||
|
||||
Base.start(r::StepRange{<:TimeType}) = 0
|
||||
Base.next(r::StepRange{<:TimeType}, i::Int) = (r.start + r.step*i, i + 1)
|
||||
Base.done(r::StepRange{<:TimeType,<:Period}, i::Integer) = length(r) <= i
|
||||
|
||||
+(x::Period, r::Range{<:TimeType}) = (x + first(r)):step(r):(x + last(r))
|
||||
+(r::Range{<:TimeType}, x::Period) = x + r
|
||||
-(r::Range{<:TimeType}, x::Period) = (first(r)-x):step(r):(last(r)-x)
|
||||
180
julia-0.6.3/share/julia/base/dates/rounding.jl
Normal file
180
julia-0.6.3/share/julia/base/dates/rounding.jl
Normal file
@@ -0,0 +1,180 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# The epochs used for date rounding are based ISO 8601's "year zero" notation
|
||||
const DATEEPOCH = value(Date(0))
|
||||
const DATETIMEEPOCH = value(DateTime(0))
|
||||
|
||||
# According to ISO 8601, the first day of the first week of year 0000 is 0000-01-03
|
||||
const WEEKEPOCH = value(Date(0, 1, 3))
|
||||
|
||||
"""
|
||||
epochdays2date(days) -> Date
|
||||
|
||||
Takes the number of days since the rounding epoch (`0000-01-01T00:00:00`) and returns the
|
||||
corresponding `Date`.
|
||||
"""
|
||||
epochdays2date(i) = Date(UTD(DATEEPOCH + Int64(i)))
|
||||
|
||||
"""
|
||||
epochms2datetime(milliseconds) -> DateTime
|
||||
|
||||
Takes the number of milliseconds since the rounding epoch (`0000-01-01T00:00:00`) and
|
||||
returns the corresponding `DateTime`.
|
||||
"""
|
||||
epochms2datetime(i) = DateTime(UTM(DATETIMEEPOCH + Int64(i)))
|
||||
|
||||
"""
|
||||
date2epochdays(dt::Date) -> Int64
|
||||
|
||||
Takes the given `Date` and returns the number of days since the rounding epoch
|
||||
(`0000-01-01T00:00:00`) as an [`Int64`](@ref).
|
||||
"""
|
||||
date2epochdays(dt::Date) = value(dt) - DATEEPOCH
|
||||
|
||||
"""
|
||||
datetime2epochms(dt::DateTime) -> Int64
|
||||
|
||||
Takes the given `DateTime` and returns the number of milliseconds since the rounding epoch
|
||||
(`0000-01-01T00:00:00`) as an [`Int64`](@ref).
|
||||
"""
|
||||
datetime2epochms(dt::DateTime) = value(dt) - DATETIMEEPOCH
|
||||
|
||||
function Base.floor(dt::Date, p::Year)
|
||||
value(p) < 1 && throw(DomainError())
|
||||
years = year(dt)
|
||||
return Date(years - mod(years, value(p)))
|
||||
end
|
||||
|
||||
function Base.floor(dt::Date, p::Month)
|
||||
value(p) < 1 && throw(DomainError())
|
||||
y, m = yearmonth(dt)
|
||||
months_since_epoch = y * 12 + m - 1
|
||||
month_offset = months_since_epoch - mod(months_since_epoch, value(p))
|
||||
target_month = mod(month_offset, 12) + 1
|
||||
target_year = div(month_offset, 12) - (month_offset < 0 && target_month != 1)
|
||||
return Date(target_year, target_month)
|
||||
end
|
||||
|
||||
function Base.floor(dt::Date, p::Week)
|
||||
value(p) < 1 && throw(DomainError())
|
||||
days = value(dt) - WEEKEPOCH
|
||||
days = days - mod(days, value(Day(p)))
|
||||
return Date(UTD(WEEKEPOCH + Int64(days)))
|
||||
end
|
||||
|
||||
function Base.floor(dt::Date, p::Day)
|
||||
value(p) < 1 && throw(DomainError())
|
||||
days = date2epochdays(dt)
|
||||
return epochdays2date(days - mod(days, value(p)))
|
||||
end
|
||||
|
||||
Base.floor(dt::DateTime, p::DatePeriod) = DateTime(Base.floor(Date(dt), p))
|
||||
|
||||
function Base.floor(dt::DateTime, p::TimePeriod)
|
||||
value(p) < 1 && throw(DomainError())
|
||||
milliseconds = datetime2epochms(dt)
|
||||
return epochms2datetime(milliseconds - mod(milliseconds, value(Millisecond(p))))
|
||||
end
|
||||
|
||||
"""
|
||||
floor(dt::TimeType, p::Period) -> TimeType
|
||||
|
||||
Returns the nearest `Date` or `DateTime` less than or equal to `dt` at resolution `p`.
|
||||
|
||||
For convenience, `p` may be a type instead of a value: `floor(dt, Dates.Hour)` is a shortcut
|
||||
for `floor(dt, Dates.Hour(1))`.
|
||||
|
||||
```jldoctest
|
||||
julia> floor(Date(1985, 8, 16), Dates.Month)
|
||||
1985-08-01
|
||||
|
||||
julia> floor(DateTime(2013, 2, 13, 0, 31, 20), Dates.Minute(15))
|
||||
2013-02-13T00:30:00
|
||||
|
||||
julia> floor(DateTime(2016, 8, 6, 12, 0, 0), Dates.Day)
|
||||
2016-08-06T00:00:00
|
||||
```
|
||||
"""
|
||||
Base.floor(::Dates.TimeType, ::Dates.Period)
|
||||
|
||||
"""
|
||||
ceil(dt::TimeType, p::Period) -> TimeType
|
||||
|
||||
Returns the nearest `Date` or `DateTime` greater than or equal to `dt` at resolution `p`.
|
||||
|
||||
For convenience, `p` may be a type instead of a value: `ceil(dt, Dates.Hour)` is a shortcut
|
||||
for `ceil(dt, Dates.Hour(1))`.
|
||||
|
||||
```jldoctest
|
||||
julia> ceil(Date(1985, 8, 16), Dates.Month)
|
||||
1985-09-01
|
||||
|
||||
julia> ceil(DateTime(2013, 2, 13, 0, 31, 20), Dates.Minute(15))
|
||||
2013-02-13T00:45:00
|
||||
|
||||
julia> ceil(DateTime(2016, 8, 6, 12, 0, 0), Dates.Day)
|
||||
2016-08-07T00:00:00
|
||||
```
|
||||
"""
|
||||
function Base.ceil(dt::TimeType, p::Period)
|
||||
f = floor(dt, p)
|
||||
return (dt == f) ? f : f + p
|
||||
end
|
||||
|
||||
"""
|
||||
floorceil(dt::TimeType, p::Period) -> (TimeType, TimeType)
|
||||
|
||||
Simultaneously return the `floor` and `ceil` of a `Date` or `DateTime` at resolution `p`.
|
||||
More efficient than calling both `floor` and `ceil` individually.
|
||||
"""
|
||||
function floorceil(dt::TimeType, p::Period)
|
||||
f = floor(dt, p)
|
||||
return f, (dt == f) ? f : f + p
|
||||
end
|
||||
|
||||
"""
|
||||
round(dt::TimeType, p::Period, [r::RoundingMode]) -> TimeType
|
||||
|
||||
Returns the `Date` or `DateTime` nearest to `dt` at resolution `p`. By default
|
||||
(`RoundNearestTiesUp`), ties (e.g., rounding 9:30 to the nearest hour) will be rounded up.
|
||||
|
||||
For convenience, `p` may be a type instead of a value: `round(dt, Dates.Hour)` is a shortcut
|
||||
for `round(dt, Dates.Hour(1))`.
|
||||
|
||||
```jldoctest
|
||||
julia> round(Date(1985, 8, 16), Dates.Month)
|
||||
1985-08-01
|
||||
|
||||
julia> round(DateTime(2013, 2, 13, 0, 31, 20), Dates.Minute(15))
|
||||
2013-02-13T00:30:00
|
||||
|
||||
julia> round(DateTime(2016, 8, 6, 12, 0, 0), Dates.Day)
|
||||
2016-08-07T00:00:00
|
||||
```
|
||||
|
||||
Valid rounding modes for `round(::TimeType, ::Period, ::RoundingMode)` are
|
||||
`RoundNearestTiesUp` (default), `RoundDown` (`floor`), and `RoundUp` (`ceil`).
|
||||
"""
|
||||
function Base.round(dt::TimeType, p::Period, r::RoundingMode{:NearestTiesUp})
|
||||
f, c = floorceil(dt, p)
|
||||
return (dt - f) < (c - dt) ? f : c
|
||||
end
|
||||
|
||||
Base.round(dt::TimeType, p::Period, r::RoundingMode{:Down}) = Base.floor(dt, p)
|
||||
Base.round(dt::TimeType, p::Period, r::RoundingMode{:Up}) = Base.ceil(dt, p)
|
||||
|
||||
# No implementation of other `RoundingMode`s: rounding to nearest "even" is skipped because
|
||||
# "even" is not defined for Period; rounding toward/away from zero is skipped because ISO
|
||||
# 8601's year 0000 is not really "zero".
|
||||
Base.round(::TimeType, ::Period, ::RoundingMode) = throw(DomainError())
|
||||
|
||||
# Default to RoundNearestTiesUp.
|
||||
Base.round(dt::TimeType, p::Period) = Base.round(dt, p, RoundNearestTiesUp)
|
||||
|
||||
# Make rounding functions callable using Period types in addition to values.
|
||||
Base.floor(dt::TimeType, p::Type{<:Period}) = Base.floor(dt, p(1))
|
||||
Base.ceil(dt::TimeType, p::Type{<:Period}) = Base.ceil(dt, p(1))
|
||||
|
||||
function Base.round(dt::TimeType, p::Type{<:Period}, r::RoundingMode=RoundNearestTiesUp)
|
||||
return Base.round(dt, p(1), r)
|
||||
end
|
||||
358
julia-0.6.3/share/julia/base/dates/types.jl
Normal file
358
julia-0.6.3/share/julia/base/dates/types.jl
Normal file
@@ -0,0 +1,358 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
abstract type AbstractTime end
|
||||
|
||||
"""
|
||||
Period
|
||||
Year
|
||||
Month
|
||||
Week
|
||||
Day
|
||||
Hour
|
||||
Minute
|
||||
Second
|
||||
Millisecond
|
||||
Microsecond
|
||||
Nanosecond
|
||||
|
||||
`Period` types represent discrete, human representations of time.
|
||||
"""
|
||||
abstract type Period <: AbstractTime end
|
||||
abstract type DatePeriod <: Period end
|
||||
abstract type TimePeriod <: Period end
|
||||
|
||||
for T in (:Year, :Month, :Week, :Day)
|
||||
@eval struct $T <: DatePeriod
|
||||
value::Int64
|
||||
$T(v::Number) = new(v)
|
||||
end
|
||||
end
|
||||
for T in (:Hour, :Minute, :Second, :Millisecond, :Microsecond, :Nanosecond)
|
||||
@eval struct $T <: TimePeriod
|
||||
value::Int64
|
||||
$T(v::Number) = new(v)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Year(v)
|
||||
Month(v)
|
||||
Week(v)
|
||||
Day(v)
|
||||
Hour(v)
|
||||
Minute(v)
|
||||
Second(v)
|
||||
Millisecond(v)
|
||||
Microsecond(v)
|
||||
Nanosecond(v)
|
||||
|
||||
Construct a `Period` type with the given `v` value. Input must be losslessly convertible
|
||||
to an [`Int64`](@ref).
|
||||
"""
|
||||
Period(v)
|
||||
|
||||
"""
|
||||
Instant
|
||||
|
||||
`Instant` types represent integer-based, machine representations of time as continuous
|
||||
timelines starting from an epoch.
|
||||
"""
|
||||
abstract type Instant <: AbstractTime end
|
||||
|
||||
"""
|
||||
UTInstant{T}
|
||||
|
||||
The `UTInstant` represents a machine timeline based on UT time (1 day = one revolution of
|
||||
the earth). The `T` is a `Period` parameter that indicates the resolution or precision of
|
||||
the instant.
|
||||
"""
|
||||
struct UTInstant{P<:Period} <: Instant
|
||||
periods::P
|
||||
end
|
||||
|
||||
# Convenience default constructors
|
||||
UTM(x) = UTInstant(Millisecond(x))
|
||||
UTD(x) = UTInstant(Day(x))
|
||||
|
||||
# Calendar types provide rules for interpretating instant
|
||||
# timelines in human-readable form.
|
||||
abstract type Calendar <: AbstractTime end
|
||||
|
||||
# ISOCalendar implements the ISO 8601 standard (en.wikipedia.org/wiki/ISO_8601)
|
||||
# Notably based on the proleptic Gregorian calendar
|
||||
# ISOCalendar provides interpretation rules for UTInstants to civil date and time parts
|
||||
struct ISOCalendar <: Calendar end
|
||||
|
||||
abstract type TimeZone end
|
||||
struct UTC <: TimeZone end
|
||||
|
||||
"""
|
||||
TimeType
|
||||
|
||||
`TimeType` types wrap `Instant` machine instances to provide human representations of the
|
||||
machine instant. `Time`, `DateTime` and `Date` are subtypes of `TimeType`.
|
||||
"""
|
||||
abstract type TimeType <: AbstractTime end
|
||||
|
||||
"""
|
||||
DateTime
|
||||
|
||||
`DateTime` wraps a `UTInstant{Millisecond}` and interprets it according to the proleptic
|
||||
Gregorian calendar.
|
||||
"""
|
||||
struct DateTime <: TimeType
|
||||
instant::UTInstant{Millisecond}
|
||||
DateTime(instant::UTInstant{Millisecond}) = new(instant)
|
||||
end
|
||||
|
||||
"""
|
||||
Date
|
||||
|
||||
`Date` wraps a `UTInstant{Day}` and interprets it according to the proleptic Gregorian calendar.
|
||||
"""
|
||||
struct Date <: TimeType
|
||||
instant::UTInstant{Day}
|
||||
Date(instant::UTInstant{Day}) = new(instant)
|
||||
end
|
||||
|
||||
"""
|
||||
Time
|
||||
|
||||
`Time` wraps a `Nanosecond` and represents a specific moment in a 24-hour day.
|
||||
"""
|
||||
struct Time <: TimeType
|
||||
instant::Nanosecond
|
||||
Time(instant::Nanosecond) = new(instant)
|
||||
end
|
||||
|
||||
# Convert y,m,d to # of Rata Die days
|
||||
# Works by shifting the beginning of the year to March 1,
|
||||
# so a leap day is the very last day of the year
|
||||
const SHIFTEDMONTHDAYS = (306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275)
|
||||
function totaldays(y, m, d)
|
||||
# If we're in Jan/Feb, shift the given year back one
|
||||
z = m < 3 ? y - 1 : y
|
||||
mdays = SHIFTEDMONTHDAYS[m]
|
||||
# days + month_days + year_days
|
||||
return d + mdays + 365z + fld(z, 4) - fld(z, 100) + fld(z, 400) - 306
|
||||
end
|
||||
|
||||
# If the year is divisible by 4, except for every 100 years, except for every 400 years
|
||||
isleapyear(y) = ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)
|
||||
|
||||
# Number of days in month
|
||||
const DAYSINMONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
daysinmonth(y,m) = DAYSINMONTH[m] + (m == 2 && isleapyear(y))
|
||||
|
||||
### UTILITIES ###
|
||||
|
||||
# These are necessary because the type constructors for TimeType subtypes can
|
||||
# throw, and we want to be able to use tryparse without requiring a try/catch.
|
||||
# This is made easier by providing a helper function that checks arguments, so
|
||||
# we can validate arguments in tryparse.
|
||||
|
||||
"""
|
||||
validargs(::Type{<:TimeType}, args...) -> Nullable{ArgumentError}
|
||||
|
||||
Determine whether the given arguments consitute valid inputs for the given type.
|
||||
Returns a `Nullable{ArgumentError}` where null signifies success.
|
||||
"""
|
||||
function validargs end
|
||||
|
||||
"""
|
||||
argerror([msg]) -> Nullable{ArgumentError}
|
||||
|
||||
Construct a `Nullable{ArgumentError}` with the given message, or null if no message
|
||||
is provided. For use by `validargs`.
|
||||
"""
|
||||
argerror(msg::String) = Nullable(ArgumentError(msg))
|
||||
argerror() = Nullable{ArgumentError}()
|
||||
|
||||
### CONSTRUCTORS ###
|
||||
# Core constructors
|
||||
"""
|
||||
DateTime(y, [m, d, h, mi, s, ms]) -> DateTime
|
||||
|
||||
Construct a `DateTime` type by parts. Arguments must be convertible to [`Int64`](@ref).
|
||||
"""
|
||||
function DateTime(y::Int64, m::Int64=1, d::Int64=1,
|
||||
h::Int64=0, mi::Int64=0, s::Int64=0, ms::Int64=0)
|
||||
err = validargs(DateTime, y, m, d, h, mi, s, ms)
|
||||
isnull(err) || throw(unsafe_get(err))
|
||||
rata = ms + 1000 * (s + 60mi + 3600h + 86400 * totaldays(y, m, d))
|
||||
return DateTime(UTM(rata))
|
||||
end
|
||||
|
||||
function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64,
|
||||
h::Int64, mi::Int64, s::Int64, ms::Int64)
|
||||
0 < m < 13 || return argerror("Month: $m out of range (1:12)")
|
||||
0 < d < daysinmonth(y, m) + 1 || return argerror("Day: $d out of range (1:$(daysinmonth(y, m)))")
|
||||
-1 < h < 24 || return argerror("Hour: $h out of range (0:23)")
|
||||
-1 < mi < 60 || return argerror("Minute: $mi out of range (0:59)")
|
||||
-1 < s < 60 || return argerror("Second: $s out of range (0:59)")
|
||||
-1 < ms < 1000 || return argerror("Millisecond: $ms out of range (0:999)")
|
||||
return argerror()
|
||||
end
|
||||
|
||||
"""
|
||||
Date(y, [m, d]) -> Date
|
||||
|
||||
Construct a `Date` type by parts. Arguments must be convertible to [`Int64`](@ref).
|
||||
"""
|
||||
function Date(y::Int64, m::Int64=1, d::Int64=1)
|
||||
err = validargs(Date, y, m, d)
|
||||
isnull(err) || throw(unsafe_get(err))
|
||||
return Date(UTD(totaldays(y, m, d)))
|
||||
end
|
||||
|
||||
function validargs(::Type{Date}, y::Int64, m::Int64, d::Int64)
|
||||
0 < m < 13 || return argerror("Month: $m out of range (1:12)")
|
||||
0 < d < daysinmonth(y, m) + 1 || return argerror("Day: $d out of range (1:$(daysinmonth(y, m)))")
|
||||
return argerror()
|
||||
end
|
||||
|
||||
"""
|
||||
Time(h, [mi, s, ms, us, ns]) -> Time
|
||||
|
||||
Construct a `Time` type by parts. Arguments must be convertible to [`Int64`](@ref).
|
||||
"""
|
||||
function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0)
|
||||
err = validargs(Time, h, mi, s, ms, us, ns)
|
||||
isnull(err) || throw(unsafe_get(err))
|
||||
return Time(Nanosecond(ns + 1000us + 1000000ms + 1000000000s + 60000000000mi + 3600000000000h))
|
||||
end
|
||||
|
||||
function validargs(::Type{Time}, h::Int64, mi::Int64, s::Int64, ms::Int64, us::Int64, ns::Int64)
|
||||
-1 < h < 24 || return argerror("Hour: $h out of range (0:23)")
|
||||
-1 < mi < 60 || return argerror("Minute: $mi out of range (0:59)")
|
||||
-1 < s < 60 || return argerror("Second: $s out of range (0:59)")
|
||||
-1 < ms < 1000 || return argerror("Millisecond: $ms out of range (0:999)")
|
||||
-1 < us < 1000 || return argerror("Microsecond: $us out of range (0:999)")
|
||||
-1 < ns < 1000 || return argerror("Nanosecond: $ns out of range (0:999)")
|
||||
return argerror()
|
||||
end
|
||||
|
||||
# Convenience constructors from Periods
|
||||
function DateTime(y::Year, m::Month=Month(1), d::Day=Day(1),
|
||||
h::Hour=Hour(0), mi::Minute=Minute(0),
|
||||
s::Second=Second(0), ms::Millisecond=Millisecond(0))
|
||||
return DateTime(value(y), value(m), value(d),
|
||||
value(h), value(mi), value(s), value(ms))
|
||||
end
|
||||
|
||||
Date(y::Year, m::Month=Month(1), d::Day=Day(1)) = Date(value(y), value(m), value(d))
|
||||
|
||||
function Time(h::Hour, mi::Minute=Minute(0), s::Second=Second(0),
|
||||
ms::Millisecond=Millisecond(0),
|
||||
us::Microsecond=Microsecond(0), ns::Nanosecond=Nanosecond(0))
|
||||
return Time(value(h), value(mi), value(s), value(ms), value(us), value(ns))
|
||||
end
|
||||
|
||||
# To allow any order/combination of Periods
|
||||
|
||||
"""
|
||||
DateTime(periods::Period...) -> DateTime
|
||||
|
||||
Construct a `DateTime` type by `Period` type parts. Arguments may be in any order. DateTime
|
||||
parts not provided will default to the value of `Dates.default(period)`.
|
||||
"""
|
||||
function DateTime(periods::Period...)
|
||||
y = Year(1); m = Month(1); d = Day(1)
|
||||
h = Hour(0); mi = Minute(0); s = Second(0); ms = Millisecond(0)
|
||||
for p in periods
|
||||
isa(p, Year) && (y = p::Year)
|
||||
isa(p, Month) && (m = p::Month)
|
||||
isa(p, Day) && (d = p::Day)
|
||||
isa(p, Hour) && (h = p::Hour)
|
||||
isa(p, Minute) && (mi = p::Minute)
|
||||
isa(p, Second) && (s = p::Second)
|
||||
isa(p, Millisecond) && (ms = p::Millisecond)
|
||||
end
|
||||
return DateTime(y, m, d, h, mi, s, ms)
|
||||
end
|
||||
|
||||
"""
|
||||
Date(period::Period...) -> Date
|
||||
|
||||
Construct a `Date` type by `Period` type parts. Arguments may be in any order. `Date` parts
|
||||
not provided will default to the value of `Dates.default(period)`.
|
||||
"""
|
||||
function Date(periods::Period...)
|
||||
y = Year(1); m = Month(1); d = Day(1)
|
||||
for p in periods
|
||||
isa(p, Year) && (y = p::Year)
|
||||
isa(p, Month) && (m = p::Month)
|
||||
isa(p, Day) && (d = p::Day)
|
||||
end
|
||||
return Date(y, m, d)
|
||||
end
|
||||
|
||||
"""
|
||||
Time(period::TimePeriod...) -> Time
|
||||
|
||||
Construct a `Time` type by `Period` type parts. Arguments may be in any order. `Time` parts
|
||||
not provided will default to the value of `Dates.default(period)`.
|
||||
"""
|
||||
function Time(periods::TimePeriod...)
|
||||
h = Hour(0); mi = Minute(0); s = Second(0)
|
||||
ms = Millisecond(0); us = Microsecond(0); ns = Nanosecond(0)
|
||||
for p in periods
|
||||
isa(p, Hour) && (h = p::Hour)
|
||||
isa(p, Minute) && (mi = p::Minute)
|
||||
isa(p, Second) && (s = p::Second)
|
||||
isa(p, Millisecond) && (ms = p::Millisecond)
|
||||
isa(p, Microsecond) && (us = p::Microsecond)
|
||||
isa(p, Nanosecond) && (ns = p::Nanosecond)
|
||||
end
|
||||
return Time(h, mi, s, ms, us, ns)
|
||||
end
|
||||
|
||||
# Fallback constructors
|
||||
DateTime(y, m=1, d=1, h=0, mi=0, s=0, ms=0) = DateTime(Int64(y), Int64(m), Int64(d), Int64(h), Int64(mi), Int64(s), Int64(ms))
|
||||
Date(y, m=1, d=1) = Date(Int64(y), Int64(m), Int64(d))
|
||||
Time(h, mi=0, s=0, ms=0, us=0, ns=0) = Time(Int64(h), Int64(mi), Int64(s), Int64(ms), Int64(us), Int64(ns))
|
||||
|
||||
# Traits, Equality
|
||||
Base.isfinite(::Union{Type{T}, T}) where {T<:TimeType} = true
|
||||
calendar(dt::DateTime) = ISOCalendar
|
||||
calendar(dt::Date) = ISOCalendar
|
||||
|
||||
"""
|
||||
eps(::DateTime) -> Millisecond
|
||||
eps(::Date) -> Day
|
||||
eps(::Time) -> Nanosecond
|
||||
|
||||
Returns `Millisecond(1)` for `DateTime` values, `Day(1)` for `Date` values, and `Nanosecond(1)` for `Time` values.
|
||||
"""
|
||||
Base.eps
|
||||
|
||||
Base.eps(dt::DateTime) = Millisecond(1)
|
||||
Base.eps(dt::Date) = Day(1)
|
||||
Base.eps(t::Time) = Nanosecond(1)
|
||||
|
||||
Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(146138512, 12, 31, 23, 59, 59)
|
||||
Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-146138511, 1, 1, 0, 0, 0)
|
||||
Base.typemax(::Union{Date, Type{Date}}) = Date(252522163911149, 12, 31)
|
||||
Base.typemin(::Union{Date, Type{Date}}) = Date(-252522163911150, 1, 1)
|
||||
Base.typemax(::Union{Time, Type{Time}}) = Time(23, 59, 59, 999, 999, 999)
|
||||
Base.typemin(::Union{Time, Type{Time}}) = Time(0)
|
||||
# Date-DateTime promotion, isless, ==
|
||||
Base.eltype(::Type{T}) where {T<:Period} = T
|
||||
Base.promote_rule(::Type{Date}, x::Type{DateTime}) = DateTime
|
||||
Base.isless(x::T, y::T) where {T<:TimeType} = isless(value(x), value(y))
|
||||
Base.isless(x::TimeType, y::TimeType) = isless(Base.promote_noncircular(x, y)...)
|
||||
(==)(x::T, y::T) where {T<:TimeType} = (==)(value(x), value(y))
|
||||
function ==(a::Time, b::Time)
|
||||
return hour(a) == hour(b) && minute(a) == minute(b) &&
|
||||
second(a) == second(b) && millisecond(a) == millisecond(b) &&
|
||||
microsecond(a) == microsecond(b) && nanosecond(a) == nanosecond(b)
|
||||
end
|
||||
(==)(x::TimeType, y::TimeType) = (===)(promote(x, y)...)
|
||||
|
||||
import Base: sleep, Timer, timedwait
|
||||
sleep(time::Period) = sleep(toms(time) / 1000)
|
||||
Timer(time::Period, repeat::Period=Second(0)) = Timer(toms(time) / 1000, toms(repeat) / 1000)
|
||||
timedwait(testcb::Function, time::Period) = timedwait(testcb, toms(time) / 1000)
|
||||
|
||||
Base.TypeOrder(::Type{<:AbstractTime}) = Base.HasOrder()
|
||||
Base.TypeArithmetic(::Type{<:AbstractTime}) = Base.ArithmeticOverflows()
|
||||
Reference in New Issue
Block a user