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.
@@ -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
@@ -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