or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcore-iteration.mdindex.mdrange-operations.mdvalidation-matching.md

range-operations.mddocs/

0

# Range Operations

1

2

Range-based iteration functionality similar to Python's built-in `range()` function, but for datetime objects using cron expressions as the "step" parameter. This allows you to find all datetime matches within a specific time range.

3

4

## Capabilities

5

6

### Croniter Range Function

7

8

Generate all datetime matches within a specified range using a cron expression.

9

10

```python { .api }

11

def croniter_range(

12

start,

13

stop,

14

expr_format: str,

15

ret_type=None,

16

day_or=True,

17

exclude_ends=False,

18

_croniter=None,

19

second_at_beginning=False,

20

expand_from_start_time=False,

21

):

22

"""

23

Generator that provides all times from start to stop matching the given cron expression.

24

25

You can think of this function as sibling to the builtin range function for datetime objects.

26

Like range(start,stop,step), except that here 'step' is a cron expression.

27

28

Parameters:

29

- start: Start datetime (datetime object or timestamp)

30

- stop: End datetime (datetime object or timestamp)

31

- expr_format: Cron expression string

32

- ret_type: Return type for yielded values (float, datetime, or None for auto-detect)

33

- day_or: How to handle day and day_of_week fields (True=OR, False=AND)

34

- exclude_ends: If True, exclude start/stop times even if they match (default False)

35

- _croniter: Custom croniter class (for testing/customization)

36

- second_at_beginning: Put seconds field at beginning instead of end

37

- expand_from_start_time: Calculate intervals from start_time instead of calendar

38

39

Yields:

40

All matching datetimes between start and stop (inclusive by default)

41

"""

42

```

43

44

## Usage Examples

45

46

### Basic Range Operations

47

48

```python

49

from croniter import croniter_range

50

from datetime import datetime

51

52

# Find all first Saturdays of each month in 2019

53

start = datetime(2019, 1, 1)

54

stop = datetime(2019, 12, 31)

55

56

for dt in croniter_range(start, stop, "0 0 * * sat#1"):

57

print(dt)

58

# 2019-01-05 00:00:00

59

# 2019-02-02 00:00:00

60

# 2019-03-02 00:00:00

61

# ... (continues for each month)

62

```

63

64

### Working with Timestamps

65

66

```python

67

import time

68

from datetime import datetime

69

70

# Use timestamps for input

71

start_ts = time.mktime(datetime(2019, 1, 1).timetuple())

72

stop_ts = time.mktime(datetime(2019, 1, 31).timetuple())

73

74

# Find all weekdays at 9 AM in January 2019

75

for dt in croniter_range(start_ts, stop_ts, "0 9 * * 1-5"):

76

print(datetime.fromtimestamp(dt))

77

```

78

79

### Excluding Endpoints

80

81

```python

82

from datetime import datetime

83

84

start = datetime(2019, 1, 1, 0, 0, 0) # Exactly midnight

85

stop = datetime(2019, 1, 2, 0, 0, 0) # Exactly next midnight

86

87

# Without exclude_ends (default): includes both endpoints if they match

88

daily_matches = list(croniter_range(start, stop, "0 0 * * *"))

89

print(len(daily_matches)) # 2 (both midnight times)

90

91

# With exclude_ends: excludes endpoints even if they match

92

daily_matches = list(croniter_range(start, stop, "0 0 * * *", exclude_ends=True))

93

print(len(daily_matches)) # 0 (no matches between the endpoints)

94

```

95

96

### Reverse Time Order

97

98

```python

99

from datetime import datetime

100

101

# Croniter_range can work with reverse time order (stop before start)

102

start = datetime(2019, 1, 31)

103

stop = datetime(2019, 1, 1)

104

105

# This will work and yield dates in reverse chronological order

106

for dt in croniter_range(start, stop, "0 12 * * *"): # Noon every day

107

print(dt)

108

# 2019-01-31 12:00:00

109

# 2019-01-30 12:00:00

110

# ... (continues backward)

111

# 2019-01-01 12:00:00

112

```

113

114

### Complex Cron Expressions

115

116

```python

117

from datetime import datetime

118

119

start = datetime(2019, 1, 1)

120

stop = datetime(2019, 12, 31)

121

122

# Every weekday at 9:30 AM and 2:30 PM

123

for dt in croniter_range(start, stop, "30 9,14 * * 1-5"):

124

print(dt)

125

126

# Last Friday of each month at 5 PM

127

for dt in croniter_range(start, stop, "0 17 * * L5"):

128

print(dt)

129

130

# Every 15 minutes during business hours (9 AM - 5 PM) on weekdays

131

for dt in croniter_range(start, stop, "*/15 9-17 * * 1-5"):

132

print(dt)

133

```

134

135

### Return Type Control

136

137

```python

138

from datetime import datetime

139

140

start = datetime(2019, 1, 1)

141

stop = datetime(2019, 1, 7)

142

143

# Return as datetime objects

144

datetime_results = list(croniter_range(start, stop, "0 0 * * *", ret_type=datetime))

145

print(type(datetime_results[0])) # <class 'datetime.datetime'>

146

147

# Return as timestamps

148

timestamp_results = list(croniter_range(start, stop, "0 0 * * *", ret_type=float))

149

print(type(timestamp_results[0])) # <class 'float'>

150

151

# Auto-detect based on input types (default behavior)

152

auto_results = list(croniter_range(start, stop, "0 0 * * *"))

153

print(type(auto_results[0])) # <class 'datetime.datetime'> (matches input type)

154

```

155

156

### Second-Level Precision

157

158

```python

159

from datetime import datetime

160

161

start = datetime(2019, 1, 1, 0, 0, 0)

162

stop = datetime(2019, 1, 1, 0, 1, 0) # One minute range

163

164

# Every 15 seconds within the minute

165

for dt in croniter_range(start, stop, "*/15 * * * * *"): # 6-field format with seconds

166

print(dt)

167

# 2019-01-01 00:00:00

168

# 2019-01-01 00:00:15

169

# 2019-01-01 00:00:30

170

# 2019-01-01 00:00:45

171

# 2019-01-01 00:01:00

172

```

173

174

### Day/Dayofweek Logic in Ranges

175

176

```python

177

from datetime import datetime

178

179

start = datetime(2019, 6, 1)

180

stop = datetime(2019, 6, 30)

181

182

# OR logic (default): 1st day of month OR any Wednesday

183

or_results = list(croniter_range(start, stop, "0 0 1 * wed", day_or=True))

184

185

# AND logic: 1st day of month IF it's a Wednesday

186

and_results = list(croniter_range(start, stop, "0 0 1 * wed", day_or=False))

187

188

print(f"OR results: {len(or_results)}") # More matches

189

print(f"AND results: {len(and_results)}") # Fewer matches (only if 1st is Wednesday)

190

```

191

192

### Performance Considerations

193

194

```python

195

from datetime import datetime

196

197

# For very large date ranges or sparse expressions,

198

# croniter_range inherits performance limits from croniter class

199

start = datetime(2000, 1, 1)

200

stop = datetime(2050, 12, 31)

201

202

# Very sparse expression - might hit performance limits

203

try:

204

sparse_matches = list(croniter_range(

205

start, stop,

206

"0 4 1 1 fri", # 4 AM on January 1st if it's Friday

207

day_or=False

208

))

209

print(f"Found {len(sparse_matches)} matches")

210

except Exception as e:

211

print(f"Performance limit reached: {e}")

212

```

213

214

### Custom Expansion from Start Time

215

216

```python

217

from datetime import datetime

218

219

start = datetime(2024, 7, 11) # Start on July 11th

220

stop = datetime(2024, 10, 1)

221

222

# Default behavior: every 7 days from calendar start (1st, 8th, 15th, etc.)

223

calendar_matches = list(croniter_range(start, stop, "0 0 */7 * *"))

224

225

# Custom expansion: every 7 days from start_time (11th, 18th, 25th, etc.)

226

custom_matches = list(croniter_range(

227

start, stop,

228

"0 0 */7 * *",

229

expand_from_start_time=True

230

))

231

232

print("Calendar-based:", [dt.day for dt in calendar_matches[:5]])

233

print("Start-time-based:", [dt.day for dt in custom_matches[:5]])

234

```