time_cursor 1.0.0
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 +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.adoc +143 -0
- data/README.ja.adoc +143 -0
- data/Rakefile +96 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/time_cursor.rb +11 -0
- data/lib/time_cursor/base.rb +103 -0
- data/lib/time_cursor/elements.rb +111 -0
- data/lib/time_cursor/matcher.rb +356 -0
- data/lib/time_cursor/version.rb +4 -0
- data/time_cursor.gemspec +23 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 13d6fc3914b795f5587cc827bd66beb87ed3784a6425c6567cb3b38225a0f6ec
|
4
|
+
data.tar.gz: 8383cd520881f529af5009de0b9899ed7843d038f6e9341bbd356b4d8e38139c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 070fb20e91c91a491d49ac8b564b52b4e78a503810a4c52acfa289d2c9ec511ff4c2ffa593c220365ddb4e105ab7a2ac499e7ef60759de5cd3dcda14234b07f3
|
7
|
+
data.tar.gz: 7f6ed476e9cd62dc4aac181050f94729d6b607c585a99258a4e216276ccd6d6385aeaa1656c60b333be4a2da95d36a890c73dad1a3fc18c9e1456a8c865f7e29
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
*.bundle
|
11
|
+
*.so
|
12
|
+
*.o
|
13
|
+
*.a
|
14
|
+
mkmf.log
|
15
|
+
/vendor/
|
16
|
+
*.pid
|
17
|
+
*.log
|
18
|
+
*.log.*
|
19
|
+
/var/
|
20
|
+
/log/
|
21
|
+
*.swp
|
22
|
+
/.rspec_status
|
23
|
+
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.adoc
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
= TimeCursor
|
2
|
+
|
3
|
+
TimeCursor is a library to get the next or previous date time along the rules that are given in a crontab-like format.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
[source,ruby]
|
10
|
+
----
|
11
|
+
gem 'time_cursor'
|
12
|
+
----
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle install
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install time_cursor
|
21
|
+
or
|
22
|
+
$ gem install -l time_cursor-x.x.x.gem
|
23
|
+
|
24
|
+
== Usage
|
25
|
+
|
26
|
+
=== Initialize matcher
|
27
|
+
|
28
|
+
[source,ruby]
|
29
|
+
----
|
30
|
+
time_cursor = TimeCursor.new( at: '2015-02-26 01:23' )
|
31
|
+
time_cursor = TimeCursor.new( year: 2015, month: 2, day: 26, hour: 1, min: 23 )
|
32
|
+
|
33
|
+
time_cursor = TimeCursor.new( cron: '0 9,17 * * mon-fri' )
|
34
|
+
time_cursor = TimeCursor.new( wday: 'mon-fri', hour: [9,17] )
|
35
|
+
|
36
|
+
time_cursor = TimeCursor.new( cron: '0 12 1-7 * sun' )
|
37
|
+
time_cursor = TimeCursor.new( day: 1..7, wday: 'sun', hour: 12 )
|
38
|
+
|
39
|
+
time_cursor = TimeCursor.new( sec: '*/10' )
|
40
|
+
----
|
41
|
+
|
42
|
+
=== Get next time
|
43
|
+
|
44
|
+
[source,ruby]
|
45
|
+
----
|
46
|
+
time_cursor = TimeCursor.new( cron: '* * * * *' ) = => =<TimeCursor::Matcher>
|
47
|
+
time = time_cursor.next( '2015-02-26 01:23' ) = => 2015-02-26 01:24:00
|
48
|
+
time = time_cursor.next( time ) = => 2015-02-26 01:25:00
|
49
|
+
----
|
50
|
+
|
51
|
+
=== Get previous time
|
52
|
+
|
53
|
+
[source,ruby]
|
54
|
+
----
|
55
|
+
time_cursor = TimeCursor.new( hour: '*/3' ) = => =<TimeCursor::Matcher>
|
56
|
+
time = time_cursor.prev( '2015-02-26 01:23' ) = => 2015-02-26 00:00:00
|
57
|
+
time = time_cursor.prev( time ) = => 2015-02-25 21:00:00
|
58
|
+
----
|
59
|
+
|
60
|
+
=== Check to match
|
61
|
+
|
62
|
+
[source,ruby]
|
63
|
+
----
|
64
|
+
time_cursor = TimeCursor.new( day: 26, hour: 12 ) = => =<TimeCursor::Matcher>
|
65
|
+
time = time_cursor.match( '2015-02-26 12:00' ) = => 2015-02-26 12:00:00
|
66
|
+
time = time_cursor.match( '2015-02-26 00:00' ) = => nil
|
67
|
+
----
|
68
|
+
|
69
|
+
== Reference
|
70
|
+
|
71
|
+
=== Create a new TimeCursor with conditions.
|
72
|
+
|
73
|
+
[source,ruby]
|
74
|
+
----
|
75
|
+
TimeCursor.new( at: nil, cron: nil, year: nil, month: nil, day: nil, wday: nil, hour: nil, min: nil, sec: 0, msec: nil )
|
76
|
+
----
|
77
|
+
|
78
|
+
* Result:
|
79
|
+
** TimeCursor::Matcher object.
|
80
|
+
|
81
|
+
* Parameter:
|
82
|
+
** at: time. Time or String object. (default: nil)
|
83
|
+
** cron: set of min, hour, day, month, wday pattern. (default: nil)
|
84
|
+
** year: year. unlimited range is denied. (default: nil)
|
85
|
+
** month: month. 1..12, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec. (default: nil)
|
86
|
+
** day: day of month. 1..31. (default: nil)
|
87
|
+
** wday: day of week. 0..7, sun, mon, tue, wed, thr, fri, sat. (default: nil)
|
88
|
+
** hour: minute. 0..23. (default: nil)
|
89
|
+
** min: minute. 0..59. (default: nil)
|
90
|
+
** sec: second. 0..59. (default: 0)
|
91
|
+
** msec: millisecond. 0..999. (default: nil), If msec is assigned, then other parameters are ignored.
|
92
|
+
In detail, it can use "*" as wildcard.
|
93
|
+
|
94
|
+
=== Get next time
|
95
|
+
|
96
|
+
[source,ruby]
|
97
|
+
----
|
98
|
+
TimeCursor::Matcher#next( time = Time.now )
|
99
|
+
----
|
100
|
+
|
101
|
+
* Result:
|
102
|
+
** Next Time object or nil.
|
103
|
+
|
104
|
+
* Parameter:
|
105
|
+
** time: base time. Time or String object. (default: Time.now)
|
106
|
+
|
107
|
+
=== Get previous time
|
108
|
+
|
109
|
+
[source,ruby]
|
110
|
+
----
|
111
|
+
TimeCursor::Matcher#prev( time = Time.now )
|
112
|
+
----
|
113
|
+
|
114
|
+
* Result:
|
115
|
+
** Previous Time object or nil.
|
116
|
+
|
117
|
+
* Parameter:
|
118
|
+
** time: base time. Time or String object. (default: Time.now)
|
119
|
+
|
120
|
+
=== Check to match
|
121
|
+
|
122
|
+
[source,ruby]
|
123
|
+
----
|
124
|
+
TimeCursor::Matcher#match( time )
|
125
|
+
----
|
126
|
+
|
127
|
+
* Result:
|
128
|
+
** Time object or nil.
|
129
|
+
|
130
|
+
* Parameter:
|
131
|
+
** time: Time or String object for matching.
|
132
|
+
|
133
|
+
== Caution
|
134
|
+
|
135
|
+
Because it is calculated in local time, it does not work as expected when switching to daylight saving time.
|
136
|
+
|
137
|
+
== Contributing
|
138
|
+
|
139
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/arimay/time_cursor.
|
140
|
+
|
141
|
+
== License
|
142
|
+
|
143
|
+
The gem is available as open source under the terms of the http://opensource.org/licenses/MIT[MIT License].
|
data/README.ja.adoc
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
= TimeCursor
|
2
|
+
|
3
|
+
TimeCursor は crontab のような書式で与えた規則で, 次または前の日時を得るためのライブラリ.
|
4
|
+
|
5
|
+
== 導入
|
6
|
+
|
7
|
+
アプリの Gemfile にこの行を追加
|
8
|
+
|
9
|
+
[source,ruby]
|
10
|
+
----
|
11
|
+
gem 'time_cursor'
|
12
|
+
----
|
13
|
+
|
14
|
+
それから実行
|
15
|
+
|
16
|
+
$ bundle install
|
17
|
+
|
18
|
+
または次のように手動で導入
|
19
|
+
|
20
|
+
$ gem install time_cursor
|
21
|
+
or
|
22
|
+
$ gem install -l time_cursor-x.x.x.gem
|
23
|
+
|
24
|
+
== 使い方
|
25
|
+
|
26
|
+
=== マッチャを初期化
|
27
|
+
|
28
|
+
[source,ruby]
|
29
|
+
----
|
30
|
+
time_cursor = TimeCursor.new( at: '2015-02-26 01:23' )
|
31
|
+
time_cursor = TimeCursor.new( year: 2015, month: 2, day: 26, hour: 1, min: 23 )
|
32
|
+
|
33
|
+
time_cursor = TimeCursor.new( cron: '0 9,17 * * mon-fri' )
|
34
|
+
time_cursor = TimeCursor.new( wday: 'mon-fri', hour: [9,17] )
|
35
|
+
|
36
|
+
time_cursor = TimeCursor.new( cron: '0 12 1-7 * sun' )
|
37
|
+
time_cursor = TimeCursor.new( day: 1..7, wday: 'sun', hour: 12 )
|
38
|
+
|
39
|
+
time_cursor = TimeCursor.new( sec: '*/10' )
|
40
|
+
----
|
41
|
+
|
42
|
+
=== 次の日時を得る
|
43
|
+
|
44
|
+
[source,ruby]
|
45
|
+
----
|
46
|
+
time_cursor = TimeCursor.new( cron: '* * * * *' ) = => =<TimeCursor::Matcher>
|
47
|
+
time = time_cursor.next( '2015-02-26 01:23' ) = => 2015-02-26 01:24:00
|
48
|
+
time = time_cursor.next( time ) = => 2015-02-26 01:25:00
|
49
|
+
----
|
50
|
+
|
51
|
+
=== 前の日時を得る
|
52
|
+
|
53
|
+
[source,ruby]
|
54
|
+
----
|
55
|
+
time_cursor = TimeCursor.new( hour: '*/3' ) = => =<TimeCursor::Matcher>
|
56
|
+
time = time_cursor.prev( '2015-02-26 01:23' ) = => 2015-02-26 00:00:00
|
57
|
+
time = time_cursor.prev( time ) = => 2015-02-25 21:00:00
|
58
|
+
----
|
59
|
+
|
60
|
+
=== マッチするか調べる
|
61
|
+
|
62
|
+
[source,ruby]
|
63
|
+
----
|
64
|
+
time_cursor = TimeCursor.new( day: 26, hour: 12 ) = => =<TimeCursor::Matcher>
|
65
|
+
time = time_cursor.match( '2015-02-26 12:00' ) = => 2015-02-26 12:00:00
|
66
|
+
time = time_cursor.match( '2015-02-26 00:00' ) = => nil
|
67
|
+
----
|
68
|
+
|
69
|
+
== リファレンス
|
70
|
+
|
71
|
+
=== 条件を指定して、新たな TimeCursor を作成する.
|
72
|
+
|
73
|
+
[source,ruby]
|
74
|
+
----
|
75
|
+
TimeCursor.new( at: nil, cron: nil, year: nil, month: nil, day: nil, wday: nil, hour: nil, min: nil, sec: 0, msec: nil )
|
76
|
+
----
|
77
|
+
|
78
|
+
* Result:
|
79
|
+
** TimeCursor::Matcher オブジェクト.
|
80
|
+
|
81
|
+
* Parameter:
|
82
|
+
** at: 日時. Time または String オブジェクト. (default: nil)
|
83
|
+
** cron: 分、時、日、月、曜パターンのセット. (default: nil)
|
84
|
+
** year: 年. 範囲制限なしは拒否される. (default: nil)
|
85
|
+
** month: 月. 1..12, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec. (default: nil)
|
86
|
+
** day: 日. 1..31. (default: nil)
|
87
|
+
** wday: 曜. 0..7, sun, mon, tue, wed, thr, fri, sat. (default: nil)
|
88
|
+
** hour: 時. 0..23. (default: nil)
|
89
|
+
** min: 分. 0..59. (default: nil)
|
90
|
+
** sec: 秒. 0..59. (default: 0)
|
91
|
+
** msec: ミリ秒. 0..999. (default: nil), ミリ秒が指定されたとき, 他のパラメータは無視される.
|
92
|
+
詳細では, ワイルドカードとして "*" を使用できる.
|
93
|
+
|
94
|
+
=== 次の日時を得る.
|
95
|
+
|
96
|
+
[source,ruby]
|
97
|
+
----
|
98
|
+
TimeCursor::Matcher#next( time = Time.now )
|
99
|
+
----
|
100
|
+
|
101
|
+
* Result:
|
102
|
+
** 次の Time オブジェクト または nil.
|
103
|
+
|
104
|
+
* Parameter:
|
105
|
+
** time: 基準日時. Time または String オブジェクト. (default: Time.now)
|
106
|
+
|
107
|
+
=== 前の日時を得る.
|
108
|
+
|
109
|
+
[source,ruby]
|
110
|
+
----
|
111
|
+
TimeCursor::Matcher#prev( time = Time.now )
|
112
|
+
----
|
113
|
+
|
114
|
+
* Result:
|
115
|
+
** 前の Time オブジェクト または nil.
|
116
|
+
|
117
|
+
* Parameter:
|
118
|
+
** time: 基準日時. Time または String オブジェクト. (default: Time.now)
|
119
|
+
|
120
|
+
=== 日時が条件に適合するか調べる.
|
121
|
+
|
122
|
+
[source,ruby]
|
123
|
+
----
|
124
|
+
TimeCursor::Matcher#match( time )
|
125
|
+
----
|
126
|
+
|
127
|
+
* Result:
|
128
|
+
** Time object or nil.
|
129
|
+
|
130
|
+
* Parameter:
|
131
|
+
** time: 適合検査のための Time または String オブジェクト.
|
132
|
+
|
133
|
+
== 注意
|
134
|
+
|
135
|
+
地域時刻で計算しているため、夏時間の切り替わりにおいて期待しない挙動となる.
|
136
|
+
|
137
|
+
== 貢献
|
138
|
+
|
139
|
+
不具合報告とプルリクエストは GitHub https://github.com/arimay/time_cursor まで.
|
140
|
+
|
141
|
+
== ライセンス
|
142
|
+
|
143
|
+
この Gem は、 http://opensource.org/licenses/MIT[MITライセンス] の条件に基づいてオープンソースとして入手できる.
|
data/Rakefile
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require "bundler/gem_helper"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
class Bundler::GemHelper
|
10
|
+
|
11
|
+
def git_archive( dir = "../#{Time.now.strftime("%Y%m%d")}" )
|
12
|
+
FileUtils.mkdir_p dir
|
13
|
+
dest_path = File.join(dir, "#{name}-#{version}.zip")
|
14
|
+
cmnd = "git archive --format zip --prefix=#{name}/ HEAD > #{dest_path}"
|
15
|
+
|
16
|
+
out, code = sh_with_status( cmnd )
|
17
|
+
raise "Couldn't archive gem," unless code == 0
|
18
|
+
|
19
|
+
Bundler.ui.confirm "#{name} #{version} archived to #{dest_path}."
|
20
|
+
end
|
21
|
+
|
22
|
+
def git_push
|
23
|
+
ver = version.to_s
|
24
|
+
|
25
|
+
cmnd = "git push origin #{ver} "
|
26
|
+
out, code = sh_with_status( cmnd )
|
27
|
+
raise "Couldn't git push origin." unless code == 0
|
28
|
+
|
29
|
+
cmnd = "git push "
|
30
|
+
out, code = sh_with_status( cmnd )
|
31
|
+
raise "Couldn't git push." unless code == 0
|
32
|
+
|
33
|
+
Bundler.ui.confirm "Git Push #{ver}."
|
34
|
+
end
|
35
|
+
|
36
|
+
def update_version( new_version )
|
37
|
+
version_filename = %x[ find . -type f -name "version.rb" | grep -v vendor | head -1 ].chomp
|
38
|
+
version_pathname = File.expand_path( version_filename )
|
39
|
+
lines = File.open( version_pathname ).read
|
40
|
+
lines = lines.gsub( /VERSION\s*=\s*\"\d+\.\d+\.\d+\"/, "VERSION = \"#{new_version}\"" )
|
41
|
+
File.open( version_pathname, "w" ) do |file|
|
42
|
+
file.write( lines )
|
43
|
+
end
|
44
|
+
|
45
|
+
cmnd = "git add #{version_pathname} "
|
46
|
+
out, code = sh_with_status( cmnd )
|
47
|
+
raise "Couldn't git add," unless code == 0
|
48
|
+
|
49
|
+
cmnd = "git commit -m '#{new_version}' "
|
50
|
+
out, code = sh_with_status( cmnd )
|
51
|
+
raise "Couldn't git commit." unless code == 0
|
52
|
+
|
53
|
+
cmnd = "git tag #{new_version} "
|
54
|
+
out, code = sh_with_status( cmnd )
|
55
|
+
raise "Couldn't git tag." unless code == 0
|
56
|
+
|
57
|
+
Bundler.ui.confirm "Update Tags to #{new_version}."
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
Bundler::GemHelper.new(Dir.pwd).instance_eval do
|
63
|
+
|
64
|
+
desc "Archive #{name}-#{version}.zip from repository"
|
65
|
+
task 'zip' do
|
66
|
+
git_archive
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "Git Push"
|
70
|
+
task 'push' do
|
71
|
+
git_push
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "Update Version Tiny"
|
75
|
+
task 'tiny' do
|
76
|
+
major, minor, tiny = version.to_s.split('.')
|
77
|
+
new_version = [major, minor, tiny.to_i + 1].join('.')
|
78
|
+
update_version( new_version )
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "Update Version Minor"
|
82
|
+
task 'minor' do
|
83
|
+
major, minor, tiny = version.to_s.split('.')
|
84
|
+
new_version = [major, minor.to_i + 1, 0].join('.')
|
85
|
+
update_version( new_version )
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Update Version Major"
|
89
|
+
task 'major' do
|
90
|
+
major, minor, tiny = version.to_s.split('.')
|
91
|
+
new_version = [major.to_i + 1, 0, 0].join('.')
|
92
|
+
update_version( new_version )
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "time_cursor"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/time_cursor.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require "time"
|
2
|
+
require "date"
|
3
|
+
|
4
|
+
module TimeCursor
|
5
|
+
|
6
|
+
module Base
|
7
|
+
|
8
|
+
def new( at: nil, cron: nil, year: nil, month: nil, day: nil, wday: nil, hour: nil, min: nil, sec: 0, msec: nil )
|
9
|
+
if !at.nil?
|
10
|
+
at( at )
|
11
|
+
elsif !cron.nil?
|
12
|
+
cron( cron )
|
13
|
+
else
|
14
|
+
detail( year: year, month: month, day: day, wday: wday, hour: hour, min: min, sec: sec, msec: msec )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def at( time )
|
19
|
+
case time
|
20
|
+
when String
|
21
|
+
date = Date.parse( time ) rescue nil
|
22
|
+
time = Time.parse( time )
|
23
|
+
when Time
|
24
|
+
date = time.to_date
|
25
|
+
end
|
26
|
+
|
27
|
+
case time
|
28
|
+
when Date
|
29
|
+
Matcher.new(
|
30
|
+
year: time.year,
|
31
|
+
month: time.month,
|
32
|
+
day: time.day,
|
33
|
+
hour: 0,
|
34
|
+
min: 0,
|
35
|
+
sec: 0,
|
36
|
+
)
|
37
|
+
when Time
|
38
|
+
if date.nil?
|
39
|
+
Matcher.new(
|
40
|
+
hour: time.hour,
|
41
|
+
min: time.min,
|
42
|
+
sec: time.sec,
|
43
|
+
)
|
44
|
+
else
|
45
|
+
Matcher.new(
|
46
|
+
year: time.year,
|
47
|
+
month: time.month,
|
48
|
+
day: time.day,
|
49
|
+
hour: time.hour,
|
50
|
+
min: time.min,
|
51
|
+
sec: time.sec,
|
52
|
+
)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
raise ArgumentError, "invalid class : '#{datetime}'"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cron( patterns )
|
60
|
+
case patterns
|
61
|
+
when String
|
62
|
+
else
|
63
|
+
raise ArgumentError, "invalid class : '#{patterns}'"
|
64
|
+
end
|
65
|
+
a = patterns.split("\s")
|
66
|
+
unless a.size == 5
|
67
|
+
raise ArgumentError, "too/few count of fields : '#{patterns}'"
|
68
|
+
end
|
69
|
+
Matcher.new(
|
70
|
+
month: a[3],
|
71
|
+
day: a[2],
|
72
|
+
wday: a[4],
|
73
|
+
hour: a[1],
|
74
|
+
min: a[0],
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def detail( year: nil, month: nil, day: nil, wday: nil, hour: nil, min: nil, sec: 0, msec: nil )
|
79
|
+
if msec.nil?
|
80
|
+
month ||= 1 unless year.nil?
|
81
|
+
day ||= 1 unless month.nil?
|
82
|
+
hour ||= 0 unless ( day.nil? && wday.nil? )
|
83
|
+
min ||= 0 unless hour.nil?
|
84
|
+
Matcher.new(
|
85
|
+
year: year,
|
86
|
+
month: month,
|
87
|
+
day: day,
|
88
|
+
wday: wday,
|
89
|
+
hour: hour,
|
90
|
+
min: min,
|
91
|
+
sec: sec,
|
92
|
+
)
|
93
|
+
else
|
94
|
+
Matcher.new(
|
95
|
+
msec: msec,
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
|
2
|
+
module TimeCursor
|
3
|
+
|
4
|
+
class Elements < Array
|
5
|
+
|
6
|
+
DATE_ALIAS = {
|
7
|
+
'sun'=>'0', 'mon'=>'1', 'tue'=>'2', 'wed'=>'3', 'thr'=>'4', 'fri'=>'5', 'sat'=>'6',
|
8
|
+
'jan'=>'1', 'feb'=>'2', 'mar'=>'3', 'apr'=>'4', 'may'=>'5', 'jun'=>'6',
|
9
|
+
'jul'=>'7', 'aug'=>'8', 'sep'=>'9', 'oct'=>'10', 'nov'=>'11', 'dec'=>'12',
|
10
|
+
}
|
11
|
+
RE_ALL = %r|\A\*(/1)?\Z|
|
12
|
+
RE_WILD = %r|\A\*(/(\d+))?\Z|
|
13
|
+
RE_LIST = %r|\A(\d+)(-(\d+)(/(\d+))?)?\Z|
|
14
|
+
|
15
|
+
def self.build( target, range = nil )
|
16
|
+
case target
|
17
|
+
when Array
|
18
|
+
target = target.join(',')
|
19
|
+
|
20
|
+
when Symbol
|
21
|
+
target = target.to_s
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
case target
|
26
|
+
when NilClass
|
27
|
+
Elements.new( [] )
|
28
|
+
|
29
|
+
when Range
|
30
|
+
Elements.new( target.to_a )
|
31
|
+
|
32
|
+
when Numeric
|
33
|
+
Elements.new( [target] )
|
34
|
+
|
35
|
+
when String
|
36
|
+
factors = DATE_ALIAS.inject( target.downcase ) do |s, (k, v)| s.gsub( k, v ) end
|
37
|
+
items = factors.split(',').map do |factor|
|
38
|
+
if m = RE_ALL.match( factor )
|
39
|
+
[]
|
40
|
+
elsif m = RE_WILD.match( factor )
|
41
|
+
if range.nil?
|
42
|
+
raise ArgumentError, "disallow wildcard without range : '#{target}'"
|
43
|
+
end
|
44
|
+
from = range.first
|
45
|
+
to = range.last
|
46
|
+
step = ( m[2] || 1 ).to_i
|
47
|
+
expand( from, to, step, range )
|
48
|
+
elsif m = RE_LIST.match( factor )
|
49
|
+
from = ( m[1] ).to_i
|
50
|
+
to = ( m[3] || m[1] ).to_i
|
51
|
+
step = ( m[5] || 1 ).to_i
|
52
|
+
expand( from, to, step, range )
|
53
|
+
else
|
54
|
+
raise ArgumentError, "format invalid : '#{target}'"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
Elements.new( items.flatten.sort.uniq )
|
58
|
+
|
59
|
+
else
|
60
|
+
raise ArgumentError, "type invalid : '#{target}'"
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.expand( from, to, step, range )
|
66
|
+
result = []
|
67
|
+
cur = from
|
68
|
+
if from <= to
|
69
|
+
loop do
|
70
|
+
result << cur
|
71
|
+
cur += step
|
72
|
+
break if cur > to
|
73
|
+
end
|
74
|
+
else
|
75
|
+
loop do
|
76
|
+
result << cur
|
77
|
+
cur += step
|
78
|
+
break if cur > range.last
|
79
|
+
end
|
80
|
+
cur = range.first
|
81
|
+
loop do
|
82
|
+
result << cur
|
83
|
+
cur += step
|
84
|
+
break if cur > to
|
85
|
+
end
|
86
|
+
end
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
def right( item )
|
91
|
+
0.upto(self.size-1) do |i|
|
92
|
+
return self[i] if item < self[i]
|
93
|
+
end
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def left( item )
|
98
|
+
(self.size-1).downto(0) do |i|
|
99
|
+
return self[i] if self[i] < item
|
100
|
+
end
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def correspond?( item )
|
105
|
+
self.empty? || self.include?( item )
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,356 @@
|
|
1
|
+
require "time"
|
2
|
+
require "date"
|
3
|
+
|
4
|
+
module TimeCursor
|
5
|
+
|
6
|
+
class Matcher
|
7
|
+
|
8
|
+
def initialize( year: '*', month: '*', day: '*', wday: '*', hour: '*', min: '*', sec: 0, msec: nil )
|
9
|
+
if msec.nil?
|
10
|
+
@years = Elements.build( year )
|
11
|
+
@months = Elements.build( month, 1..12 )
|
12
|
+
@days = Elements.build( day, 1..31 )
|
13
|
+
@wdays = Elements.build( wday, 0..6 )
|
14
|
+
@hours = Elements.build( hour, 0..23 )
|
15
|
+
@mins = Elements.build( min, 0..59 )
|
16
|
+
@secs = Elements.build( sec, 0..59 )
|
17
|
+
@usecs = Elements.new
|
18
|
+
|
19
|
+
if !@years.empty? || !@months.empty? || !@days.empty? || !@wdays.empty?
|
20
|
+
@next_for = :next_for_date
|
21
|
+
@prev_for = :prev_for_date
|
22
|
+
|
23
|
+
elsif !@hours.empty? || !@mins.empty?
|
24
|
+
@next_for = :next_for_time
|
25
|
+
@prev_for = :prev_for_time
|
26
|
+
|
27
|
+
else
|
28
|
+
@secs = Elements.build( '0-59', 0..59 ) if @secs.empty?
|
29
|
+
@next_for = :next_for_sec
|
30
|
+
@prev_for = :prev_for_sec
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
else
|
35
|
+
@years = Elements.new
|
36
|
+
@months = Elements.new
|
37
|
+
@days = Elements.new
|
38
|
+
@wdays = Elements.new
|
39
|
+
@hours = Elements.new
|
40
|
+
@mins = Elements.new
|
41
|
+
@secs = Elements.new
|
42
|
+
@usecs = Elements.build( msec, 0..999 )
|
43
|
+
@usecs.each_with_index do |item, ndx|
|
44
|
+
@usecs[ndx] *= 1000
|
45
|
+
end
|
46
|
+
|
47
|
+
@next_for = :next_for_usec
|
48
|
+
@prev_for = :prev_for_usec
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def next( time = Time.now )
|
54
|
+
case time
|
55
|
+
when Time
|
56
|
+
when String
|
57
|
+
time = Time.parse( time )
|
58
|
+
else
|
59
|
+
raise ArgumentError, "can not parse as time."
|
60
|
+
end
|
61
|
+
send( @next_for, time )
|
62
|
+
end
|
63
|
+
|
64
|
+
def prev( time = Time.now )
|
65
|
+
case time
|
66
|
+
when Time
|
67
|
+
when String
|
68
|
+
time = Time.parse( time )
|
69
|
+
else
|
70
|
+
raise ArgumentError, "can not parse as time."
|
71
|
+
end
|
72
|
+
send( @prev_for, time )
|
73
|
+
end
|
74
|
+
|
75
|
+
def match( time = Time.now )
|
76
|
+
case time
|
77
|
+
when Time
|
78
|
+
when String
|
79
|
+
time = Time.parse( time )
|
80
|
+
else
|
81
|
+
raise ArgumentError, "can not parse as time."
|
82
|
+
end
|
83
|
+
|
84
|
+
if @years.correspond?( time.year ) &&
|
85
|
+
@months.correspond?( time.month ) &&
|
86
|
+
@days.correspond?( time.day ) &&
|
87
|
+
@hours.correspond?( time.hour ) &&
|
88
|
+
@mins.correspond?( time.min ) &&
|
89
|
+
@secs.correspond?( time.sec )
|
90
|
+
time
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# private
|
97
|
+
|
98
|
+
def next_for_date( time )
|
99
|
+
time += 1
|
100
|
+
loop do
|
101
|
+
if !@years.correspond?( year = time.year )
|
102
|
+
year = @years.right( year )
|
103
|
+
return time = nil if year.nil?
|
104
|
+
time = forward_to_year( time, year )
|
105
|
+
|
106
|
+
elsif !@months.correspond?( month = time.month )
|
107
|
+
month = @months.right( month ) || @months.first
|
108
|
+
time = forward_to_month( time, month )
|
109
|
+
|
110
|
+
elsif !@wdays.correspond?( wday = time.wday )
|
111
|
+
wday = @wdays.right( wday ) || @wdays.first
|
112
|
+
time = forward_to_wday( time, wday )
|
113
|
+
|
114
|
+
elsif !@days.correspond?( day = time.day )
|
115
|
+
day = @days.right( day ) || @days.first
|
116
|
+
time = forward_to_day( time, day )
|
117
|
+
|
118
|
+
elsif !@hours.correspond?( hour = time.hour )
|
119
|
+
hour = @hours.right( hour ) || @hours.first
|
120
|
+
time = forward_to_hour( time, hour )
|
121
|
+
|
122
|
+
elsif !@mins.correspond?( min = time.min )
|
123
|
+
min = @mins.right( min ) || @mins.first
|
124
|
+
time = forward_to_min( time, min )
|
125
|
+
|
126
|
+
elsif !@secs.correspond?( sec = time.sec )
|
127
|
+
sec = @secs.right( sec ) || @secs.first
|
128
|
+
time = forward_to_sec( time, sec )
|
129
|
+
|
130
|
+
else
|
131
|
+
return time
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def next_for_time( time )
|
138
|
+
time += 1
|
139
|
+
loop do
|
140
|
+
if !@hours.correspond?( hour = time.hour )
|
141
|
+
hour = @hours.right( hour ) || @hours.first
|
142
|
+
time = forward_to_hour( time, hour )
|
143
|
+
|
144
|
+
elsif !@mins.correspond?( min = time.min )
|
145
|
+
min = @mins.right( min ) || @mins.first
|
146
|
+
time = forward_to_min( time, min )
|
147
|
+
|
148
|
+
elsif !@secs.correspond?( sec = time.sec )
|
149
|
+
sec = @secs.right( sec ) || @secs.first
|
150
|
+
time = forward_to_sec( time, sec )
|
151
|
+
|
152
|
+
else
|
153
|
+
return time
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def next_for_sec( time )
|
160
|
+
sec = @secs.right( time.sec ) || @secs.first
|
161
|
+
time = forward_to_sec( time, sec )
|
162
|
+
end
|
163
|
+
|
164
|
+
def next_for_usec( time )
|
165
|
+
usec = @usecs.right( time.usec ) || @usecs.first
|
166
|
+
time = forward_to_usec( time, usec )
|
167
|
+
end
|
168
|
+
|
169
|
+
def prev_for_date( time )
|
170
|
+
time -= 1
|
171
|
+
loop do
|
172
|
+
if !@years.correspond?( year = time.year )
|
173
|
+
year = @years.left( year )
|
174
|
+
return time = nil if year.nil?
|
175
|
+
time = back_to_year( time, year )
|
176
|
+
|
177
|
+
elsif !@months.correspond?( month = time.month )
|
178
|
+
month = @months.left( month ) || @months.last
|
179
|
+
time = back_to_month( time, month )
|
180
|
+
|
181
|
+
elsif !@wdays.correspond?( wday = time.wday )
|
182
|
+
wday = @wdays.left( wday ) || @wdays.last
|
183
|
+
time = back_to_wday( time, wday )
|
184
|
+
|
185
|
+
elsif !@days.correspond?( day = time.day )
|
186
|
+
day = @days.left( day ) || @days.last
|
187
|
+
time = back_to_day( time, day )
|
188
|
+
|
189
|
+
elsif !@hours.correspond?( hour = time.hour )
|
190
|
+
hour = @hours.left( hour ) || @hours.last
|
191
|
+
time = back_to_hour( time, hour )
|
192
|
+
|
193
|
+
elsif !@mins.correspond?( min = time.min )
|
194
|
+
min = @mins.left( min ) || @mins.last
|
195
|
+
time = back_to_min( time, min )
|
196
|
+
|
197
|
+
elsif !@secs.correspond?( sec = time.sec )
|
198
|
+
sec = @secs.left( sec ) || @secs.last
|
199
|
+
time = back_to_sec( time, sec )
|
200
|
+
|
201
|
+
else
|
202
|
+
return time
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def prev_for_time( time )
|
209
|
+
time -= 1
|
210
|
+
loop do
|
211
|
+
if !@hours.correspond?( hour = time.hour )
|
212
|
+
hour = @hours.left( hour ) || @hours.last
|
213
|
+
time = back_to_hour( time, hour )
|
214
|
+
|
215
|
+
elsif !@mins.correspond?( min = time.min )
|
216
|
+
min = @mins.left( min ) || @mins.last
|
217
|
+
time = back_to_min( time, min )
|
218
|
+
|
219
|
+
elsif !@secs.correspond?( sec = time.sec )
|
220
|
+
sec = @secs.left( sec ) || @secs.last
|
221
|
+
time = back_to_sec( time, sec )
|
222
|
+
|
223
|
+
else
|
224
|
+
return time
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def prev_for_sec( time )
|
231
|
+
sec = @secs.left( time.sec ) || @secs.last
|
232
|
+
time = back_to_sec( time, sec )
|
233
|
+
end
|
234
|
+
|
235
|
+
def prev_for_usec( time )
|
236
|
+
usec = @usecs.left( time.usec ) || @usecs.last
|
237
|
+
time = back_to_usec( time, usec )
|
238
|
+
end
|
239
|
+
|
240
|
+
def forward_to_year( time, year )
|
241
|
+
if time.year < year
|
242
|
+
Time.new( year )
|
243
|
+
else
|
244
|
+
nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def forward_to_month( time, month )
|
249
|
+
date = Date.new( time.year, time.month, time.day )
|
250
|
+
loop do
|
251
|
+
date = date.next_month
|
252
|
+
break if date.month == month
|
253
|
+
end
|
254
|
+
Time.new( date.year, date.month )
|
255
|
+
end
|
256
|
+
|
257
|
+
def forward_to_day( time, day )
|
258
|
+
date = Date.new( time.year, time.month, time.day )
|
259
|
+
loop do
|
260
|
+
date = date.next_day
|
261
|
+
break if date.day == day
|
262
|
+
end
|
263
|
+
Time.new( date.year, date.month, date.day )
|
264
|
+
end
|
265
|
+
|
266
|
+
def forward_to_wday( time, wday )
|
267
|
+
date = Date.new( time.year, time.month, time.day )
|
268
|
+
loop do
|
269
|
+
date = date.next_day
|
270
|
+
break if date.wday == wday
|
271
|
+
end
|
272
|
+
Time.new( date.year, date.month, date.day )
|
273
|
+
end
|
274
|
+
|
275
|
+
def forward_to_hour( time, hour )
|
276
|
+
time += 60 * 60 * 24 if hour <= time.hour
|
277
|
+
Time.new( time.year, time.month, time.day, hour )
|
278
|
+
end
|
279
|
+
|
280
|
+
def forward_to_min( time, min )
|
281
|
+
time += 60 * 60 if min <= time.min
|
282
|
+
Time.new( time.year, time.month, time.day, time.hour, min )
|
283
|
+
end
|
284
|
+
|
285
|
+
def forward_to_sec( time, sec )
|
286
|
+
time += 60 if sec <= time.sec
|
287
|
+
Time.new( time.year, time.month, time.day, time.hour, time.min, sec )
|
288
|
+
end
|
289
|
+
|
290
|
+
def forward_to_usec( time, usec )
|
291
|
+
time += 1 if usec <= time.usec
|
292
|
+
Time.local( time.year, time.month, time.day, time.hour, time.min, time.sec, usec )
|
293
|
+
end
|
294
|
+
|
295
|
+
def back_to_year( time, year )
|
296
|
+
if year < time.year
|
297
|
+
Time.new( year + 1 ) - 1
|
298
|
+
else
|
299
|
+
nil
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def back_to_month( time, month )
|
304
|
+
date = Date.new( time.year, time.month, time.day )
|
305
|
+
loop do
|
306
|
+
date = date.prev_month
|
307
|
+
break if date.month == month
|
308
|
+
end
|
309
|
+
date = date.next_month
|
310
|
+
Time.new( date.year, date.month ) - 1
|
311
|
+
end
|
312
|
+
|
313
|
+
def back_to_day( time, day )
|
314
|
+
date = Date.new( time.year, time.month, time.day )
|
315
|
+
loop do
|
316
|
+
date = date.prev_day
|
317
|
+
break if date.day == day
|
318
|
+
end
|
319
|
+
date = date.next_day
|
320
|
+
Time.new( date.year, date.month, date.day ) - 1
|
321
|
+
end
|
322
|
+
|
323
|
+
def back_to_wday( time, wday )
|
324
|
+
date = Date.new( time.year, time.month, time.day )
|
325
|
+
loop do
|
326
|
+
date = date.prev_day
|
327
|
+
break if date.wday == wday
|
328
|
+
end
|
329
|
+
date = date.next_day
|
330
|
+
Time.new( date.year, date.month, date.day ) - 1
|
331
|
+
end
|
332
|
+
|
333
|
+
def back_to_hour( time, hour )
|
334
|
+
time -= 60 * 60 * 24 if time.hour <= hour
|
335
|
+
Time.new( time.year, time.month, time.day, hour, 59, 59 )
|
336
|
+
end
|
337
|
+
|
338
|
+
def back_to_min( time, min )
|
339
|
+
time -= 60 * 60 if time.min <= min
|
340
|
+
Time.new( time.year, time.month, time.day, time.hour, min, 59 )
|
341
|
+
end
|
342
|
+
|
343
|
+
def back_to_sec( time, sec )
|
344
|
+
time -= 60 if time.sec <= sec
|
345
|
+
Time.new( time.year, time.month, time.day, time.hour, time.min, sec )
|
346
|
+
end
|
347
|
+
|
348
|
+
def back_to_usec( time, usec )
|
349
|
+
time -= 1 if time.usec < usec
|
350
|
+
Time.local( time.year, time.month, time.day, time.hour, time.min, time.sec, usec )
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
data/time_cursor.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'time_cursor/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "time_cursor"
|
7
|
+
spec.version = TimeCursor::VERSION
|
8
|
+
spec.authors = ["arimay"]
|
9
|
+
spec.email = ["arima.yasuhiro@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{ Get the datetime for event schedule. }
|
12
|
+
spec.description = %q{ Get the next or previous datetime along the rules that are given in a crontab-like format or other options. }
|
13
|
+
spec.homepage = "https://github.com/arimay/time_cursor/"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: time_cursor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- arimay
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '12.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '12.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
description: " Get the next or previous datetime along the rules that are given in
|
42
|
+
a crontab-like format or other options. "
|
43
|
+
email:
|
44
|
+
- arima.yasuhiro@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- ".rspec"
|
51
|
+
- ".travis.yml"
|
52
|
+
- Gemfile
|
53
|
+
- README.adoc
|
54
|
+
- README.ja.adoc
|
55
|
+
- Rakefile
|
56
|
+
- bin/console
|
57
|
+
- bin/setup
|
58
|
+
- lib/time_cursor.rb
|
59
|
+
- lib/time_cursor/base.rb
|
60
|
+
- lib/time_cursor/elements.rb
|
61
|
+
- lib/time_cursor/matcher.rb
|
62
|
+
- lib/time_cursor/version.rb
|
63
|
+
- time_cursor.gemspec
|
64
|
+
homepage: https://github.com/arimay/time_cursor/
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubygems_version: 3.1.4
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Get the datetime for event schedule.
|
87
|
+
test_files: []
|