date_values 0.2.0 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f294856b7f5149ded52770b4d43d003b5cac6e8ee533f81ec003ac04caf9988b
4
- data.tar.gz: 8c1f6046926d15cda1a01e55f89e2975b92e6cf515bbc21cb0b6892d816bf93b
3
+ metadata.gz: 10dc98d9c1080745a0aebb8880f9bbafd34d1d5c9103736683b35633f6cd8d2c
4
+ data.tar.gz: aa7288bf8c473376b30f0e46fadfd1b78c79c34173deae88055d3ce5f7e47ec5
5
5
  SHA512:
6
- metadata.gz: 05e23c3cd91ddda9dcdf5ae63e81dd89c7a6bfd5150dab2893231ebe63c12bc64023cae0667c296e4e88db2dd5869ebdab39245370291cb29cbb9b2d906ad3e3
7
- data.tar.gz: 28e82e5dc12f33c6e8af8b546338636ef2f6cda3273a2957bf7f86dcb4bb4a6074a4e2376c1c0ed0600f19e98b2783661710c8882b066eb05d3d9c1aa359aea8
6
+ metadata.gz: 34876d1314f36317f6fd2392e5489327e00f56df5b62cfcce0f27432469026957dfbf328459dbb1c84a9e1511e5c989f9ca556988ecefcf5f05d60df65ad2e56
7
+ data.tar.gz: '09fe11dd318422c430a941be38194bdeff80ff550a9fbce78076cad789d70076c698a2e409d03c79ea85aa61a3c4d2f2b03c00b28192facb6ec90e78b5dac222'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.2] - 2026-03-21
4
+
5
+ - `TimeOfDay#-` accepts `TimeOfDay` to return difference in seconds
6
+
7
+ ## [0.2.1] - 2026-03-21
8
+
9
+ - Add `TimeOfDay` arithmetic (`+`, `-`), `advance`, `change`, `to_seconds`, `.from_seconds`
10
+ - Add `YearMonth#advance`, `YearMonth#change`, `YearMonth#days`
11
+ - Add `MonthDay#change`
12
+ - Add `#as_json` to all value classes
13
+ - Add `DateValues.config.month_day_order` for locale-dependent `MonthDay.parse`
14
+
3
15
  ## [0.2.0] - 2026-03-20
4
16
 
5
17
  - **Breaking**: Rails integration extracted to [date_values-rails](https://github.com/ursm/date_values-rails). `require 'date_values/rails'` now requires installing `date_values-rails` gem.
data/README.md CHANGED
@@ -21,17 +21,21 @@ include DateValues
21
21
 
22
22
  ```ruby
23
23
  ym = YearMonth.new(2026, 3)
24
- ym.to_s # => "2026-03"
25
- ym.to_date # => #<Date: 2026-03-01>
24
+ ym.to_s # => "2026-03"
25
+ ym.to_date # => #<Date: 2026-03-01>
26
+ ym.days # => 31
26
27
 
27
- YearMonth.from(Date.today) # => #<DateValues::YearMonth 2026-03>
28
- YearMonth.parse('2026-03') # => #<DateValues::YearMonth 2026-03>
29
- YearMonth.parse('2026/3') # also works
28
+ YearMonth.from(Date.today) # => #<DateValues::YearMonth 2026-03>
29
+ YearMonth.parse('2026-03') # => #<DateValues::YearMonth 2026-03>
30
+ YearMonth.parse('2026/3') # also works
30
31
 
31
- ym + 1 # => #<DateValues::YearMonth 2026-04>
32
- ym - 1 # => #<DateValues::YearMonth 2026-02>
32
+ ym + 1 # => #<DateValues::YearMonth 2026-04>
33
+ ym - 1 # => #<DateValues::YearMonth 2026-02>
33
34
  YearMonth.new(2026, 3) - YearMonth.new(2025, 1) # => 14
34
35
 
36
+ ym.advance(years: 1, months: 2) # => #<DateValues::YearMonth 2027-05>
37
+ ym.change(year: 2025) # => #<DateValues::YearMonth 2025-03>
38
+
35
39
  # Range support
36
40
  (YearMonth.new(2026, 1)..YearMonth.new(2026, 3)).to_a
37
41
  # => [#<DateValues::YearMonth 2026-01>, #<DateValues::YearMonth 2026-02>, #<DateValues::YearMonth 2026-03>]
@@ -43,12 +47,14 @@ String representation uses ISO 8601 `--MM-DD` format (year omitted):
43
47
 
44
48
  ```ruby
45
49
  md = MonthDay.new(3, 19)
46
- md.to_s # => "--03-19"
47
- md.to_date(2026) # => #<Date: 2026-03-19>
50
+ md.to_s # => "--03-19"
51
+ md.to_date(2026) # => #<Date: 2026-03-19>
52
+
53
+ MonthDay.from(Date.today) # => #<DateValues::MonthDay --03-20>
54
+ MonthDay.parse('--03-19') # => #<DateValues::MonthDay --03-19>
55
+ MonthDay.parse('3/19') # also works (month/day order by default)
48
56
 
49
- MonthDay.from(Date.today) # => #<DateValues::MonthDay --03-20>
50
- MonthDay.parse('--03-19') # => #<DateValues::MonthDay --03-19>
51
- MonthDay.parse('3/19') # also works
57
+ md.change(month: 12) # => #<DateValues::MonthDay --12-19>
52
58
 
53
59
  # Range membership
54
60
  summer = MonthDay.new(6, 1)..MonthDay.new(8, 31)
@@ -59,12 +65,23 @@ summer.cover?(MonthDay.new(7, 15)) # => true
59
65
 
60
66
  ```ruby
61
67
  tod = TimeOfDay.new(14, 30)
62
- tod.to_s # => "14:30"
68
+ tod.to_s # => "14:30"
69
+
70
+ TimeOfDay.new(14, 30, 45).to_s # => "14:30:45"
63
71
 
64
- TimeOfDay.new(14, 30, 45).to_s # => "14:30:45"
72
+ TimeOfDay.from(Time.now) # => #<DateValues::TimeOfDay 14:30>
73
+ TimeOfDay.parse('14:30') # => #<DateValues::TimeOfDay 14:30>
65
74
 
66
- TimeOfDay.from(Time.now) # => #<DateValues::TimeOfDay 14:30>
67
- TimeOfDay.parse('14:30') # => #<DateValues::TimeOfDay 14:30>
75
+ tod + 3600 # => #<DateValues::TimeOfDay 15:30>
76
+ tod - 1800 # => #<DateValues::TimeOfDay 14:00>
77
+ TimeOfDay.new(17, 0) - TimeOfDay.new(9, 0) # => 28800 (seconds)
78
+ tod.advance(hours: 2, minutes: 15) # => #<DateValues::TimeOfDay 16:45>
79
+ tod.change(minute: 0) # => #<DateValues::TimeOfDay 14:00>
80
+ tod.to_seconds # => 52200
81
+ TimeOfDay.from_seconds(52200) # => #<DateValues::TimeOfDay 14:30>
82
+
83
+ # Wraps at 24h boundaries
84
+ TimeOfDay.new(23, 30) + 3600 # => #<DateValues::TimeOfDay 00:30>
68
85
 
69
86
  # Range membership
70
87
  business_hours = TimeOfDay.new(9, 0)..TimeOfDay.new(17, 0)
@@ -92,9 +109,33 @@ in { hour: (9..17) }
92
109
  end
93
110
  ```
94
111
 
112
+ ### JSON
113
+
114
+ All classes implement `#as_json`, returning the same string as `#to_s`:
115
+
116
+ ```ruby
117
+ YearMonth.new(2026, 3).as_json # => "2026-03"
118
+ MonthDay.new(3, 19).as_json # => "--03-19"
119
+ TimeOfDay.new(14, 30).as_json # => "14:30"
120
+ ```
121
+
122
+ ## Configuration
123
+
124
+ ### MonthDay parse order
125
+
126
+ By default, `MonthDay.parse` interprets ambiguous formats like `"3/19"` as month/day. For day/month (European convention):
127
+
128
+ ```ruby
129
+ DateValues.config.month_day_order = :day_first
130
+
131
+ MonthDay.parse('19/3') # => #<DateValues::MonthDay --03-19>
132
+ ```
133
+
134
+ ISO 8601 format (`--MM-DD`) is always month/day regardless of this setting.
135
+
95
136
  ## Rails Integration
96
137
 
97
- See [date_values-rails](https://github.com/ursm/date_values-rails) for ActiveModel/ActiveRecord type casting, validation, and I18n support.
138
+ See [date_values-rails](https://github.com/ursm/date_values-rails) for ActiveModel/ActiveRecord type casting, validation, I18n, and ActiveJob support.
98
139
 
99
140
  ## License
100
141
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DateValues
4
+ class Configuration
5
+ attr_accessor :month_day_order
6
+
7
+ def initialize
8
+ @month_day_order = :month_first
9
+ end
10
+ end
11
+
12
+ def self.config
13
+ @config ||= Configuration.new
14
+ end
15
+
16
+ def self.configure
17
+ yield config
18
+ end
19
+ end
@@ -19,9 +19,18 @@ module DateValues
19
19
 
20
20
  def self.parse(str)
21
21
  case str
22
- when /\A--(\d{1,2})-(\d{1,2})\z/ then new($1.to_i, $2.to_i)
23
- when /\A(\d{1,2})[\/\-](\d{1,2})\z/ then new($1.to_i, $2.to_i)
24
- else raise ArgumentError, "invalid MonthDay: #{str}"
22
+ when /\A--(\d{1,2})-(\d{1,2})\z/
23
+ new($1.to_i, $2.to_i)
24
+ when /\A(\d{1,2})[\/\-](\d{1,2})\z/
25
+ a, b = $1.to_i, $2.to_i
26
+
27
+ if DateValues.config.month_day_order == :day_first
28
+ new(b, a)
29
+ else
30
+ new(a, b)
31
+ end
32
+ else
33
+ raise ArgumentError, "invalid MonthDay: #{str}"
25
34
  end
26
35
  end
27
36
 
@@ -31,6 +40,10 @@ module DateValues
31
40
  [month, day] <=> [other.month, other.day]
32
41
  end
33
42
 
43
+ def change(month: self.month, day: self.day)
44
+ self.class.new(month, day)
45
+ end
46
+
34
47
  def strftime(format)
35
48
  # 2000 is a leap year, so Feb 29 works
36
49
  Date.new(2000, month, day).strftime(format)
@@ -40,6 +53,10 @@ module DateValues
40
53
  Date.new(year, month, day)
41
54
  end
42
55
 
56
+ def as_json(*)
57
+ to_s
58
+ end
59
+
43
60
  def to_s
44
61
  format('--%02d-%02d', month, day)
45
62
  end
@@ -29,10 +29,45 @@ module DateValues
29
29
  [hour, minute, second] <=> [other.hour, other.minute, other.second]
30
30
  end
31
31
 
32
+ def +(seconds)
33
+ self.class.from_seconds((to_seconds + seconds) % 86_400)
34
+ end
35
+
36
+ def -(other)
37
+ case other
38
+ when Integer then self + (-other)
39
+ when TimeOfDay then to_seconds - other.to_seconds
40
+ else raise TypeError, "#{other.class} can't be coerced into Integer or TimeOfDay"
41
+ end
42
+ end
43
+
44
+ def advance(hours: 0, minutes: 0, seconds: 0)
45
+ self + (hours * 3600 + minutes * 60 + seconds)
46
+ end
47
+
48
+ def change(hour: self.hour, minute: self.minute, second: self.second)
49
+ self.class.new(hour, minute, second)
50
+ end
51
+
52
+ def to_seconds
53
+ hour * 3600 + minute * 60 + second
54
+ end
55
+
56
+ def self.from_seconds(total)
57
+ total = total % 86_400
58
+ h, rest = total.divmod(3600)
59
+ m, s = rest.divmod(60)
60
+ new(h, m, s)
61
+ end
62
+
32
63
  def strftime(format)
33
64
  Time.new(2000, 1, 1, hour, minute, second).strftime(format)
34
65
  end
35
66
 
67
+ def as_json(*)
68
+ to_s
69
+ end
70
+
36
71
  def to_s
37
72
  if second.zero?
38
73
  format('%02d:%02d', hour, minute)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DateValues
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.2'
5
5
  end
@@ -49,6 +49,18 @@ module DateValues
49
49
  self + 1
50
50
  end
51
51
 
52
+ def advance(years: 0, months: 0)
53
+ self + (years * 12 + months)
54
+ end
55
+
56
+ def change(year: self.year, month: self.month)
57
+ self.class.new(year, month)
58
+ end
59
+
60
+ def days
61
+ Date.new(year, month, -1).day
62
+ end
63
+
52
64
  def strftime(format)
53
65
  to_date.strftime(format)
54
66
  end
@@ -57,6 +69,10 @@ module DateValues
57
69
  Date.new(year, month, 1)
58
70
  end
59
71
 
72
+ def as_json(*)
73
+ to_s
74
+ end
75
+
60
76
  def to_s
61
77
  format('%04d-%02d', year, month)
62
78
  end
data/lib/date_values.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'date_values/version'
4
+ require_relative 'date_values/configuration'
4
5
  require_relative 'date_values/year_month'
5
6
  require_relative 'date_values/month_day'
6
7
  require_relative 'date_values/time_of_day'
data/sig/date_values.rbs CHANGED
@@ -1,6 +1,15 @@
1
1
  module DateValues
2
2
  VERSION: String
3
3
 
4
+ def self.config: () -> Configuration
5
+ def self.configure: () { (Configuration) -> void } -> void
6
+
7
+ class Configuration
8
+ attr_accessor month_day_order: :month_first | :day_first
9
+
10
+ def initialize: () -> void
11
+ end
12
+
4
13
  class YearMonth
5
14
  include Comparable
6
15
 
@@ -23,10 +32,18 @@ module DateValues
23
32
 
24
33
  def succ: () -> YearMonth
25
34
 
35
+ def advance: (?years: Integer, ?months: Integer) -> YearMonth
36
+
37
+ def change: (?year: Integer, ?month: Integer) -> YearMonth
38
+
39
+ def days: () -> Integer
40
+
26
41
  def strftime: (String format) -> String
27
42
 
28
43
  def to_date: () -> Date
29
44
 
45
+ def as_json: (*untyped) -> String
46
+
30
47
  def to_s: () -> String
31
48
 
32
49
  def inspect: () -> String
@@ -47,10 +64,14 @@ module DateValues
47
64
  def <=>: (MonthDay other) -> Integer
48
65
  | (untyped other) -> nil
49
66
 
67
+ def change: (?month: Integer, ?day: Integer) -> MonthDay
68
+
50
69
  def strftime: (String format) -> String
51
70
 
52
71
  def to_date: (Integer year) -> Date
53
72
 
73
+ def as_json: (*untyped) -> String
74
+
54
75
  def to_s: () -> String
55
76
 
56
77
  def inspect: () -> String
@@ -71,13 +92,28 @@ module DateValues
71
92
 
72
93
  def self.from: (Time time) -> TimeOfDay
73
94
 
95
+ def self.from_seconds: (Integer total) -> TimeOfDay
96
+
74
97
  def self.parse: (String str) -> TimeOfDay
75
98
 
76
99
  def <=>: (TimeOfDay other) -> Integer
77
100
  | (untyped other) -> nil
78
101
 
102
+ def +: (Integer seconds) -> TimeOfDay
103
+
104
+ def -: (Integer seconds) -> TimeOfDay
105
+ | (TimeOfDay other) -> Integer
106
+
107
+ def advance: (?hours: Integer, ?minutes: Integer, ?seconds: Integer) -> TimeOfDay
108
+
109
+ def change: (?hour: Integer, ?minute: Integer, ?second: Integer) -> TimeOfDay
110
+
111
+ def to_seconds: () -> Integer
112
+
79
113
  def strftime: (String format) -> String
80
114
 
115
+ def as_json: (*untyped) -> String
116
+
81
117
  def to_s: () -> String
82
118
 
83
119
  def inspect: () -> String
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: date_values
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keita Urashima
@@ -20,6 +20,7 @@ files:
20
20
  - README.md
21
21
  - Rakefile
22
22
  - lib/date_values.rb
23
+ - lib/date_values/configuration.rb
23
24
  - lib/date_values/month_day.rb
24
25
  - lib/date_values/time_of_day.rb
25
26
  - lib/date_values/version.rb