timeliness 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +9 -6
- data/lib/timeliness/format.rb +15 -11
- data/lib/timeliness/format_set.rb +5 -5
- data/lib/timeliness/helpers.rb +12 -7
- data/lib/timeliness/parser.rb +8 -4
- data/lib/timeliness/version.rb +1 -1
- data/spec/timeliness/helpers_spec.rb +116 -0
- data/spec/timeliness/parser_spec.rb +6 -6
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed9d4a7bedb3b511ea9a907ccbf31b40903dd29fdd4d4c9aa3763e6827d02d2e
|
4
|
+
data.tar.gz: 6de9d02d5dfb2e96fb9899e190cf5dfbf75e29fffbe51a7c6b7c41e00fa5190e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a06066317124ddf6be084e604fb398824f5ffc4febbc33863ca42110c5cda782ccd5da8f0de11482f3a9aef6e16d33f29272777aa150a77f8eaa432db924124e
|
7
|
+
data.tar.gz: b2aba3bd75fe034df0cc867fa0d8e7aa69a895a74891ea2ce9f157ad691e437504553d195fd3a0aaa1c369c52548514f2d8360e62c2683125564ed4a93366374
|
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 0.5.2 - 2025-01-31
|
2
|
+
* Reduce allocations through on the parse hot path
|
3
|
+
|
4
|
+
= 0.5.1 - 2025-01-07
|
5
|
+
* Make frozen string compatible
|
6
|
+
|
1
7
|
= 0.5.0 - 2024-12-02
|
2
8
|
* Reduce allocations through some internal parsing changes
|
3
9
|
* Changed parse method arg handling to simple using keyword args
|
@@ -13,12 +19,10 @@
|
|
13
19
|
|
14
20
|
= 0.4.3 - 2019-06-16
|
15
21
|
* Fixed `Timeliness.ambiguous_date_format` being used in new threads if custom value set
|
16
|
-
* Moved all config from Timeliness to new Configuration class. Delegated all
|
17
|
-
old config methods to Timeliness.configuration instance.
|
22
|
+
* Moved all config from Timeliness to new Configuration class. Delegated all old config methods to Timeliness.configuration instance.
|
18
23
|
|
19
24
|
= 0.4.2 - 2019-06-15
|
20
|
-
* Fixed thread safe issue that forced you to use one of the date format methods e.g. `use_euro_formats`
|
21
|
-
to initialize the format sets in each new thread. Now a new thread will default to the global default (main thread).
|
25
|
+
* Fixed thread safe issue that forced you to use one of the date format methods e.g. `use_euro_formats` to initialize the format sets in each new thread. Now a new thread will default to the global default (main thread).
|
22
26
|
* Add `Timeliness.ambiguous_date_format` config setting (:us or :euro) to control global default for date format sets.
|
23
27
|
|
24
28
|
= 0.4.1 - 2019-06-11
|
@@ -27,8 +31,7 @@
|
|
27
31
|
* Add 'zt' format token to support 'Z' (Zulu time) zone offset i.e. +00:00 or UTC
|
28
32
|
|
29
33
|
= 0.4.0 - 2019-02-09
|
30
|
-
* Add threadsafety for use_euro_formats & use_us_formats to allow runtime
|
31
|
-
switching (andruby, timdiggins)
|
34
|
+
* Add threadsafety for use_euro_formats & use_us_formats to allow runtime switching (andruby, timdiggins)
|
32
35
|
|
33
36
|
= 0.3.10 - 2019-02-06
|
34
37
|
* Fixed file permissions in gem build
|
data/lib/timeliness/format.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Timeliness
|
2
4
|
class Format
|
3
5
|
include Helpers
|
@@ -11,7 +13,6 @@ module Timeliness
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def compile!
|
14
|
-
@token_count = 0
|
15
16
|
found_tokens, token_order = [], []
|
16
17
|
|
17
18
|
format = format_string.dup
|
@@ -19,20 +20,16 @@ module Timeliness
|
|
19
20
|
|
20
21
|
# Substitute tokens with numbered placeholder
|
21
22
|
Definitions.sorted_token_keys.each do |token|
|
22
|
-
count = 0
|
23
23
|
format.gsub!(token) do
|
24
24
|
token_regexp_str, arg_key = Definitions.format_tokens[token]
|
25
|
-
token_index = found_tokens.size
|
26
25
|
|
27
|
-
if arg_key
|
26
|
+
if arg_key && found_tokens.rassoc(arg_key)
|
28
27
|
raise CompilationFailed, "Token '#{token}' was found more than once in format '#{format_string}'. This has unexpected effects should be removed." if count > 0
|
29
|
-
count += 1
|
30
|
-
|
31
|
-
token_regexp_str = "(#{token_regexp_str})"
|
32
|
-
@token_count += 1
|
33
28
|
end
|
29
|
+
|
34
30
|
found_tokens << [ token_regexp_str, arg_key ]
|
35
31
|
|
32
|
+
token_index = found_tokens.size - 1
|
36
33
|
"%<#{token_index}>"
|
37
34
|
end
|
38
35
|
end
|
@@ -40,11 +37,18 @@ module Timeliness
|
|
40
37
|
# Replace placeholders with token regexps
|
41
38
|
format.gsub!(/%<(\d+)>/) do
|
42
39
|
token_regexp_str, arg_key = found_tokens[$1.to_i]
|
43
|
-
|
44
|
-
|
40
|
+
|
41
|
+
if arg_key
|
42
|
+
token_order << arg_key
|
43
|
+
"(#{token_regexp_str})"
|
44
|
+
else
|
45
|
+
token_regexp_str
|
46
|
+
end
|
45
47
|
end
|
46
48
|
|
47
|
-
|
49
|
+
@token_count = token_order.size
|
50
|
+
|
51
|
+
define_process_method(token_order)
|
48
52
|
@regexp_string = format
|
49
53
|
@regexp = Regexp.new("^(?>#{format})$")
|
50
54
|
self
|
@@ -31,12 +31,12 @@ module Timeliness
|
|
31
31
|
|
32
32
|
def match(string, format_string=nil)
|
33
33
|
format = single_format(format_string) if format_string
|
34
|
-
match_regexp = format
|
34
|
+
match_regexp = format ? format.regexp : @regexp
|
35
35
|
|
36
|
-
match_regexp.match(string)
|
37
|
-
captures = match_data.captures
|
38
|
-
index
|
39
|
-
values
|
36
|
+
if (match_data = match_regexp.match(string))
|
37
|
+
captures = match_data.captures # For a multi-format regexp there are lots of nils
|
38
|
+
index = captures.index { |e| !e.nil? } # Find the start of captures for matched format
|
39
|
+
values = captures[index, 8]
|
40
40
|
format ||= @match_indexes[index]
|
41
41
|
format.process(*values)
|
42
42
|
end
|
data/lib/timeliness/helpers.rb
CHANGED
@@ -1,10 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Timeliness
|
2
4
|
module Helpers
|
5
|
+
# Helper methods used in format component processing. See Definitions.
|
3
6
|
|
4
7
|
def full_hour(hour, meridian)
|
5
8
|
hour = hour.to_i
|
6
9
|
return hour if meridian.nil?
|
7
|
-
|
10
|
+
|
11
|
+
meridian.delete!('.')
|
12
|
+
meridian.downcase!
|
13
|
+
|
14
|
+
if meridian == 'am'
|
8
15
|
raise(ArgumentError) if hour == 0 || hour > 12
|
9
16
|
hour == 12 ? 0 : hour
|
10
17
|
else
|
@@ -20,9 +27,9 @@ module Timeliness
|
|
20
27
|
end
|
21
28
|
year.to_i
|
22
29
|
end
|
23
|
-
|
30
|
+
|
24
31
|
def month_index(month)
|
25
|
-
return month.to_i if month.
|
32
|
+
return month.to_i if month.match?(/\d/)
|
26
33
|
(month.length > 3 ? month_names : abbr_month_names).index { |str| month.casecmp?(str) }
|
27
34
|
end
|
28
35
|
|
@@ -39,10 +46,8 @@ module Timeliness
|
|
39
46
|
end
|
40
47
|
|
41
48
|
def offset_in_seconds(offset)
|
42
|
-
|
43
|
-
|
44
|
-
parts[1] = parts[1].to_f / 60
|
45
|
-
(parts[0] + parts[1]) * sign * 3600
|
49
|
+
offset =~ /^([-+])?(\d{2}):?(\d{2})/
|
50
|
+
($1 == '-' ? -1 : 1) * ($2.to_f * 3600 + $3.to_f * 60)
|
46
51
|
end
|
47
52
|
|
48
53
|
def i18n_loaded?
|
data/lib/timeliness/parser.rb
CHANGED
@@ -18,19 +18,20 @@ module Timeliness
|
|
18
18
|
|
19
19
|
default_values_by_type(time_array, type, options) unless type == :datetime
|
20
20
|
|
21
|
-
make_time(time_array
|
21
|
+
make_time(time_array, options[:zone])
|
22
22
|
rescue NoMethodError => ex
|
23
23
|
raise ex unless ex.message =~ /undefined method `(zone|use_zone|current)' for Time:Class/
|
24
24
|
raise MissingTimezoneSupport, "ActiveSupport timezone support must be loaded to use timezones other than :utc and :local."
|
25
25
|
end
|
26
26
|
|
27
27
|
def make_time(time_array, zone_option=nil)
|
28
|
-
return nil unless fast_date_valid_with_fallback(
|
28
|
+
return nil unless fast_date_valid_with_fallback(time_array[0], time_array[1], time_array[2])
|
29
29
|
|
30
30
|
zone, offset = zone_and_offset(time_array[7]) if time_array[7]
|
31
31
|
|
32
32
|
value = create_time_in_zone(time_array[0..6].compact, zone || zone_option)
|
33
33
|
value = shift_time_to_zone(value, zone_option) if zone
|
34
|
+
|
34
35
|
return nil unless value
|
35
36
|
|
36
37
|
offset ? value + (value.utc_offset - offset) : value
|
@@ -74,9 +75,12 @@ module Timeliness
|
|
74
75
|
def default_values_by_type(values, type, options)
|
75
76
|
case type
|
76
77
|
when :date
|
77
|
-
values
|
78
|
+
values.fill(nil, 3..7)
|
78
79
|
when :time
|
79
|
-
|
80
|
+
current_date = current_date(options)
|
81
|
+
values[0] = current_date[0]
|
82
|
+
values[1] = current_date[1]
|
83
|
+
values[2] = current_date[2]
|
80
84
|
when nil
|
81
85
|
dummy_date = current_date(options)
|
82
86
|
values[0] ||= dummy_date[0]
|
data/lib/timeliness/version.rb
CHANGED
@@ -0,0 +1,116 @@
|
|
1
|
+
describe Timeliness::Helpers do
|
2
|
+
include Timeliness::Helpers
|
3
|
+
|
4
|
+
describe "#full_hour" do
|
5
|
+
it "should convert a 12-hour clock AM time to 24-hour format correctly" do
|
6
|
+
expect(full_hour(12, 'am')).to eq 0
|
7
|
+
expect(full_hour(1, 'am')).to eq 1
|
8
|
+
expect(full_hour(10, 'am')).to eq 10
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should convert a 12-hour clock PM time to 24-hour format correctly" do
|
12
|
+
expect(full_hour(12, 'pm')).to eq 12
|
13
|
+
expect(full_hour(1, 'pm')).to eq 13
|
14
|
+
expect(full_hour(10, 'pm')).to eq 22
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise ArgumentError when given an hour of 0 with AM meridian" do
|
18
|
+
expect { full_hour(0, 'am') }.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should raise ArgumentError when given an hour greater than 12 with AM meridian" do
|
22
|
+
expect { full_hour(13, 'am') }.to raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should handle meridian strings with periods" do
|
26
|
+
expect(full_hour(10, 'A.M.')).to eq 10
|
27
|
+
expect(full_hour(10, 'P.M.')).to eq 22
|
28
|
+
expect(full_hour(12, 'A.M.')).to eq 0
|
29
|
+
expect(full_hour(12, 'P.M.')).to eq 12
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#unambiguous_year" do
|
34
|
+
before do
|
35
|
+
@original_threshold = Timeliness.configuration.ambiguous_year_threshold
|
36
|
+
Timeliness.configuration.ambiguous_year_threshold = 30
|
37
|
+
Timecop.freeze(Time.new(2023, 1, 1))
|
38
|
+
end
|
39
|
+
|
40
|
+
after do
|
41
|
+
Timeliness.configuration.ambiguous_year_threshold = @original_threshold
|
42
|
+
Timecop.return
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should convert 2-digit years to 4-digit years based on the current century and ambiguous year threshold" do
|
46
|
+
# Current century (21st) for years below threshold
|
47
|
+
expect(unambiguous_year('29')).to eq 2029
|
48
|
+
|
49
|
+
# Previous century (20th) for years above or equal to threshold
|
50
|
+
expect(unambiguous_year('30')).to eq 1930
|
51
|
+
expect(unambiguous_year('99')).to eq 1999
|
52
|
+
|
53
|
+
# Should not modify years that are already 4-digits
|
54
|
+
expect(unambiguous_year('2023')).to eq 2023
|
55
|
+
|
56
|
+
# Should handle single digit years with padding
|
57
|
+
expect(unambiguous_year('7')).to eq 2007
|
58
|
+
|
59
|
+
# Should handle years at century boundaries
|
60
|
+
expect(unambiguous_year('00')).to eq 2000
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#month_index" do
|
65
|
+
before do
|
66
|
+
allow(self).to receive(:i18n_loaded?).and_return(false)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should correctly parse month names as month indices regardless of case" do
|
70
|
+
# Testing with full month names
|
71
|
+
expect(month_index("january")).to eq 1
|
72
|
+
expect(month_index("MARCH")).to eq 3
|
73
|
+
expect(month_index("DeCeMbEr")).to eq 12
|
74
|
+
|
75
|
+
# Testing with abbreviated month names
|
76
|
+
expect(month_index("jan")).to eq 1
|
77
|
+
expect(month_index("MAR")).to eq 3
|
78
|
+
expect(month_index("deC")).to eq 12
|
79
|
+
|
80
|
+
# Testing with numeric month
|
81
|
+
expect(month_index("7")).to eq 7
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#microseconds" do
|
86
|
+
it "should convert microsecond strings to integer microsecond values" do
|
87
|
+
expect(microseconds('0')).to eq 0
|
88
|
+
expect(microseconds('1')).to eq 100000
|
89
|
+
expect(microseconds('01')).to eq 10000
|
90
|
+
expect(microseconds('001')).to eq 1000
|
91
|
+
expect(microseconds('9')).to eq 900000
|
92
|
+
expect(microseconds('99')).to eq 990000
|
93
|
+
expect(microseconds('999')).to eq 999000
|
94
|
+
expect(microseconds('999999')).to eq 999999
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#offset_in_seconds" do
|
99
|
+
it "should calculate offset in seconds from timezone string formats" do
|
100
|
+
# Standard format with colon
|
101
|
+
expect(offset_in_seconds('+10:00')).to eq 36000
|
102
|
+
expect(offset_in_seconds('-05:30')).to eq -19800
|
103
|
+
|
104
|
+
# Format without colon
|
105
|
+
expect(offset_in_seconds('+1030')).to eq 37800
|
106
|
+
expect(offset_in_seconds('-0530')).to eq -19800
|
107
|
+
|
108
|
+
# Default positive sign when omitted
|
109
|
+
expect(offset_in_seconds('08:00')).to eq 28800
|
110
|
+
|
111
|
+
# Zero offset
|
112
|
+
expect(offset_in_seconds('+00:00')).to eq 0
|
113
|
+
expect(offset_in_seconds('-00:00')).to eq 0
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -2,7 +2,7 @@ describe Timeliness::Parser do
|
|
2
2
|
def self.timezone_settings(zone: nil, output: nil)
|
3
3
|
before do
|
4
4
|
Time.zone = zone if zone
|
5
|
-
Timeliness.configuration.default_timezone = output if output
|
5
|
+
Timeliness.configuration.default_timezone = output if output
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -82,23 +82,23 @@ describe Timeliness::Parser do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
context "string with zone offset value" do
|
85
|
-
context "when current timezone is
|
85
|
+
context "when current timezone is later than string zone" do
|
86
86
|
timezone_settings zone: 'Australia/Melbourne', output: :current
|
87
87
|
|
88
|
-
it 'should return value shifted by positive offset in default timezone' do
|
88
|
+
it 'should return value shifted by positive string offset in default timezone' do
|
89
89
|
value = parse("2000-06-01T12:00:00+02:00")
|
90
90
|
expect(value).to eq Time.zone.local(2000,6,1,20,0,0)
|
91
91
|
expect(value.utc_offset).to eq 10.hours
|
92
92
|
end
|
93
93
|
|
94
|
-
it 'should return value shifted by negative offset in default timezone' do
|
94
|
+
it 'should return value shifted by negative string offset in default timezone' do
|
95
95
|
value = parse("2000-06-01T12:00:00-01:00")
|
96
96
|
expect(value).to eq Time.zone.local(2000,6,1,23,0,0)
|
97
97
|
expect(value.utc_offset).to eq 10.hours
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
context "when current timezone is
|
101
|
+
context "when current timezone is earlier than string zone" do
|
102
102
|
timezone_settings zone: 'America/Phoenix', output: :current
|
103
103
|
|
104
104
|
it 'should return value shifted by positive offset in default timezone' do
|
@@ -146,7 +146,7 @@ describe Timeliness::Parser do
|
|
146
146
|
|
147
147
|
context "string with zulu time abbreviation 'Z'" do
|
148
148
|
timezone_settings zone: 'Australia/Melbourne'
|
149
|
-
|
149
|
+
|
150
150
|
it 'should return value using string zone adjusted to default :current timezone' do
|
151
151
|
Timeliness.configuration.default_timezone = :current
|
152
152
|
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timeliness
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Meehan
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-05-13 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activesupport
|
@@ -164,6 +163,7 @@ files:
|
|
164
163
|
- spec/timeliness/definitions_spec.rb
|
165
164
|
- spec/timeliness/format_set_spec.rb
|
166
165
|
- spec/timeliness/format_spec.rb
|
166
|
+
- spec/timeliness/helpers_spec.rb
|
167
167
|
- spec/timeliness/parser_spec.rb
|
168
168
|
- spec/timeliness_helper.rb
|
169
169
|
- timeliness.gemspec
|
@@ -176,7 +176,6 @@ metadata:
|
|
176
176
|
changelog_uri: https://github.com/adzap/timeliness/blob/master/CHANGELOG.rdoc
|
177
177
|
source_code_uri: https://github.com/adzap/timeliness
|
178
178
|
wiki_uri: https://github.com/adzap/timeliness/wiki
|
179
|
-
post_install_message:
|
180
179
|
rdoc_options: []
|
181
180
|
require_paths:
|
182
181
|
- lib
|
@@ -191,8 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
191
190
|
- !ruby/object:Gem::Version
|
192
191
|
version: '0'
|
193
192
|
requirements: []
|
194
|
-
rubygems_version: 3.
|
195
|
-
signing_key:
|
193
|
+
rubygems_version: 3.6.3
|
196
194
|
specification_version: 4
|
197
195
|
summary: Date/time parsing for the control freak.
|
198
196
|
test_files: []
|