Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Overhaul printing of types #194

Merged
merged 2 commits into from
Mar 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/discovery.jl
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ function show_next_transition(io::IO, zdt::ZonedDateTime)

println(io, "Transition Date: ", Dates.format(instant, dateformat"yyyy-mm-dd"))
println(io, "Local Time Change: ", time_format(instant), " → ", time_format(to), " (", direction, ")")
println(io, "Offset Change: ", repr(from.zone.offset), " → ", repr(to.zone.offset))
println(io, "Offset Change: ", repr("text/plain", from.zone.offset), " → ", repr("text/plain", to.zone.offset))
println(io, "Transition From: ", zdt_format(from))
println(io, "Transition To: ", zdt_format(to))

Expand Down
106 changes: 88 additions & 18 deletions src/io.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
using Dates: value

Base.print(io::IO, tz::TimeZone) = print(io, tz.name)
function Base.print(io::IO, tz::FixedTimeZone)
isempty(tz.name) ? print(io, "UTC", tz.offset) : print(io, tz.name)
end
Base.print(io::IO, zdt::ZonedDateTime) = print(io, localtime(zdt), zdt.zone.offset)

function Base.show(io::IO, t::Transition)
print(io, t.utc_datetime, " ")
show(io, t.zone.offset)
!isempty(t.zone.name) && print(io, " (", t.zone.name, ")")
end

function Base.show(io::IO, tz::FixedTimeZone)
if get(io, :compact, false)
print(io, tz)
if get(io, :compact, true)
isempty(tz.name) ? print(io, "UTC", tz.offset) : print(io, tz.name)
else
offset_str = "UTC" * offset_string(tz.offset, true) # Use ISO 8601 for comparision
if isempty(tz.name)
Expand All @@ -27,9 +15,9 @@ function Base.show(io::IO, tz::FixedTimeZone)
end
end

function Base.show(io::IO, tz::VariableTimeZone)
if get(io, :compact, false)
print(io, tz)
function Base.print(io::IO, tz::VariableTimeZone)
if get(io, :compact, true)
print(io, tz.name)
else
trans = tz.transitions

Expand All @@ -56,4 +44,86 @@ function Base.show(io::IO, tz::VariableTimeZone)
end
end

Base.show(io::IO,dt::ZonedDateTime) = print(io, string(dt))
function Base.print(io::IO, t::Transition)
print(io, t.utc_datetime, " ")
show(io, MIME("text/plain"), t.zone.offset) # Long-form
!isempty(t.zone.name) && print(io, " (", t.zone.name, ")")
end

Base.print(io::IO, zdt::ZonedDateTime) = print(io, localtime(zdt), zdt.zone.offset)


function Base.show(io::IO, tz::FixedTimeZone)
if istimezone(tz.name, Class(:ALL)) && isequal(tz, TimeZone(tz.name, Class(:ALL)))
print(io, "tz\"$(tz.name)\"")
else
std = Dates.value(tz.offset.std)
dst = Dates.value(tz.offset.dst)

params = [repr(tz.name), repr(std)]
dst != 0 && push!(params, repr(dst))
print(io, FixedTimeZone, "(", join(params, ", "), ")")
end
end

function Base.show(io::IO, tz::VariableTimeZone)
# Compat printing when the time zone can be constructed with `@tz_str`
if istimezone(tz.name, Class(:ALL)) && isequal(tz, TimeZone(tz.name, Class(:ALL)))
print(io, "tz\"$(tz.name)\"")

# Compact printing of a custom time zone which is non-constructable
elseif get(io, :compact, false)
print(io, VariableTimeZone, "(")
show(io, tz.name)
print(io, ", ...)")

# Verbose printing which should print a fully constructable `VariableTimeZone`.
else
# Force `:compact => false` to make the force the transition vector printing into
# long form.
print(io, VariableTimeZone, "(")
show(io, tz.name)
print(io, ", ")
show(IOContext(io, :compact => false), tz.transitions)
print(io, ", ")
show(io, tz.cutoff)
print(io, ")")
end
end

function Base.show(io::IO, t::Transition)
# Note: Using combo of `:typeinfo` and `:limit` as a way of detecting when a vector of
# transitions is being printed in the REPL.
if get(io, :compact, false) || get(io, :typeinfo, Union{}) == Transition && get(io, :limit, false)
print(io, t)
else
# Fallback to calling the default show instead of reimplementing it.
invoke(show, Tuple{IO, Any}, io, t)
end
end

function Base.show(io::IO, zdt::ZonedDateTime)
if get(io, :compact, false)
print(io, zdt)
else
values = [
yearmonthday(zdt)...
hour(zdt)
minute(zdt)
second(zdt)
millisecond(zdt)
]
index = something(findlast(!iszero, values), 1)
params = [
map(repr, values[1:index]);
repr(timezone(zdt); context=:compact => true)
]

print(io, ZonedDateTime, "(", join(params, ", "), ")")
end
end


Base.show(io::IO, ::MIME"text/plain", t::Transition) = print(io, t)
Base.show(io::IO, ::MIME"text/plain", tz::TimeZone) = print(IOContext(io, :compact => false), tz)
Base.show(io::IO, ::MIME"text/plain", zdt::ZonedDateTime) = print(io, zdt)
16 changes: 13 additions & 3 deletions src/utcoffset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,18 @@ function offset_string(offset::UTCOffset, iso8601::Bool=false)
end

Base.print(io::IO, o::UTCOffset) = print(io, offset_string(o, true))

function Base.show(io::IO, o::UTCOffset)
# Show DST as a separate offset since we want to distinguish between normal hourly
# daylight saving time offsets and exotic DST offsets (e.g. midsummer time).
print(io, "UTC", offset_string(o.std), "/", offset_string(o.dst))
if get(io, :compact, false)
# Show DST as a separate offset since we want to distinguish between normal hourly
# daylight saving time offsets and exotic DST offsets (e.g. midsummer time).
print(io, "UTC", offset_string(o.std), "/", offset_string(o.dst))
else
# Fallback to calling the default show instead of reimplementing it.
invoke(show, Tuple{IO, Any}, io, o)
omus marked this conversation as resolved.
Show resolved Hide resolved
end
end

function Base.show(io::IO, ::MIME"text/plain", o::UTCOffset)
show(IOContext(io, :compact => true), o)
end
11 changes: 11 additions & 0 deletions test/helpers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,14 @@ function ignore_output(body::Function; stdout::Bool=true, stderr::Bool=true)

return result
end

# Used in tests as a shorter form of: `sprint(show, ..., context=:compact => true)`
show_compact = (io, args...) -> show(IOContext(io, :compact => true), args...)

# Takes the tuple from `compile` and adds the result into TimeZones cache. Typically should
# not be used and only should be required if the test tzdata version and built tzdata
# version do not match.
function cache_tz((tz, class)::Tuple{TimeZone, TimeZones.Class})
TimeZones.TIME_ZONE_CACHE[TimeZones.name(tz)] = (tz, class)
return tz
end
133 changes: 96 additions & 37 deletions test/io.jl
Original file line number Diff line number Diff line change
@@ -1,52 +1,76 @@
using TimeZones.TZData: parse_components
using TimeZones: Transition

dt = DateTime(1942,12,25,1,23,45)
custom_dt = DateTime(1800,1,1)

utc = FixedTimeZone("UTC")
gmt = FixedTimeZone("GMT", 0)
foo = FixedTimeZone("FOO", 0)
null = FixedTimeZone("", 10800)
fixed = FixedTimeZone("UTC+01:00")
est = FixedTimeZone("EST", -18000)
warsaw = first(compile("Europe/Warsaw", tzdata["europe"]))
apia = first(compile("Pacific/Apia", tzdata["australasia"]))
honolulu = first(compile("Pacific/Honolulu", tzdata["northamerica"])) # Uses cutoff
apia = cache_tz(compile("Pacific/Apia", tzdata["australasia"]))
honolulu = cache_tz(compile("Pacific/Honolulu", tzdata["northamerica"])) # Uses cutoff
ulyanovsk = first(compile("Europe/Ulyanovsk", tzdata["europe"])) # No named abbreviations
new_york = first(compile("America/New_York", tzdata["northamerica"])) # Underscore in name
dt = DateTime(1942,12,25,1,23,45)

# TimeZones as a string
@test string(null) == "UTC+03:00"
@test string(fixed) == "UTC+01:00"
@test string(est) == "EST"
@test string(warsaw) == "Europe/Warsaw"
@test string(apia) == "Pacific/Apia"
@test string(honolulu) == "Pacific/Honolulu"
@test string(ulyanovsk) == "Europe/Ulyanovsk"

# Alternatively in Julia 0.7.0-DEV.4517 we could use
# `sprint(show, ..., context=:compact => true)`
show_compact = (io, args...) -> show(IOContext(io, :compact => true), args...)
@test sprint(show_compact, null) == "UTC+03:00"
@test sprint(show_compact, fixed) == "UTC+01:00"
@test sprint(show_compact, est) == "EST"
@test sprint(show_compact, warsaw) == "Europe/Warsaw"
@test sprint(show_compact, apia) == "Pacific/Apia"
@test sprint(show_compact, honolulu) == "Pacific/Honolulu"
@test sprint(show_compact, ulyanovsk) == "Europe/Ulyanovsk"

@test sprint(show, null) == "UTC+03:00"
@test sprint(show, fixed) == "UTC+01:00"
@test sprint(show, est) == "EST (UTC-5)"
@test sprint(show, warsaw) == "Europe/Warsaw (UTC+1/UTC+2)"
@test sprint(show, apia) == "Pacific/Apia (UTC+13/UTC+14)"
@test sprint(show, honolulu) == "Pacific/Honolulu (UTC-10)"
@test sprint(show, ulyanovsk) == "Europe/Ulyanovsk (UTC+4)"

# UTC and GMT are special cases
@test sprint(show, FixedTimeZone("UTC")) == "UTC"
@test sprint(show, FixedTimeZone("GMT", 0)) == "GMT"
@test sprint(show, FixedTimeZone("FOO", 0)) == "FOO (UTC+0)"
custom = VariableTimeZone("Test/Custom", [Transition(custom_dt, utc)]) # Non-cached variable time zone

@test sprint(print, utc) == "UTC"
@test sprint(print, gmt) == "GMT"
@test sprint(print, foo) == "FOO"
@test sprint(print, null) == "UTC+03:00"
@test sprint(print, fixed) == "UTC+01:00"
@test sprint(print, est) == "EST"
@test sprint(print, warsaw) == "Europe/Warsaw"
@test sprint(print, apia) == "Pacific/Apia"
@test sprint(print, honolulu) == "Pacific/Honolulu"
@test sprint(print, ulyanovsk) == "Europe/Ulyanovsk"
@test sprint(print, custom) == "Test/Custom"

@test sprint(show_compact, utc) == "tz\"UTC\""
@test sprint(show_compact, gmt) == "tz\"GMT\""
@test sprint(show_compact, foo) == "FixedTimeZone(\"FOO\", 0)"
@test sprint(show_compact, null) == "FixedTimeZone(\"\", 10800)"
@test sprint(show_compact, fixed) == "tz\"UTC+01:00\""
@test sprint(show_compact, est) == "tz\"EST\""
@test sprint(show_compact, warsaw) == "tz\"Europe/Warsaw\""
@test sprint(show_compact, apia) == "tz\"Pacific/Apia\""
@test sprint(show_compact, honolulu) == "tz\"Pacific/Honolulu\""
@test sprint(show_compact, ulyanovsk) == "tz\"Europe/Ulyanovsk\""
@test sprint(show_compact, custom) == "VariableTimeZone(\"Test/Custom\", ...)"

@test sprint(show, utc) == "tz\"UTC\""
@test sprint(show, gmt) == "tz\"GMT\""
@test sprint(show, foo) == "FixedTimeZone(\"FOO\", 0)"
@test sprint(show, null) == "FixedTimeZone(\"\", 10800)"
@test sprint(show, fixed) == "tz\"UTC+01:00\""
@test sprint(show, est) == "tz\"EST\""
@test sprint(show, warsaw) == "tz\"Europe/Warsaw\""
@test sprint(show, apia) == "tz\"Pacific/Apia\""
@test sprint(show, honolulu) == "tz\"Pacific/Honolulu\""
@test sprint(show, ulyanovsk) == "tz\"Europe/Ulyanovsk\""
@test sprint(show, custom) == "VariableTimeZone(\"Test/Custom\", Transition[Transition($(repr(custom_dt)), tz\"UTC\")], nothing)"

@test sprint(show, MIME("text/plain"), utc) == "UTC"
@test sprint(show, MIME("text/plain"), gmt) == "GMT"
@test sprint(show, MIME("text/plain"), foo) == "FOO (UTC+0)"
@test sprint(show, MIME("text/plain"), null) == "UTC+03:00"
@test sprint(show, MIME("text/plain"), fixed) == "UTC+01:00"
@test sprint(show, MIME("text/plain"), est) == "EST (UTC-5)"
@test sprint(show, MIME("text/plain"), warsaw) == "Europe/Warsaw (UTC+1/UTC+2)"
@test sprint(show, MIME("text/plain"), apia) == "Pacific/Apia (UTC+13/UTC+14)"
@test sprint(show, MIME("text/plain"), honolulu) == "Pacific/Honolulu (UTC-10)"
@test sprint(show, MIME("text/plain"), ulyanovsk) == "Europe/Ulyanovsk (UTC+4)"
@test sprint(show, MIME("text/plain"), custom) == "Test/Custom (UTC+0)"

# ZonedDateTime as a string
zdt = ZonedDateTime(dt, warsaw)
@test string(zdt) == "1942-12-25T01:23:45+01:00"
@test sprint(show, zdt) == "1942-12-25T01:23:45+01:00"
@test sprint(show_compact, zdt) == "1942-12-25T01:23:45+01:00"
@test sprint(show, zdt) == "ZonedDateTime(1942, 12, 25, 1, 23, 45, tz\"Europe/Warsaw\")"
@test sprint(show, MIME("text/plain"), zdt) == "1942-12-25T01:23:45+01:00"


# TimeZone parsing
Expand Down Expand Up @@ -105,3 +129,38 @@ f = "yyyy/m/d H:M:S zzz"
df = Dates.DateFormat("yyyy-mm-ddTHH:MM:SS ZZZ")
zdt = ZonedDateTime(dt, warsaw)
@test_throws ArgumentError parse(ZonedDateTime, Dates.format(zdt, df), df)


# Displaying VariableTimeZone's vector of transitions on the REPL has a special printing
@testset "Transitions I/O" begin
transitions = honolulu.transitions # Only contains a few transitions
t = transitions[2] # 1896-01-13T22:31:26 UTC-10:30/+0 (HST)

@testset "basic" begin
@test sprint(print, t) == "1896-01-13T22:31:26 UTC-10:30/+0 (HST)"
@test sprint(show_compact, t) == "1896-01-13T22:31:26 UTC-10:30/+0 (HST)"
@test sprint(show, t) == "Transition($(repr(t.utc_datetime)), FixedTimeZone(\"HST\", -37800))"
@test sprint(show, MIME("text/plain"), t) == "1896-01-13T22:31:26 UTC-10:30/+0 (HST)"
end

@testset "REPL vector" begin
expected_full = string(
TimeZones.Transition,
"[",
join(map(t -> sprint(show, t, context=:compact => false), transitions), ", "),
"]",
)

# Note: The output here is different from the interactive REPL but is representative
# of the output.
expected_repl = string(
TimeZones.Transition,
"[",
join(map(t -> sprint(show, t, context=:compact => true), transitions), ", "),
"]",
)

@test sprint(show, transitions, context=:compact => false) == expected_full
@test sprint(show, transitions; context=:limit => true) == expected_repl
end
end
16 changes: 14 additions & 2 deletions test/utcoffset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,17 @@ let a = UTCOffset(7200, 3600), b = UTCOffset(3600, 7200)
@test isequal(a, b)
end

@test sprint(show, UTCOffset(0, 0)) == "UTC+0/+0"
@test sprint(show, UTCOffset(3600, 7200)) == "UTC+1/+2"
@test sprint(show_compact, UTCOffset(0, 0)) == "UTC+0/+0"
@test sprint(show_compact, UTCOffset(3600, 7200)) == "UTC+1/+2"

# https://github.com/JuliaLang/julia/pull/30817
if VERSION >= v"1.2.0-DEV.223"
@test sprint(show, UTCOffset(0, 0)) == "UTCOffset(Second(0), Second(0))"
@test sprint(show, UTCOffset(3600, 7200)) == "UTCOffset(Second(3600), Second(7200))"
else
@test sprint(show, UTCOffset(0, 0)) == "UTCOffset(0 seconds, 0 seconds)"
@test sprint(show, UTCOffset(3600, 7200)) == "UTCOffset(3600 seconds, 7200 seconds)"
end

@test sprint(show, MIME("text/plain"), UTCOffset(0, 0)) == "UTC+0/+0"
@test sprint(show, MIME("text/plain"), UTCOffset(3600, 7200)) == "UTC+1/+2"