win32-taskscheduler 0.3.2 → 0.4.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 +4 -4
- data/CHANGELOG.md +122 -0
- data/Gemfile +3 -0
- data/README +2 -1
- data/RELEASE_NOTES.md +36 -0
- data/Rakefile +0 -1
- data/certs/djberg96_pub.pem +21 -21
- data/lib/win32/taskscheduler.rb +345 -404
- data/lib/win32/windows/constants.rb +260 -0
- data/lib/win32/windows/time_calc_helper.rb +130 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/win32/windows/time_calc_helper_spec.rb +392 -0
- data/win32-taskscheduler.gemspec +5 -4
- metadata +31 -29
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +0 -108
- metadata.gz.sig +0 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
module Windows
|
2
|
+
module TaskSchedulerConstants
|
3
|
+
|
4
|
+
SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze
|
5
|
+
|
6
|
+
# Triggers
|
7
|
+
|
8
|
+
# Trigger is set to run the task a single time
|
9
|
+
TASK_TIME_TRIGGER_ONCE = 1
|
10
|
+
|
11
|
+
# Trigger is set to run the task on a daily interval
|
12
|
+
TASK_TIME_TRIGGER_DAILY = 2
|
13
|
+
|
14
|
+
# Trigger is set to run the task on specific days of a specific week & month
|
15
|
+
TASK_TIME_TRIGGER_WEEKLY = 3
|
16
|
+
|
17
|
+
# Trigger is set to run the task on specific day(s) of the month
|
18
|
+
TASK_TIME_TRIGGER_MONTHLYDATE = 4
|
19
|
+
|
20
|
+
# Trigger is set to run the task on specific day(s) of the month
|
21
|
+
TASK_TIME_TRIGGER_MONTHLYDOW = 5
|
22
|
+
|
23
|
+
# Trigger is set to run the task if the system remains idle for the amount
|
24
|
+
# of time specified by the idle wait time of the task
|
25
|
+
TASK_EVENT_TRIGGER_ON_IDLE = 6
|
26
|
+
|
27
|
+
TASK_TRIGGER_REGISTRATION = 7
|
28
|
+
|
29
|
+
# Trigger is set to run the task at system startup
|
30
|
+
TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 8
|
31
|
+
|
32
|
+
# Trigger is set to run the task when a user logs on
|
33
|
+
TASK_EVENT_TRIGGER_AT_LOGON = 9
|
34
|
+
|
35
|
+
TASK_TRIGGER_SESSION_STATE_CHANGE = 11
|
36
|
+
|
37
|
+
# Daily Tasks
|
38
|
+
|
39
|
+
# The task will run on Sunday
|
40
|
+
TASK_SUNDAY = 0x1
|
41
|
+
|
42
|
+
# The task will run on Monday
|
43
|
+
TASK_MONDAY = 0x2
|
44
|
+
|
45
|
+
# The task will run on Tuesday
|
46
|
+
TASK_TUESDAY = 0x4
|
47
|
+
|
48
|
+
# The task will run on Wednesday
|
49
|
+
TASK_WEDNESDAY = 0x8
|
50
|
+
|
51
|
+
# The task will run on Thursday
|
52
|
+
TASK_THURSDAY = 0x10
|
53
|
+
|
54
|
+
# The task will run on Friday
|
55
|
+
TASK_FRIDAY = 0x20
|
56
|
+
|
57
|
+
# The task will run on Saturday
|
58
|
+
TASK_SATURDAY = 0x40
|
59
|
+
|
60
|
+
# Weekly tasks
|
61
|
+
|
62
|
+
# The task will run between the 1st and 7th day of the month
|
63
|
+
TASK_FIRST_WEEK = 0x01
|
64
|
+
|
65
|
+
# The task will run between the 8th and 14th day of the month
|
66
|
+
TASK_SECOND_WEEK = 0x02
|
67
|
+
|
68
|
+
# The task will run between the 15th and 21st day of the month
|
69
|
+
TASK_THIRD_WEEK = 0x04
|
70
|
+
|
71
|
+
# The task will run between the 22nd and 28th day of the month
|
72
|
+
TASK_FOURTH_WEEK = 0x08
|
73
|
+
|
74
|
+
# The task will run the last seven days of the month
|
75
|
+
TASK_LAST_WEEK = 0x10
|
76
|
+
|
77
|
+
# Monthly tasks
|
78
|
+
|
79
|
+
# The task will run in January
|
80
|
+
TASK_JANUARY = 0x1
|
81
|
+
|
82
|
+
# The task will run in February
|
83
|
+
TASK_FEBRUARY = 0x2
|
84
|
+
|
85
|
+
# The task will run in March
|
86
|
+
TASK_MARCH = 0x4
|
87
|
+
|
88
|
+
# The task will run in April
|
89
|
+
TASK_APRIL = 0x8
|
90
|
+
|
91
|
+
# The task will run in May
|
92
|
+
TASK_MAY = 0x10
|
93
|
+
|
94
|
+
# The task will run in June
|
95
|
+
TASK_JUNE = 0x20
|
96
|
+
|
97
|
+
# The task will run in July
|
98
|
+
TASK_JULY = 0x40
|
99
|
+
|
100
|
+
# The task will run in August
|
101
|
+
TASK_AUGUST = 0x80
|
102
|
+
|
103
|
+
# The task will run in September
|
104
|
+
TASK_SEPTEMBER = 0x100
|
105
|
+
|
106
|
+
# The task will run in October
|
107
|
+
TASK_OCTOBER = 0x200
|
108
|
+
|
109
|
+
# The task will run in November
|
110
|
+
TASK_NOVEMBER = 0x400
|
111
|
+
|
112
|
+
# The task will run in December
|
113
|
+
TASK_DECEMBER = 0x800
|
114
|
+
|
115
|
+
# Flags
|
116
|
+
|
117
|
+
# Used when converting AT service jobs into work items
|
118
|
+
TASK_FLAG_INTERACTIVE = 0x1
|
119
|
+
|
120
|
+
# The work item will be deleted when there are no more scheduled run times
|
121
|
+
TASK_FLAG_DELETE_WHEN_DONE = 0x2
|
122
|
+
|
123
|
+
# The work item is disabled. Useful for temporarily disabling a task
|
124
|
+
TASK_FLAG_DISABLED = 0x4
|
125
|
+
|
126
|
+
# The work item begins only if the computer is not in use at the scheduled
|
127
|
+
# start time
|
128
|
+
TASK_FLAG_START_ONLY_IF_IDLE = 0x10
|
129
|
+
|
130
|
+
# The work item terminates if the computer makes an idle to non-idle
|
131
|
+
# transition while the work item is running
|
132
|
+
TASK_FLAG_KILL_ON_IDLE_END = 0x20
|
133
|
+
|
134
|
+
# The work item does not start if the computer is running on battery power
|
135
|
+
TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
|
136
|
+
|
137
|
+
# The work item ends, and the associated application quits, if the computer
|
138
|
+
# switches to battery power
|
139
|
+
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
|
140
|
+
|
141
|
+
# The work item starts only if the computer is in a docking station
|
142
|
+
TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
|
143
|
+
|
144
|
+
# The work item created will be hidden
|
145
|
+
TASK_FLAG_HIDDEN = 0x200
|
146
|
+
|
147
|
+
# The work item runs only if there is a valid internet connection
|
148
|
+
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
|
149
|
+
|
150
|
+
# The work item starts again if the computer makes a non-idle to idle
|
151
|
+
# transition
|
152
|
+
TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
|
153
|
+
|
154
|
+
# The work item causes the system to be resumed, or awakened, if the
|
155
|
+
# system is running on batter power
|
156
|
+
TASK_FLAG_SYSTEM_REQUIRED = 0x1000
|
157
|
+
|
158
|
+
# The work item runs only if a specified account is logged on interactively
|
159
|
+
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
|
160
|
+
|
161
|
+
# Triggers
|
162
|
+
|
163
|
+
# The task will stop at some point in time
|
164
|
+
TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1
|
165
|
+
|
166
|
+
# The task can be stopped at the end of the repetition period
|
167
|
+
TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2
|
168
|
+
|
169
|
+
# The task trigger is disabled
|
170
|
+
TASK_TRIGGER_FLAG_DISABLED = 0x4
|
171
|
+
|
172
|
+
# Run Level Types
|
173
|
+
# Tasks will be run with the least privileges
|
174
|
+
TASK_RUNLEVEL_LUA = 0
|
175
|
+
# Tasks will be run with the highest privileges
|
176
|
+
TASK_RUNLEVEL_HIGHEST = 1
|
177
|
+
|
178
|
+
# Logon Types
|
179
|
+
# Used for non-NT credentials
|
180
|
+
TASK_LOGON_NONE = 0
|
181
|
+
# Use a password for logging on the user
|
182
|
+
TASK_LOGON_PASSWORD = 1
|
183
|
+
# The service will log the user on using Service For User
|
184
|
+
TASK_LOGON_S4U = 2
|
185
|
+
# Task will be run only in an existing interactive session
|
186
|
+
TASK_LOGON_INTERACTIVE_TOKEN = 3
|
187
|
+
# Group activation. The groupId field specifies the group
|
188
|
+
TASK_LOGON_GROUP = 4
|
189
|
+
# When Local System, Local Service, or Network Service account is
|
190
|
+
# being used as a security context to run the task
|
191
|
+
TASK_LOGON_SERVICE_ACCOUNT = 5
|
192
|
+
# Not in use; currently identical to TASK_LOGON_PASSWORD
|
193
|
+
TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
|
194
|
+
|
195
|
+
|
196
|
+
TASK_MAX_RUN_TIMES = 1440
|
197
|
+
TASKS_TO_RETRIEVE = 5
|
198
|
+
|
199
|
+
# Task creation
|
200
|
+
|
201
|
+
TASK_VALIDATE_ONLY = 0x1
|
202
|
+
TASK_CREATE = 0x2
|
203
|
+
TASK_UPDATE = 0x4
|
204
|
+
TASK_CREATE_OR_UPDATE = 0x6
|
205
|
+
TASK_DISABLE = 0x8
|
206
|
+
TASK_DONT_ADD_PRINCIPAL_ACE = 0x10
|
207
|
+
TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20
|
208
|
+
|
209
|
+
# Priority classes
|
210
|
+
|
211
|
+
REALTIME_PRIORITY_CLASS = 0
|
212
|
+
HIGH_PRIORITY_CLASS = 1
|
213
|
+
ABOVE_NORMAL_PRIORITY_CLASS = 2 # Or 3
|
214
|
+
NORMAL_PRIORITY_CLASS = 4 # Or 5, 6
|
215
|
+
BELOW_NORMAL_PRIORITY_CLASS = 7 # Or 8
|
216
|
+
IDLE_PRIORITY_CLASS = 9 # Or 10
|
217
|
+
|
218
|
+
CLSCTX_INPROC_SERVER = 0x1
|
219
|
+
CLSID_CTask = [0x148BD520,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
220
|
+
CLSID_CTaskScheduler = [0x148BD52A,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
221
|
+
IID_ITaskScheduler = [0x148BD527,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
222
|
+
IID_ITask = [0x148BD524,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
223
|
+
IID_IPersistFile = [0x0000010b,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46].pack('LSSC8')
|
224
|
+
|
225
|
+
# Days of month
|
226
|
+
|
227
|
+
TASK_FIRST = 0x01
|
228
|
+
TASK_SECOND = 0x02
|
229
|
+
TASK_THIRD = 0x04
|
230
|
+
TASK_FOURTH = 0x08
|
231
|
+
TASK_FIFTH = 0x10
|
232
|
+
TASK_SIXTH = 0x20
|
233
|
+
TASK_SEVENTH = 0x40
|
234
|
+
TASK_EIGHTH = 0x80
|
235
|
+
TASK_NINETH = 0x100
|
236
|
+
TASK_TENTH = 0x200
|
237
|
+
TASK_ELEVENTH = 0x400
|
238
|
+
TASK_TWELFTH = 0x800
|
239
|
+
TASK_THIRTEENTH = 0x1000
|
240
|
+
TASK_FOURTEENTH = 0x2000
|
241
|
+
TASK_FIFTEENTH = 0x4000
|
242
|
+
TASK_SIXTEENTH = 0x8000
|
243
|
+
TASK_SEVENTEENTH = 0x10000
|
244
|
+
TASK_EIGHTEENTH = 0x20000
|
245
|
+
TASK_NINETEENTH = 0x40000
|
246
|
+
TASK_TWENTIETH = 0x80000
|
247
|
+
TASK_TWENTY_FIRST = 0x100000
|
248
|
+
TASK_TWENTY_SECOND = 0x200000
|
249
|
+
TASK_TWENTY_THIRD = 0x400000
|
250
|
+
TASK_TWENTY_FOURTH = 0x800000
|
251
|
+
TASK_TWENTY_FIFTH = 0x1000000
|
252
|
+
TASK_TWENTY_SIXTH = 0x2000000
|
253
|
+
TASK_TWENTY_SEVENTH = 0x4000000
|
254
|
+
TASK_TWENTY_EIGHTH = 0x8000000
|
255
|
+
TASK_TWENTY_NINTH = 0x10000000
|
256
|
+
TASK_THIRTYETH = 0x20000000
|
257
|
+
TASK_THIRTY_FIRST = 0x40000000
|
258
|
+
TASK_LAST = 0x80000000
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Windows
|
2
|
+
module TimeCalcHelper
|
3
|
+
|
4
|
+
# Returns actual no of days for given month;
|
5
|
+
# Array with a 0 is defined to give actual result without
|
6
|
+
# any manipulation. eg, DAYS_IN_A_MONTH[1] = 31
|
7
|
+
# 0(NUMBER) is kept to avoid exceptions during calculations
|
8
|
+
DAYS_IN_A_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
9
|
+
|
10
|
+
# Returns no of days in a given month of a year
|
11
|
+
def days_in_month(month, year)
|
12
|
+
(month == 2 && is_leap_year?(year)) ? 29 : DAYS_IN_A_MONTH[month]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Year is leap when it is a multiple of 4 and not a multiple of 100.
|
16
|
+
# But it can be a multiple of 400
|
17
|
+
def is_leap_year?(year)
|
18
|
+
(((year % 4).zero? && !(year % 100).zero?) || (year % 400).zero?)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns total time duration in minutes
|
22
|
+
def time_in_minutes(time_str)
|
23
|
+
time_in_seconds(time_str) / 60
|
24
|
+
end
|
25
|
+
|
26
|
+
# Calculates total time duration in seconds
|
27
|
+
def time_in_seconds(time_str)
|
28
|
+
dt_tm_hash = time_details(time_str)
|
29
|
+
curr_time = Time.now
|
30
|
+
|
31
|
+
# Basic time variables
|
32
|
+
future_year = curr_time.year + dt_tm_hash[:year].to_i
|
33
|
+
future_month = curr_time.month + dt_tm_hash[:month].to_i
|
34
|
+
future_day = curr_time.day + dt_tm_hash[:day].to_i
|
35
|
+
future_hr = curr_time.hour + dt_tm_hash[:hour].to_i
|
36
|
+
future_min = curr_time.min + dt_tm_hash[:min].to_i
|
37
|
+
future_sec = curr_time.sec + dt_tm_hash[:sec].to_i
|
38
|
+
|
39
|
+
# 'extra value' calculations for these time variables
|
40
|
+
future_sec, future_min = extra_time(future_sec, future_min, 60)
|
41
|
+
future_min, future_hr = extra_time(future_min, future_hr, 60)
|
42
|
+
future_hr, future_day = extra_time(future_hr, future_day, 24)
|
43
|
+
|
44
|
+
# explicit method to calculate overloaded days;
|
45
|
+
# They may stretch upto years; heance leap year & months are into consideration
|
46
|
+
future_day, future_month, future_year = extra_days(future_day, future_month, future_year, curr_time.month, curr_time.year)
|
47
|
+
|
48
|
+
future_month, future_year = extra_months(future_month, future_year, curr_time.month, curr_time.year)
|
49
|
+
|
50
|
+
future_time = Time.new(future_year, future_month, future_day, future_hr, future_min, future_sec)
|
51
|
+
|
52
|
+
# Difference in time will return seconds
|
53
|
+
future_time.to_i - curr_time.to_i
|
54
|
+
end
|
55
|
+
|
56
|
+
# a will contain extra value of low_rank (in high_rank(eg min));
|
57
|
+
# b will hold actual low_rank value(ie sec) Example:
|
58
|
+
# low_rank = 65, high_rank = 2, div_val = 60
|
59
|
+
# Hence a = 1; b = 5
|
60
|
+
def extra_time(low_rank, high_rank, div_val)
|
61
|
+
a, b = low_rank.divmod(div_val)
|
62
|
+
high_rank += a; low_rank = b
|
63
|
+
[low_rank, high_rank]
|
64
|
+
end
|
65
|
+
|
66
|
+
def extra_months(month_count, year_count, init_month, init_year)
|
67
|
+
year, month_count = month_count.divmod(12)
|
68
|
+
if year.positive? && month_count.zero?
|
69
|
+
month_count = 12
|
70
|
+
year -= 1
|
71
|
+
end
|
72
|
+
year_count += year
|
73
|
+
[month_count, year_count]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns no of actual days with all overloaded months & Years
|
77
|
+
def extra_days(days_count, month_count, year_count, init_month, init_year)
|
78
|
+
# Will keep increamenting them with surplus days
|
79
|
+
days = days_count
|
80
|
+
mth = init_month
|
81
|
+
yr = init_year
|
82
|
+
|
83
|
+
loop do
|
84
|
+
days -= days_in_month(mth, yr)
|
85
|
+
break if days <= 0
|
86
|
+
mth += 1
|
87
|
+
if mth > 12
|
88
|
+
mth = 1; yr += 1
|
89
|
+
end
|
90
|
+
days_count = days
|
91
|
+
end
|
92
|
+
|
93
|
+
# Setting actual incremented values
|
94
|
+
month_count += (mth - init_month)
|
95
|
+
year_count += (yr - init_year)
|
96
|
+
|
97
|
+
[days_count, month_count, year_count]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Extracts "P_Y_M_DT_H_M_S" format and
|
101
|
+
# Returns a hash with applicable values of
|
102
|
+
# (keys =>) [:year, :month, :day, :hour, :min, :sec]
|
103
|
+
# Example: "PT3S" => {sec: 3}
|
104
|
+
def time_details(time_str)
|
105
|
+
tm_detail = {}
|
106
|
+
if time_str.to_s != ''
|
107
|
+
# time_str will be like "PxxYxxMxxDTxxHxxMxxS"
|
108
|
+
# Ignoring 'P' and extracting date and time
|
109
|
+
dt, tm = time_str[1..-1].split('T')
|
110
|
+
|
111
|
+
# Replacing strings
|
112
|
+
if dt.to_s != ''
|
113
|
+
dt['Y'] = 'year' if dt['Y']; dt['M'] = 'month' if dt['M']; dt['D'] = 'day' if dt['D']
|
114
|
+
dt_tm_array_to_hash(dt, tm_detail)
|
115
|
+
end
|
116
|
+
|
117
|
+
if tm.to_s != ''
|
118
|
+
tm['H'] = 'hour' if tm['H']; tm['M'] = 'min' if tm['M']; tm['S'] = 'sec' if tm['S']
|
119
|
+
dt_tm_array_to_hash(tm, tm_detail)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
tm_detail
|
123
|
+
end
|
124
|
+
|
125
|
+
# Method to convert date/time array to hash
|
126
|
+
def dt_tm_array_to_hash(arr, tm_detail)
|
127
|
+
arr.split(/(\d+)/)[1..-1].each_slice(2).inject(tm_detail) { |h, i| h[i.last.to_sym] = i.first; h }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib/win32', __dir__)
|
@@ -0,0 +1,392 @@
|
|
1
|
+
require 'win32/windows/time_calc_helper'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Windows::TimeCalcHelper do
|
5
|
+
let(:object) { klass.new }
|
6
|
+
let(:klass) do
|
7
|
+
Class.new do
|
8
|
+
include Windows::TimeCalcHelper
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'Format Test:' do
|
13
|
+
context 'An Invalid Date-Time string' do
|
14
|
+
time_str = 'An Invalid String'
|
15
|
+
let(:time_details) { object.time_details(time_str) }
|
16
|
+
it 'Returns an empty hash' do
|
17
|
+
expect(time_details).to be_a(Hash)
|
18
|
+
expect(time_details).to be_empty
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'A valid Date string' do
|
23
|
+
time_str = 'P1Y2M3D'
|
24
|
+
let(:time_details) { object.time_details(time_str) }
|
25
|
+
it 'Returns a valid Hash with only Date values' do
|
26
|
+
expect(time_details).to be_a(Hash)
|
27
|
+
expect(time_details[:year]).to eql('1')
|
28
|
+
expect(time_details[:month]).to eql('2')
|
29
|
+
expect(time_details[:day]).to eql('3')
|
30
|
+
expect(time_details[:hour]).to be_nil
|
31
|
+
expect(time_details[:min]).to be_nil
|
32
|
+
expect(time_details[:sec]).to be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'A valid Time string' do
|
37
|
+
time_str = 'PT4H5M6S'
|
38
|
+
let(:time_details) { object.time_details(time_str) }
|
39
|
+
it 'Returns a valid Hash with only Time values' do
|
40
|
+
expect(time_details).to be_a(Hash)
|
41
|
+
expect(time_details[:year]).to be_nil
|
42
|
+
expect(time_details[:month]).to be_nil
|
43
|
+
expect(time_details[:day]).to be_nil
|
44
|
+
expect(time_details[:hour]).to eql('4')
|
45
|
+
expect(time_details[:min]).to eql('5')
|
46
|
+
expect(time_details[:sec]).to eql('6')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'A valid Date-Time string' do
|
51
|
+
time_str = 'P1Y2M3DT4H5M6S'
|
52
|
+
let(:time_details) { object.time_details(time_str) }
|
53
|
+
it 'returns a valid Hash with values' do
|
54
|
+
expect(time_details).to be_a(Hash)
|
55
|
+
expect(time_details[:year]).to eql('1')
|
56
|
+
expect(time_details[:month]).to eql('2')
|
57
|
+
expect(time_details[:day]).to eql('3')
|
58
|
+
expect(time_details[:hour]).to eql('4')
|
59
|
+
expect(time_details[:min]).to eql('5')
|
60
|
+
expect(time_details[:sec]).to eql('6')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'An Unformatted Date-Time string' do
|
65
|
+
time_str = 'P2M3D1YT6S5M4H'
|
66
|
+
let(:time_details) { object.time_details(time_str) }
|
67
|
+
it 'Returns a valid Hash with values' do
|
68
|
+
expect(time_details).to be_a(Hash)
|
69
|
+
expect(time_details[:year]).to eql('1')
|
70
|
+
expect(time_details[:month]).to eql('2')
|
71
|
+
expect(time_details[:day]).to eql('3')
|
72
|
+
expect(time_details[:hour]).to eql('4')
|
73
|
+
expect(time_details[:min]).to eql('5')
|
74
|
+
expect(time_details[:sec]).to eql('6')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'A Date-Time string with few parameters' do
|
79
|
+
time_str = 'P1Y2MT3M4S'
|
80
|
+
let(:time_details) { object.time_details(time_str) }
|
81
|
+
it 'Returns a valid Hash with selected values' do
|
82
|
+
expect(time_details).to be_a(Hash)
|
83
|
+
expect(time_details[:year]).to eql('1')
|
84
|
+
expect(time_details[:month]).to eql('2')
|
85
|
+
expect(time_details[:day]).to be_nil
|
86
|
+
expect(time_details[:hour]).to be_nil
|
87
|
+
expect(time_details[:min]).to eql('3')
|
88
|
+
expect(time_details[:sec]).to eql('4')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'Year conversion:' do
|
94
|
+
# Time.now to be a Non-Leap Year(0001-01-01 00:00:00), So that
|
95
|
+
# Tests suites will be independant of their execution Time
|
96
|
+
before(:each) do
|
97
|
+
allow(Time).to receive(:now).and_return(Time.new(1))
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:current_time) { Time.new(1) }
|
101
|
+
context 'on given year' do
|
102
|
+
time_str = 'P1Y'
|
103
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
104
|
+
let(:next_time) { current_time + time_seconds }
|
105
|
+
it 'returns expected year' do
|
106
|
+
expect(next_time.year).to eql(current_time.year + 1)
|
107
|
+
expect(next_time.month).to eql(current_time.month)
|
108
|
+
expect(next_time.day).to eql(current_time.day)
|
109
|
+
expect(next_time.hour).to eql(current_time.hour)
|
110
|
+
expect(next_time.min).to eql(current_time.min)
|
111
|
+
expect(next_time.sec).to eql(current_time.sec)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'on given months' do
|
116
|
+
time_str = 'P12M'
|
117
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
118
|
+
let(:next_time) { current_time + time_seconds }
|
119
|
+
it 'returns expected year' do
|
120
|
+
expect(next_time.year).to eql(current_time.year + 1)
|
121
|
+
expect(next_time.month).to eql(current_time.month)
|
122
|
+
expect(next_time.day).to eql(current_time.day)
|
123
|
+
expect(next_time.hour).to eql(current_time.hour)
|
124
|
+
expect(next_time.min).to eql(current_time.min)
|
125
|
+
expect(next_time.sec).to eql(current_time.sec)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'on given days' do
|
130
|
+
time_str = 'P365D'
|
131
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
132
|
+
let(:next_time) { current_time + time_seconds }
|
133
|
+
it 'returns expected year' do
|
134
|
+
expect(next_time.year).to eql(current_time.year + 1)
|
135
|
+
expect(next_time.month).to eql(current_time.month)
|
136
|
+
expect(next_time.day).to eql(current_time.day)
|
137
|
+
expect(next_time.hour).to eql(current_time.hour)
|
138
|
+
expect(next_time.min).to eql(current_time.min)
|
139
|
+
expect(next_time.sec).to eql(current_time.sec)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'by given year and month' do
|
144
|
+
time_str = 'P1Y12M'
|
145
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
146
|
+
let(:next_time) { current_time + time_seconds }
|
147
|
+
it 'returns expected year' do
|
148
|
+
expect(next_time.year).to eql(current_time.year + 2)
|
149
|
+
expect(next_time.month).to eql(current_time.month)
|
150
|
+
expect(next_time.day).to eql(current_time.day)
|
151
|
+
expect(next_time.hour).to eql(current_time.hour)
|
152
|
+
expect(next_time.min).to eql(current_time.min)
|
153
|
+
expect(next_time.sec).to eql(current_time.sec)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'by given year, months and days' do
|
158
|
+
time_str = 'P1Y12M365D'
|
159
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
160
|
+
let(:next_time) { current_time + time_seconds }
|
161
|
+
it 'Returns the actual incremented day' do
|
162
|
+
expect(next_time.year).to eql(current_time.year + 3)
|
163
|
+
expect(next_time.month).to eql(current_time.month)
|
164
|
+
expect(next_time.day).to eql(current_time.day)
|
165
|
+
expect(next_time.hour).to eql(current_time.hour)
|
166
|
+
expect(next_time.min).to eql(current_time.min)
|
167
|
+
expect(next_time.sec).to eql(current_time.sec)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe 'Month conversion:' do
|
173
|
+
# Time.now to be a Non-Leap Year(0001-01-01 00:00:00), So that
|
174
|
+
# Tests suites will be independant of their execution Time
|
175
|
+
before(:each) do
|
176
|
+
allow(Time).to receive(:now).and_return(Time.new(1))
|
177
|
+
end
|
178
|
+
|
179
|
+
let(:current_time) { Time.new(1) }
|
180
|
+
context 'on given month' do
|
181
|
+
time_str = 'P1M'
|
182
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
183
|
+
let(:next_time) { current_time + time_seconds }
|
184
|
+
it 'returns expected month' do
|
185
|
+
expect(next_time.year).to eql(current_time.year)
|
186
|
+
expect(next_time.month).to eql(current_time.month + 1)
|
187
|
+
expect(next_time.day).to eql(current_time.day)
|
188
|
+
expect(next_time.hour).to eql(current_time.hour)
|
189
|
+
expect(next_time.min).to eql(current_time.min)
|
190
|
+
expect(next_time.sec).to eql(current_time.sec)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'on given days' do
|
195
|
+
# Since we are running test on 1st month, next month will occur in 31 days
|
196
|
+
time_str = 'P31D'
|
197
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
198
|
+
let(:next_time) { current_time + time_seconds }
|
199
|
+
it 'returns expected month' do
|
200
|
+
expect(next_time.year).to eql(current_time.year)
|
201
|
+
expect(next_time.month).to eql(current_time.month + 1)
|
202
|
+
expect(next_time.day).to eql(current_time.day)
|
203
|
+
expect(next_time.hour).to eql(current_time.hour)
|
204
|
+
expect(next_time.min).to eql(current_time.min)
|
205
|
+
expect(next_time.sec).to eql(current_time.sec)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'by given month and days' do
|
210
|
+
# Since we are running test on 1st month, next month will occur in 31 days
|
211
|
+
time_str = 'P1M31D'
|
212
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
213
|
+
let(:next_time) { current_time + time_seconds }
|
214
|
+
it 'returns expected month' do
|
215
|
+
expect(next_time.year).to eql(current_time.year)
|
216
|
+
expect(next_time.month).to eql(current_time.month + 2)
|
217
|
+
expect(next_time.day).to eql(current_time.day)
|
218
|
+
expect(next_time.hour).to eql(current_time.hour)
|
219
|
+
expect(next_time.min).to eql(current_time.min)
|
220
|
+
expect(next_time.sec).to eql(current_time.sec)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe 'Day conversion:' do
|
226
|
+
# Time.now to be a Non-Leap Year(0001-01-01 00:00:00), So that
|
227
|
+
# Tests suites will be independant of their execution Time
|
228
|
+
before(:each) do
|
229
|
+
allow(Time).to receive(:now).and_return(Time.new(1))
|
230
|
+
end
|
231
|
+
|
232
|
+
let(:current_time) { Time.new(1) }
|
233
|
+
context 'on given days' do
|
234
|
+
time_str = 'P1D'
|
235
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
236
|
+
let(:next_time) { current_time + time_seconds }
|
237
|
+
it 'returns expected days' do
|
238
|
+
expect(next_time.year).to eql(current_time.year)
|
239
|
+
expect(next_time.month).to eql(current_time.month)
|
240
|
+
expect(next_time.day).to eql(current_time.day + 1)
|
241
|
+
expect(next_time.hour).to eql(current_time.hour)
|
242
|
+
expect(next_time.min).to eql(current_time.min)
|
243
|
+
expect(next_time.sec).to eql(current_time.sec)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'on given hours' do
|
248
|
+
time_str = 'PT24H'
|
249
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
250
|
+
let(:next_time) { current_time + time_seconds }
|
251
|
+
it 'returns expected days' do
|
252
|
+
expect(next_time.year).to eql(current_time.year)
|
253
|
+
expect(next_time.month).to eql(current_time.month)
|
254
|
+
expect(next_time.day).to eql(current_time.day + 1)
|
255
|
+
expect(next_time.hour).to eql(current_time.hour)
|
256
|
+
expect(next_time.min).to eql(current_time.min)
|
257
|
+
expect(next_time.sec).to eql(current_time.sec)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context 'on given minutes' do
|
262
|
+
time_str = 'PT1440M'
|
263
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
264
|
+
let(:next_time) { current_time + time_seconds }
|
265
|
+
it 'returns expected days' do
|
266
|
+
expect(next_time.year).to eql(current_time.year)
|
267
|
+
expect(next_time.month).to eql(current_time.month)
|
268
|
+
expect(next_time.day).to eql(current_time.day + 1)
|
269
|
+
expect(next_time.hour).to eql(current_time.hour)
|
270
|
+
expect(next_time.min).to eql(current_time.min)
|
271
|
+
expect(next_time.sec).to eql(current_time.sec)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'on given seconds' do
|
276
|
+
time_str = 'PT86400S'
|
277
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
278
|
+
let(:next_time) { current_time + time_seconds }
|
279
|
+
it 'returns expected days' do
|
280
|
+
expect(next_time.year).to eql(current_time.year)
|
281
|
+
expect(next_time.month).to eql(current_time.month)
|
282
|
+
expect(next_time.day).to eql(current_time.day + 1)
|
283
|
+
expect(next_time.hour).to eql(current_time.hour)
|
284
|
+
expect(next_time.min).to eql(current_time.min)
|
285
|
+
expect(next_time.sec).to eql(current_time.sec)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'By the given days, hours, minutes and seconds' do
|
290
|
+
time_str = 'P1DT24H1440M86400S'
|
291
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
292
|
+
let(:next_time) { current_time + time_seconds }
|
293
|
+
it 'returns expected days' do
|
294
|
+
expect(next_time.year).to eql(current_time.year)
|
295
|
+
expect(next_time.month).to eql(current_time.month)
|
296
|
+
expect(next_time.day).to eql(current_time.day + 4)
|
297
|
+
expect(next_time.hour).to eql(current_time.hour)
|
298
|
+
expect(next_time.min).to eql(current_time.min)
|
299
|
+
expect(next_time.sec).to eql(current_time.sec)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe 'Minute conversion:' do
|
305
|
+
# Time.now to be a Non-Leap Year(0001-01-01 00:00:00), So that
|
306
|
+
# Tests suites will be independant of their execution Time
|
307
|
+
before(:each) do
|
308
|
+
allow(Time).to receive(:now).and_return(Time.new(1))
|
309
|
+
end
|
310
|
+
|
311
|
+
let(:current_time) { Time.new(1) }
|
312
|
+
context 'on given minute' do
|
313
|
+
time_str = 'PT1M'
|
314
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
315
|
+
let(:next_time) { current_time + time_seconds }
|
316
|
+
it 'returns expected minutes' do
|
317
|
+
expect(next_time.year).to eql(current_time.year)
|
318
|
+
expect(next_time.month).to eql(current_time.month)
|
319
|
+
expect(next_time.day).to eql(current_time.day)
|
320
|
+
expect(next_time.hour).to eql(current_time.hour)
|
321
|
+
expect(next_time.min).to eql(current_time.min + 1)
|
322
|
+
expect(next_time.sec).to eql(current_time.sec)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context 'on given seconds' do
|
327
|
+
time_str = 'PT60S'
|
328
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
329
|
+
let(:next_time) { current_time + time_seconds }
|
330
|
+
it 'returns expected minutes' do
|
331
|
+
expect(next_time.year).to eql(current_time.year)
|
332
|
+
expect(next_time.month).to eql(current_time.month)
|
333
|
+
expect(next_time.day).to eql(current_time.day)
|
334
|
+
expect(next_time.hour).to eql(current_time.hour)
|
335
|
+
expect(next_time.min).to eql(current_time.min + 1)
|
336
|
+
expect(next_time.sec).to eql(current_time.sec)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
context 'By given minutes and seconds' do
|
341
|
+
time_str = 'PT1M60S'
|
342
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
343
|
+
let(:next_time) { current_time + time_seconds }
|
344
|
+
it 'returns expected minutes' do
|
345
|
+
expect(next_time.year).to eql(current_time.year)
|
346
|
+
expect(next_time.month).to eql(current_time.month)
|
347
|
+
expect(next_time.day).to eql(current_time.day)
|
348
|
+
expect(next_time.hour).to eql(current_time.hour)
|
349
|
+
expect(next_time.min).to eql(current_time.min + 2)
|
350
|
+
expect(next_time.sec).to eql(current_time.sec)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
describe 'Date-Time conversions:' do
|
356
|
+
# For a single string given in leap/non-leap year
|
357
|
+
time_str = 'P400Y500M600DT700H800M900S'
|
358
|
+
|
359
|
+
context 'In a non leap year' do
|
360
|
+
let(:current_time) { Time.new(1) }
|
361
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
362
|
+
let(:next_time) { current_time + time_seconds }
|
363
|
+
it 'returns expected values' do
|
364
|
+
allow(Time).to receive(:now).and_return(Time.new(1))
|
365
|
+
|
366
|
+
expect(next_time.year).to eql(current_time.year + 443)
|
367
|
+
expect(next_time.month).to eql(current_time.month + 4)
|
368
|
+
expect(next_time.day).to eql(current_time.day + 21)
|
369
|
+
expect(next_time.hour).to eql(current_time.hour + 17)
|
370
|
+
expect(next_time.min).to eql(current_time.min + 35)
|
371
|
+
expect(next_time.sec).to eql(current_time.sec)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context 'In a leap year' do
|
376
|
+
let(:current_time) { Time.new(0) }
|
377
|
+
let(:time_seconds) { object.time_in_seconds(time_str) }
|
378
|
+
let(:next_time) { current_time + time_seconds }
|
379
|
+
it 'returns expected values' do
|
380
|
+
allow(Time).to receive(:now).and_return(Time.new(0))
|
381
|
+
|
382
|
+
# 1 Day less for the same string
|
383
|
+
expect(next_time.year).to eql(current_time.year + 443)
|
384
|
+
expect(next_time.month).to eql(current_time.month + 4)
|
385
|
+
expect(next_time.day).to eql(current_time.day + 20)
|
386
|
+
expect(next_time.hour).to eql(current_time.hour + 17)
|
387
|
+
expect(next_time.min).to eql(current_time.min + 35)
|
388
|
+
expect(next_time.sec).to eql(current_time.sec)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|