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

YAML/Time parsers/formatters now take in care nanoseconds #5070

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion spec/std/yaml/serialization_spec.cr
Expand Up @@ -304,7 +304,7 @@ describe "YAML serialization" do
time.to_yaml.should eq("--- 2010-11-12\n...\n")
end

it "does for utc time with milliseconds" do
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.to_yaml.should eq("--- 2010-11-12 01:02:03.456\n...\n")
end
Expand Down
1 change: 1 addition & 0 deletions src/time/format.cr
Expand Up @@ -28,6 +28,7 @@
# * **%k**: hour of the day, 24-hour clock, blank padded (" 0", " 1", ..., "24")
# * **%l**: hour of the day, 12-hour clock, blank padded (" 0", " 1", ..., "12")
# * **%L**: milliseconds, zero padded (000, 001, ..., 999)
# * **%N**: nanoseconds, zero padded (000000000, 000000001, ..., 999999999)
# * **%m**: month number, zero padded (01, 02, ..., 12)
# * **%_m**: month number, blank padded (" 1", " 2", ..., "12")
# * **%-m**: month number (1, 2, ..., 12)
Expand Down
13 changes: 13 additions & 0 deletions src/time/format/formatter.cr
Expand Up @@ -113,6 +113,10 @@ struct Time::Format
pad3 time.millisecond, '0'
end

def nanoseconds
pad9 time.nanosecond, '0'
end

def am_pm
io << (time.hour < 12 ? "am" : "pm")
end
Expand Down Expand Up @@ -216,5 +220,14 @@ struct Time::Format
io.write_byte padding.ord.to_u8 if value < 1000
pad3 value, padding
end

def pad9(value, padding)
io.write_byte padding.ord.to_u8 if value < 100000000
io.write_byte padding.ord.to_u8 if value < 10000000
io.write_byte padding.ord.to_u8 if value < 1000000
io.write_byte padding.ord.to_u8 if value < 100000
io.write_byte padding.ord.to_u8 if value < 10000
pad4 value, padding
end
end
end
19 changes: 16 additions & 3 deletions src/time/format/parser.cr
Expand Up @@ -166,14 +166,27 @@ struct Time::Format
def milliseconds
# Consume more than 3 digits (12 seems a good maximum),
# and later just use the first 3 digits because Time
# only has microsecond precision.
# need millisecond precision.
pos = @reader.pos
millisecond = consume_number(12)
millisecond = consume_number_i64(12)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think there is a need for 64 bit integer here. You should use consume_number. It will still read 12 digits.

Copy link
Contributor Author

@akzhan akzhan Oct 4, 2017

Choose a reason for hiding this comment

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

  1. this value will be multiplied by Int64 later. Less conversions needed.
  2. consume_number is just consume_number_i64.to_i.
  3. parsing of 12 numbers at once is just current reasonable choice (picoseconds). It may be changed later.

digits = @reader.pos - pos
if digits > 3
millisecond /= 10 ** (digits - 3)
end
@nanosecond = millisecond * Time::NANOSECONDS_PER_MILLISECOND
@nanosecond = (millisecond * Time::NANOSECONDS_PER_MILLISECOND).to_i
end

def nanoseconds
# Consume more than 9 digits (12 seems a good maximum),
# and later just use the first 9 digits because Time
# only has nanosecond precision.
pos = @reader.pos
nanosecond = consume_number(12)
digits = @reader.pos - pos
if digits > 9
nanosecond /= 10 ** (digits - 9)
end
@nanosecond = nanosecond
end

def am_pm
Expand Down
2 changes: 2 additions & 0 deletions src/time/format/pattern.cr
Expand Up @@ -49,6 +49,8 @@ struct Time::Format
hour_12_blank_padded
when 'L'
milliseconds
when 'N'
nanoseconds
when 'm'
month_zero_padded
when 'M'
Expand Down
14 changes: 7 additions & 7 deletions src/yaml/schema/core/time_parser.cr
Expand Up @@ -75,13 +75,13 @@ struct YAML::Schema::Core::TimeParser
return new_time(year, month, day, hour, minute, second)
end

millisecond = 0
nanosecond = 0

if current_char == '.'
next_char

millisecond = parse_milliseconds
return nil unless millisecond
nanosecond = parse_nanoseconds
return nil unless nanosecond
end

skip_space
Expand Down Expand Up @@ -109,22 +109,22 @@ struct YAML::Schema::Core::TimeParser

return nil if @reader.has_next?

time = new_time(year, month, day, hour, minute, second, nanosecond: millisecond * 1_000_000)
time = new_time(year, month, day, hour, minute, second, nanosecond)
Copy link
Contributor

Choose a reason for hiding this comment

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

should be nanosecond: nanosecond.

if time && tz_offset
time = time - tz_offset.minutes
end
time
end

def parse_milliseconds
def parse_nanoseconds
return nil unless current_char.ascii_number?

multiplier = 100
multiplier = Time::NANOSECONDS_PER_SECOND / 10
number = current_char.to_i

next_char

2.times do
8.times do
break unless current_char.ascii_number?

number *= 10
Expand Down