Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Time.utc #5321

Merged
merged 3 commits into from
Nov 28, 2017
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 spec/std/gzip/gzip_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe Gzip do
it "writes and reads to memory" do
io = IO::Memory.new

time = Time.new(2016, 1, 2)
time = Time.utc(2016, 1, 2)
os = 4_u8
extra = Bytes[1, 2, 3]
name = "foo.txt"
Expand Down
10 changes: 5 additions & 5 deletions spec/std/http/cookie_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ module HTTP

it "parses expires rfc1123" do
cookie = parse_set_cookie("key=value; expires=Sun, 06 Nov 1994 08:49:37 GMT")
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)

cookie.name.should eq("key")
cookie.value.should eq("value")
Expand All @@ -106,7 +106,7 @@ module HTTP

it "parses expires rfc1036" do
cookie = parse_set_cookie("key=value; expires=Sunday, 06-Nov-94 08:49:37 GMT")
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)

cookie.name.should eq("key")
cookie.value.should eq("value")
Expand All @@ -115,7 +115,7 @@ module HTTP

it "parses expires ansi c" do
cookie = parse_set_cookie("key=value; expires=Sun Nov 6 08:49:37 1994")
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)

cookie.name.should eq("key")
cookie.value.should eq("value")
Expand All @@ -124,12 +124,12 @@ module HTTP

it "parses expires ansi c, variant with zone" do
cookie = parse_set_cookie("bla=; expires=Thu, 01 Jan 1970 00:00:00 -0000")
cookie.expires.should eq(Time.new(1970, 1, 1, 0, 0, 0))
cookie.expires.should eq(Time.utc(1970, 1, 1, 0, 0, 0))
end

it "parses full" do
cookie = parse_set_cookie("key=value; path=/test; domain=www.example.com; HttpOnly; Secure; expires=Sun, 06 Nov 1994 08:49:37 GMT")
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)

cookie.name.should eq("key")
cookie.value.should eq("value")
Expand Down
2 changes: 1 addition & 1 deletion spec/std/http/formdata/builder_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe HTTP::FormData::Builder do
g.field("baz", "qux", HTTP::Headers{"X-Testing" => "headers"})

body = IO::Memory.new "file content"
time = Time.new(2016, 1, 1, 12, 0, 0, kind: Time::Kind::Utc)
time = Time.utc(2016, 1, 1, 12, 0, 0)
metadata = HTTP::FormData::FileMetadata.new("filename.txt \"", time, time, time, 12_u64)
headers = HTTP::Headers{"Foo" => "Bar", "Baz" => "Qux"}
g.file("file-test", body, metadata, headers)
Expand Down
6 changes: 3 additions & 3 deletions spec/std/http/formdata_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ describe HTTP::FormData do

name.should eq("foo")
meta.filename.should eq(%q(foo"\bar baz\))
meta.creation_time.should eq(Time.new(1997, 2, 12, 21, 29, 51, nanosecond: 0, kind: Time::Kind::Utc))
meta.modification_time.should eq(Time.new(1997, 2, 12, 21, 29, 51, nanosecond: 0, kind: Time::Kind::Utc))
meta.read_time.should eq(Time.new(1997, 2, 12, 21, 29, 51, nanosecond: 0, kind: Time::Kind::Utc))
meta.creation_time.should eq(Time.utc(1997, 2, 12, 21, 29, 51, nanosecond: 0))
meta.modification_time.should eq(Time.utc(1997, 2, 12, 21, 29, 51, nanosecond: 0))
meta.read_time.should eq(Time.utc(1997, 2, 12, 21, 29, 51, nanosecond: 0))
meta.size.should eq(432334)
end
end
Expand Down
16 changes: 8 additions & 8 deletions spec/std/http/http_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,43 @@ require "http"

describe HTTP do
it "parses RFC 1123" do
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)
HTTP.parse_time("Sun, 06 Nov 1994 08:49:37 GMT").should eq(time)
end

it "parses RFC 1123 without day name" do
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be UTC (following line check it's a GMT time)

Copy link
Member Author

@straight-shoota straight-shoota Nov 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is completely valid.

  • HTTP dates represented in the RFC 1123 format are usually marked as GMT but are really UTC times, because the standard doesn't recognize UTC. This is also how the Crystal implementation works.
  • This method is used to create a time instance refering to a specific point in time. For the equality check, the individual time zones do not really matter, because the values are compared based on UTC anyway. It has just to be assured that both, the RFC 1123 string representation and the local comparison date refere to the same point in time. And that, they do.
  • Apart from that, there is not really a difference between dates in GMT and UTC zones at all in Crystal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, forgot GMT was a timezone.

HTTP.parse_time("06 Nov 1994 08:49:37 GMT").should eq(time)
end

it "parses RFC 1036" do
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)
HTTP.parse_time("Sunday, 06-Nov-94 08:49:37 GMT").should eq(time)
end

it "parses ANSI C" do
time = Time.new(1994, 11, 6, 8, 49, 37)
time = Time.utc(1994, 11, 6, 8, 49, 37)
HTTP.parse_time("Sun Nov 6 08:49:37 1994").should eq(time)
time2 = Time.new(1994, 11, 16, 8, 49, 37)
time2 = Time.utc(1994, 11, 16, 8, 49, 37)
HTTP.parse_time("Sun Nov 16 08:49:37 1994").should eq(time2)
end

it "parses and is UTC (#2744)" do
date = "Mon, 09 Sep 2011 23:36:00 GMT"
parsed_time = HTTP.parse_time(date).not_nil!
parsed_time.kind.should eq(Time::Kind::Utc)
parsed_time.utc?.should be_true
end

it "parses and is local (#2744)" do
date = "Mon, 09 Sep 2011 23:36:00 -0300"
parsed_time = HTTP.parse_time(date).not_nil!
parsed_time.kind.should eq(Time::Kind::Local)
parsed_time.local?.should be_true
parsed_time.to_utc.to_s.should eq("2011-09-10 02:36:00 UTC")
end

describe "generates RFC 1123" do
it "without time zone" do
time = Time.new(1994, 11, 6, 8, 49, 37, nanosecond: 0, kind: Time::Kind::Utc)
time = Time.utc(1994, 11, 6, 8, 49, 37, nanosecond: 0)
HTTP.rfc1123_date(time).should eq("Sun, 06 Nov 1994 08:49:37 GMT")
end

Expand Down
4 changes: 2 additions & 2 deletions spec/std/json/serialization_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe "JSON serialization" do
end

it "deserializes Time" do
Time.from_json(%("2016-11-16T09:55:48-0300")).to_utc.should eq(Time.new(2016, 11, 16, 12, 55, 48, kind: Time::Kind::Utc))
Time.from_json(%("2016-11-16T09:55:48-0300")).to_utc.should eq(Time.utc(2016, 11, 16, 12, 55, 48))
end

describe "parse exceptions" do
Expand Down Expand Up @@ -312,7 +312,7 @@ describe "JSON serialization" do
end

it "does for time" do
Time.new(2016, 11, 16, 12, 55, 48, kind: Time::Kind::Utc).to_json.should eq(%("2016-11-16T12:55:48+0000"))
Time.utc(2016, 11, 16, 12, 55, 48).to_json.should eq(%("2016-11-16T12:55:48+0000"))
end
end
end
14 changes: 7 additions & 7 deletions spec/std/time/time_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ describe Time do
it "initialize with .epoch" do
seconds = 1439404155
time = Time.epoch(seconds)
time.should eq(Time.new(2015, 8, 12, 18, 29, 15, kind: Time::Kind::Utc))
time.should eq(Time.utc(2015, 8, 12, 18, 29, 15))
time.epoch.should eq(seconds)
end

it "initialize with .epoch_ms" do
milliseconds = 1439404155000
time = Time.epoch_ms(milliseconds)
time.should eq(Time.new(2015, 8, 12, 18, 29, 15, kind: Time::Kind::Utc))
time.should eq(Time.utc(2015, 8, 12, 18, 29, 15))
time.epoch_ms.should eq(milliseconds)
end

Expand Down Expand Up @@ -246,7 +246,7 @@ describe Time do
end

it "gets unix epoch seconds" do
t1 = Time.new 2014, 10, 30, 21, 18, 13, nanosecond: 0, kind: Time::Kind::Utc
t1 = Time.utc 2014, 10, 30, 21, 18, 13, nanosecond: 0
t1.epoch.should eq(1414703893)
t1.epoch_f.should be_close(1414703893, 1e-01)
end
Expand Down Expand Up @@ -369,7 +369,7 @@ describe Time do

t.to_s("%Y-%m-hello").should eq("2014-01-hello")

t = Time.new 2014, 1, 2, 3, 4, 5, nanosecond: 6, kind: Time::Kind::Utc
t = Time.utc 2014, 1, 2, 3, 4, 5, nanosecond: 6
t.to_s("%s").should eq("1388631845")
end

Expand Down Expand Up @@ -521,7 +521,7 @@ describe Time do

it "can parse in UTC" do
time = Time.parse("2014-10-31 11:12:13", "%F %T", Time::Kind::Utc)
time.kind.should eq(Time::Kind::Utc)
time.utc?.should be_true
end

it "at" do
Expand Down Expand Up @@ -600,9 +600,9 @@ describe Time do

it "preserves kind when adding" do
time = Time.utc_now
time.kind.should eq(Time::Kind::Utc)
time.utc?.should be_true

(time + 5.minutes).kind.should eq(Time::Kind::Utc)
(time + 5.minutes).utc?.should be_true
end

it "asks for day name" do
Expand Down
4 changes: 2 additions & 2 deletions spec/std/yaml/any_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ describe YAML::Any do

it "gets time" do
value = YAML.parse("2010-01-02").as_time
value.should eq(Time.new(2010, 1, 2, kind: Time::Kind::Utc))
value.should eq(Time.utc(2010, 1, 2))

value = YAML.parse("2010-01-02").as_time?
value.should eq(Time.new(2010, 1, 2, kind: Time::Kind::Utc))
value.should eq(Time.utc(2010, 1, 2))

value = YAML.parse("hello").as_time?
value.should be_nil
Expand Down
36 changes: 18 additions & 18 deletions spec/std/yaml/schema/core_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,23 @@ describe YAML::Schema::Core do
it_parses_scalar "-0x123abc", -0x123abc

# time
it_parses_scalar "2002-12-14", Time.new(2002, 12, 14, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2", Time.new(2002, 1, 2, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12", Time.new(2002, 1, 2, 10, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2 10:11:12", Time.new(2002, 1, 2, 10, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2 1:11:12", Time.new(2002, 1, 2, 1, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12.3", Time.new(2002, 1, 2, 10, 11, 12, nanosecond: 300_000_000, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12.34", Time.new(2002, 1, 2, 10, 11, 12, nanosecond: 340_000_000, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12.345", Time.new(2002, 1, 2, 10, 11, 12, nanosecond: 345_000_000, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12.3456", Time.new(2002, 1, 2, 10, 11, 12, nanosecond: 345_600_000, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12Z", Time.new(2002, 1, 2, 10, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12 Z", Time.new(2002, 1, 2, 10, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12 +3", Time.new(2002, 1, 2, 7, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12 +03:00", Time.new(2002, 1, 2, 7, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12 -03:00", Time.new(2002, 1, 2, 13, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12 -03:31", Time.new(2002, 1, 2, 13, 42, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12-03:31", Time.new(2002, 1, 2, 13, 42, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-1-2T10:11:12 +0300", Time.new(2002, 1, 2, 7, 11, 12, kind: Time::Kind::Utc)
it_parses_scalar "2002-12-14", Time.utc(2002, 12, 14)
it_parses_scalar "2002-1-2", Time.utc(2002, 1, 2)
it_parses_scalar "2002-1-2T10:11:12", Time.utc(2002, 1, 2, 10, 11, 12)
it_parses_scalar "2002-1-2 10:11:12", Time.utc(2002, 1, 2, 10, 11, 12)
it_parses_scalar "2002-1-2 1:11:12", Time.utc(2002, 1, 2, 1, 11, 12)
it_parses_scalar "2002-1-2T10:11:12.3", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 300_000_000)
it_parses_scalar "2002-1-2T10:11:12.34", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 340_000_000)
it_parses_scalar "2002-1-2T10:11:12.345", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 345_000_000)
it_parses_scalar "2002-1-2T10:11:12.3456", Time.utc(2002, 1, 2, 10, 11, 12, nanosecond: 345_600_000)
it_parses_scalar "2002-1-2T10:11:12Z", Time.utc(2002, 1, 2, 10, 11, 12)
it_parses_scalar "2002-1-2T10:11:12 Z", Time.utc(2002, 1, 2, 10, 11, 12)
it_parses_scalar "2002-1-2T10:11:12 +3", Time.utc(2002, 1, 2, 7, 11, 12)
it_parses_scalar "2002-1-2T10:11:12 +03:00", Time.utc(2002, 1, 2, 7, 11, 12)
it_parses_scalar "2002-1-2T10:11:12 -03:00", Time.utc(2002, 1, 2, 13, 11, 12)
it_parses_scalar "2002-1-2T10:11:12 -03:31", Time.utc(2002, 1, 2, 13, 42, 12)
it_parses_scalar "2002-1-2T10:11:12-03:31", Time.utc(2002, 1, 2, 13, 42, 12)
it_parses_scalar "2002-1-2T10:11:12 +0300", Time.utc(2002, 1, 2, 7, 11, 12)

# invalid time
it_parses_string "2002-34-45"
Expand Down Expand Up @@ -197,6 +197,6 @@ describe YAML::Schema::Core do
it_raises_on_parse "!!str [1]", "Expected SCALAR"

# # !!timestamp
it_parses "!!timestamp 2010-01-02", Time.new(2010, 1, 2, kind: Time::Kind::Utc)
it_parses "!!timestamp 2010-01-02", Time.utc(2010, 1, 2)
it_raises_on_parse "!!timestamp foo", "Invalid timestamp"
end
8 changes: 4 additions & 4 deletions spec/std/yaml/serialization_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe "YAML serialization" do
end

it "deserializes time" do
Time.from_yaml("2010-11-12").should eq(Time.new(2010, 11, 12, kind: Time::Kind::Utc))
Time.from_yaml("2010-11-12").should eq(Time.utc(2010, 11, 12))
end

it "deserializes bytes" do
Expand Down Expand Up @@ -295,17 +295,17 @@ describe "YAML serialization" do
end

it "does for utc time" do
time = Time.new(2010, 11, 12, 1, 2, 3, kind: Time::Kind::Utc)
time = Time.utc(2010, 11, 12, 1, 2, 3)
time.to_yaml.should eq("--- 2010-11-12 01:02:03\n...\n")
end

it "does for time at date" do
time = Time.new(2010, 11, 12, kind: Time::Kind::Utc)
time = Time.utc(2010, 11, 12)
time.to_yaml.should eq("--- 2010-11-12\n...\n")
end

it "does for utc time with nanoseconds" do
time = Time.new(2010, 11, 12, 1, 2, 3, nanosecond: 456_000_000, kind: Time::Kind::Utc)
time = Time.utc(2010, 11, 12, 1, 2, 3, nanosecond: 456_000_000)
time.to_yaml.should eq("--- 2010-11-12 01:02:03.456\n...\n")
end

Expand Down
26 changes: 19 additions & 7 deletions src/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ require "crystal/system/time"
# Time.new(2016, 2, 15) # => 2016-02-15 00:00:00
#
# # Specifying a time
# Time.new(2016, 2, 15, 10, 20, 30) # => 2016-02-15 10:20:30 UTC
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc line was actually wrong, Time.new(2016, 2, 15, 10, 20, 30) currently returns a time of kind Unspecified, which does not have a UTC timezone.

# Time.new(2016, 2, 15, 10, 20, 30) # => 2016-02-15 10:20:30
#
# # Creating a time instance in UTC
# Time.utc(2016, 2, 15, 10, 20, 30) # => 2016-02-15 10:20:30 UTC
# ```
#
# ### Formatting Time
Expand Down Expand Up @@ -225,7 +228,7 @@ struct Time
# Time.epoch(981173106) # => 2001-02-03 04:05:06 UTC
# ```
def self.epoch(seconds : Int) : Time
new(seconds: UNIX_SECONDS + seconds, nanoseconds: 0, kind: Kind::Utc)
utc(seconds: UNIX_SECONDS + seconds, nanoseconds: 0)
end

# Returns a new `Time` instance that corresponds to the number
Expand All @@ -239,7 +242,17 @@ struct Time
milliseconds = milliseconds.to_i64
seconds = UNIX_SECONDS + (milliseconds / 1_000)
nanoseconds = (milliseconds % 1000) * NANOSECONDS_PER_MILLISECOND
new(seconds: seconds, nanoseconds: nanoseconds.to_i, kind: Kind::Utc)
utc(seconds: seconds, nanoseconds: nanoseconds.to_i)
end

# Returns a new `Time` instance at the specified time in UTC time zone.
def self.utc(year, month, day, hour = 0, minute = 0, second = 0, *, nanosecond = 0) : Time
new(year, month, day, hour, minute, second, nanosecond: nanosecond, kind: Kind::Utc)
end

# Returns a new `Time` instance at the specified time in UTC time zone.
def self.utc(*, seconds : Int64, nanoseconds : Int32) : Time
new(seconds: seconds, nanoseconds: nanoseconds, kind: Kind::Utc)
end

def clone : self
Expand Down Expand Up @@ -334,7 +347,7 @@ struct Time
# Returns the current time in UTC time zone.
def self.utc_now : Time
seconds, nanoseconds = compute_seconds_and_nanoseconds
new(seconds: seconds, nanoseconds: nanoseconds, kind: Kind::Utc)
utc(seconds: seconds, nanoseconds: nanoseconds)
end

# Returns a copy of `self` with time-of-day components (hour, minute, ...) set to zero.
Expand Down Expand Up @@ -539,10 +552,9 @@ struct Time
if utc?
self
else
Time.new(
Time.utc(
seconds: total_seconds - Time.compute_offset,
nanoseconds: nanosecond,
kind: Kind::Utc,
nanoseconds: nanosecond
)
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/yaml/schema/core/time_parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ struct YAML::Schema::Core::TimeParser
end

def new_time(*args, **named_args)
Time.new(*args, **named_args, kind: Time::Kind::Utc)
Time.utc(*args, **named_args)
rescue
nil
end
Expand Down