• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jtmoon79 / super-speedy-syslog-searcher / 23992895924

05 Apr 2026 02:53AM UTC coverage: 68.152% (-0.002%) from 68.154%
23992895924

push

github

jtmoon79
(CI) NFC TODO why not setup-rust-toolchain?

15527 of 22783 relevant lines covered (68.15%)

137023.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.51
/src/data/datetime.rs
1
// src/data/datetime.rs
2

3
//! Functions to perform regular expression ("regex") searches on bytes and
4
//! transform matches to chrono [`DateTime`] instances.
5
//!
6
//! Parsing bytes and finding datetime strings requires:
7
//! 1. searching some slice of bytes from a [`Line`] for a regular expression match.
8
//! 2. using a [`DateTimeParseInstr`], attempting to transform the matched regular expression named
9
//!    capture groups into data passable to chrono [`DateTime::parse_from_str`] or
10
//!    [`NaiveDateTime::parse_from_str`].
11
//! 3. return chrono `DateTime` instances along with byte offsets of the found matches to a caller
12
//!    (who will presumably use it create a new [`Sysline`]).
13
//!
14
//! The most relevant documents to understand this file are:
15
//! - `chrono` crate [`strftime`] format.
16
//! - `regex` crate [Regular Expression syntax].
17
//!
18
//! The most relevant functions are:
19
//! 1. [`bytes_to_regex_to_datetime`] which calls private function:
20
//! 2. [`captures_to_buffer_bytes`]
21
//!
22
//! The most relevant constant is [`DATETIME_PARSE_DATAS`].
23
//!
24
//! [`DATETIME_PARSE_DATAS`]: self::DATETIME_PARSE_DATAS
25
//! [`Line`]: crate::data::line::Line
26
//! [`Sysline`]: crate::data::sysline::Sysline
27
//! [`DateTime`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html
28
//! [`DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
29
//! [`NaiveDateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDateTime.html#method.parse_from_str
30
//! [`strftime`]: https://docs.rs/chrono/0.4.38/chrono/format/strftime/index.html
31
//! [Regular Expression syntax]: https://docs.rs/regex/1.10.5/regex/index.html#syntax
32

33
#![allow(non_camel_case_types)]
34
#![allow(non_upper_case_globals)]
35

36
use std::convert::TryFrom; // for passing array slices as references
37
use std::fmt;
38
use std::sync::RwLock;
39
#[cfg(any(debug_assertions, test))]
40
use std::thread;
41
use std::time::Duration as StdDuration;
42
#[doc(hidden)]
43
pub use std::time::{
44
    SystemTime,
45
    UNIX_EPOCH,
46
};
47

48
#[doc(hidden)]
49
pub use ::chrono::{
50
    DateTime,
51
    Datelike, // adds method `.year()` onto `DateTime`
52
    Duration,
53
    FixedOffset,
54
    Local,
55
    LocalResult,
56
    NaiveDateTime,
57
    NaiveTime,
58
    Offset,
59
    TimeZone,
60
    Utc,
61
};
62
use ::const_format::concatcp;
63
#[cfg(feature = "bench_jetscii")]
64
use ::jetscii;
65
use ::lazy_static::lazy_static;
66
use ::memchr;
67
use ::more_asserts::{
68
    debug_assert_ge,
69
    debug_assert_le,
70
    debug_assert_lt,
71
};
72
use ::numtoa::NumToA; // adds `numtoa` method to numbers
73
use ::once_cell::sync::OnceCell;
74
use ::phf::{
75
    phf_map,
76
    Map as PhfMap,
77
};
78
use ::regex::bytes::Regex;
79
#[allow(unused_imports)]
80
use ::si_trace_print::{
81
    defn,
82
    defo,
83
    defx,
84
    defñ,
85
    den,
86
    deo,
87
    dex,
88
    deñ,
89
};
90
#[cfg(feature = "bench_stringzilla")]
91
use ::stringzilla;
92
use ::unroll::unroll_for_loops;
93

94
#[cfg(any(debug_assertions, test))]
95
use crate::common::FPath;
96
#[doc(hidden)]
97
pub use crate::data::line::{
98
    LineIndex,
99
    RangeLineIndex,
100
};
101
#[cfg(any(debug_assertions, test))]
102
use crate::debug::printers::{
103
    buffer_to_String_noraw,
104
    str_to_String_noraw,
105
};
106
use crate::{
107
    de_err,
108
    de_wrn,
109
    debug_panic,
110
};
111

112
// -----------------------------------------------
113
// DateTime Regex Matching and strftime formatting
114

115
/// A _Year_ in a date
116
pub type Year = i32;
117

118
/// An _Uptime_ in a date, e.g. from `[    1.000043] kernel: Linux starting...`,
119
/// the seconds parts, e.g. the `1`.
120
/// This the default format for dmesg-style log files.
121
pub type Uptime = i32;
122

123
/// Crate `chrono` [`strftime`] formatting pattern, passed to
124
/// chrono [`DateTime::parse_from_str`] or [`NaiveDateTime::parse_from_str`].
125
///
126
/// Specific `const` instances of `DateTimePattern_str` are hardcoded in
127
/// [`captures_to_buffer_bytes`].
128
///
129
/// [`strftime`]: https://docs.rs/chrono/0.4.38/chrono/format/strftime/index.html
130
/// [`DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
131
/// [`NaiveDateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDateTime.html#method.parse_from_str
132
pub type DateTimePattern_str = str;
133

134
/// Analogous to [`DateTimePattern_str`], but for `String` instances.
135
pub type DateTimePattern_string = String;
136

137
/// Regular expression formatting pattern, passed to [`regex::bytes::Regex`].
138
///
139
/// [`regex::bytes::Regex`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Regex.html
140
pub type DateTimeRegex_str = str;
141

142
/// Regular expression capture group name, used within the regular expression
143
/// and for later retrieval via [`regex::captures.name`].
144
///
145
/// [`regex::captures.name`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Captures.html#method.name
146
pub type CaptureGroupName = str;
147

148
/// Regular expression capture group pattern, used within a [`RegexPattern`].
149
pub type CaptureGroupPattern = str;
150

151
/// A regular expression, passed to [`regex::bytes::Regex::captures`].
152
///
153
/// [`regex::bytes::Regex::captures`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Regex.html#method.captures
154
pub type RegexPattern = str;
155

156
/// The regular expression "class" used here, specifically for matching datetime
157
/// substrings within a [`&str`](str).
158
pub type DateTimeRegex = Regex;
159

160
/// A chrono [`DateTime`] type used in _s4lib_.
161
///
162
/// [`DateTime`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html
163
// TODO: rename to `DateTimeS4`
164
pub type DateTimeL = DateTime<FixedOffset>;
165
pub type DateTimeLOpt = Option<DateTimeL>;
166

167
pub(crate) const UPTIME_DEFAULT_OFFSET: SystemTime = UNIX_EPOCH;
168

169
/// FixedOffset seconds
170
#[cfg(test)]
171
#[allow(non_camel_case_types)]
172
type fos = i32;
173

174
#[cfg(test)]
175
pub(crate) const YEAR_FALLBACKDUMMY_VAL: i32 = 1972;
176
/// For datetimes missing a year, in some circumstances a filler year must be
177
/// used.
178
///
179
/// First leap year after Unix Epoch.
180
///
181
/// XXX: using leap year as a filler might help handle 'Feb 29' dates without a
182
///      year but it is not guaranteed. It depends on the file modified time
183
///      (i.e. [`blockreader.mtime()`](BlockReader)) being true.
184
const YEAR_FALLBACKDUMMY: &str = "1972";
185

186
/// Convert a `T` to a [`SystemTime`].
187
///
188
/// [`SystemTime`]: std::time::SystemTime
189
pub fn convert_to_systemtime<T>(epoch_seconds: T) -> SystemTime
×
190
where
×
191
    u64: From<T>,
×
192
{
193
    UNIX_EPOCH + std::time::Duration::from_secs(epoch_seconds.into())
×
194
}
×
195

196
/// create a `DateTime`
197
///
198
/// wrapper for chrono DateTime creation function
199
#[allow(unused)]
200
pub fn ymdhms(
14✔
201
    fixedoffset: &FixedOffset,
14✔
202
    year: i32,
14✔
203
    month: u32,
14✔
204
    day: u32,
14✔
205
    hour: u32,
14✔
206
    min: u32,
14✔
207
    sec: u32,
14✔
208
) -> DateTimeL {
14✔
209
    fixedoffset.with_ymd_and_hms(
14✔
210
        year,
14✔
211
        month,
14✔
212
        day,
14✔
213
        hour,
14✔
214
        min,
14✔
215
        sec,
14✔
216
    ).unwrap()
14✔
217
}
14✔
218

219
/// create a `DateTime` with FixedOffset `+00:00`
220
///
221
/// wrapper for chrono DateTime creation function
222
#[allow(unused)]
223
pub fn ymdhms0(
2✔
224
    year: i32,
2✔
225
    month: u32,
2✔
226
    day: u32,
2✔
227
    hour: u32,
2✔
228
    min: u32,
2✔
229
    sec: u32,
2✔
230
) -> DateTimeL {
2✔
231
    let fixedoffset = FixedOffset::east_opt(0).unwrap();
2✔
232
    fixedoffset.with_ymd_and_hms(
2✔
233
        year,
2✔
234
        month,
2✔
235
        day,
2✔
236
        hour,
2✔
237
        min,
2✔
238
        sec,
2✔
239
    ).unwrap()
2✔
240
}
2✔
241

242
/// create a `DateTime` with milliseconds
243
///
244
/// wrapper for chrono DateTime creation function
245
#[allow(clippy::too_many_arguments)]
246
pub fn ymdhmsl(
8✔
247
    fixedoffset: &FixedOffset,
8✔
248
    year: i32,
8✔
249
    month: u32,
8✔
250
    day: u32,
8✔
251
    hour: u32,
8✔
252
    min: u32,
8✔
253
    sec: u32,
8✔
254
    milli: i64,
8✔
255
) -> DateTimeL {
8✔
256
    fixedoffset.with_ymd_and_hms(
8✔
257
        year,
8✔
258
        month,
8✔
259
        day,
8✔
260
        hour,
8✔
261
        min,
8✔
262
        sec,
8✔
263
    )
8✔
264
    .unwrap()
8✔
265
    + Duration::try_milliseconds(milli).unwrap()
8✔
266
}
8✔
267

268
/// create a `DateTime` with microseconds
269
///
270
/// wrapper for chrono DateTime creation function
271
#[allow(clippy::too_many_arguments)]
272
pub fn ymdhmsm(
112✔
273
    fixedoffset: &FixedOffset,
112✔
274
    year: i32,
112✔
275
    month: u32,
112✔
276
    day: u32,
112✔
277
    hour: u32,
112✔
278
    min: u32,
112✔
279
    sec: u32,
112✔
280
    micro: i64,
112✔
281
) -> DateTimeL {
112✔
282
    fixedoffset.with_ymd_and_hms(
112✔
283
        year,
112✔
284
        month,
112✔
285
        day,
112✔
286
        hour,
112✔
287
        min,
112✔
288
        sec,
112✔
289
    )
112✔
290
    .unwrap()
112✔
291
    + Duration::microseconds(micro)
112✔
292
}
112✔
293

294
/// create a `DateTime` with nanoseconds
295
///
296
/// wrapper for chrono DateTime creation function
297
#[allow(unused)]
298
#[allow(clippy::too_many_arguments)]
299
pub fn ymdhmsn(
2,424✔
300
    fixedoffset: &FixedOffset,
2,424✔
301
    year: i32,
2,424✔
302
    month: u32,
2,424✔
303
    day: u32,
2,424✔
304
    hour: u32,
2,424✔
305
    min: u32,
2,424✔
306
    sec: u32,
2,424✔
307
    nano: i64,
2,424✔
308
) -> DateTimeL {
2,424✔
309
    fixedoffset
2,424✔
310
    .with_ymd_and_hms(
2,424✔
311
        year,
2,424✔
312
        month,
2,424✔
313
        day,
2,424✔
314
        hour,
2,424✔
315
        min,
2,424✔
316
        sec,
2,424✔
317
    )
2,424✔
318
    .unwrap()
2,424✔
319
    + Duration::nanoseconds(nano)
2,424✔
320
}
2,424✔
321

322
// abbreviate *y*ear *d*ummy
323
#[cfg(test)]
324
pub(crate) const YD: i32 = YEAR_FALLBACKDUMMY_VAL;
325

326
// Offset UTC/Z
327
#[cfg(test)]
328
const O_Z: fos = 0;
329
#[cfg(test)]
330
const O_0: fos = 0;
331
// Offset Local
332
// symbolic value for Local time, replaced at runtime
333
#[cfg(test)]
334
pub(crate) const O_L: fos = i32::MAX;
335
// Offset Minus (or West)
336
#[cfg(test)]
337
const O_M1: fos = -3600;
338
#[cfg(test)]
339
const O_M1_30: fos = -3600 - 30 * 60;
340
#[cfg(test)]
341
const O_M2: fos = -2 * 3600;
342
#[cfg(test)]
343
const O_M3: fos = -3 * 3600;
344
#[cfg(test)]
345
const O_M330: fos = -3 * 3600 - 30 * 60;
346
#[allow(dead_code)]
347
#[cfg(test)]
348
const O_M4: fos = -4 * 3600;
349
#[cfg(test)]
350
const O_M5: fos = -5 * 3600;
351
#[allow(dead_code)]
352
#[cfg(test)]
353
const O_M6: fos = -6 * 3600;
354
#[cfg(test)]
355
const O_M7: fos = -7 * 3600;
356
#[cfg(test)]
357
const O_M730: fos = -7 * 3600 - 30 * 60;
358
#[cfg(test)]
359
const O_M8: fos = -8 * 3600;
360
#[cfg(test)]
361
const O_M9: fos = -9 * 3600;
362
#[cfg(test)]
363
const O_M10: fos = -10 * 3600;
364
#[cfg(test)]
365
const O_M1030: fos = -10 * 3600 - 30 * 60;
366
#[cfg(test)]
367
const O_M11: fos = -11 * 3600;
368
#[cfg(test)]
369
const O_M1130: fos = -11 * 3600 - 30 * 60;
370
#[allow(dead_code)]
371
#[cfg(test)]
372
const O_M12: fos = -12 * 3600;
373
#[allow(dead_code)]
374
#[cfg(test)]
375
const O_M1230: fos = -12 * 3600 - 30 * 60;
376
// Offset Plus (or East)
377
#[cfg(test)]
378
const O_P1: fos = 3600;
379
#[cfg(test)]
380
const O_P1_30: fos = 3600 + 30 * 60;
381
#[allow(dead_code)]
382
#[cfg(test)]
383
const O_P2: fos = 2 * 3600;
384
#[cfg(test)]
385
const O_P3: fos = 3 * 3600;
386
#[allow(dead_code)]
387
#[cfg(test)]
388
const O_P4: fos = 4 * 3600;
389
#[cfg(test)]
390
const O_P5: fos = 5 * 3600;
391
#[cfg(test)]
392
const O_P6: fos = 6 * 3600;
393
#[allow(dead_code)]
394
#[cfg(test)]
395
const O_P7: fos = 7 * 3600;
396
#[cfg(test)]
397
const O_P8: fos = 8 * 3600;
398
#[cfg(test)]
399
const O_P9: fos = 9 * 3600;
400
#[cfg(test)]
401
const O_P945: fos = 9 * 3600 + 45 * 60;
402
#[cfg(test)]
403
const O_P10: fos = 10 * 3600;
404
#[cfg(test)]
405
const O_P11: fos = 11 * 3600;
406
#[cfg(test)]
407
const O_P12: fos = 12 * 3600;
408
#[cfg(test)]
409
const O_P1230: fos = 12 * 3600 + 30 * 60;
410
// misc. named timezones
411
#[cfg(test)]
412
const O_ALMT: fos = O_P6;
413
#[cfg(test)]
414
const O_CHUT: fos = O_P10;
415
#[cfg(test)]
416
const O_CIST: fos = O_M8;
417
#[cfg(test)]
418
const O_EDT: fos = O_M4;
419
#[cfg(test)]
420
const O_CAT: fos = O_P2;
421
#[cfg(test)]
422
const O_PDT: fos = O_M7;
423
#[cfg(test)]
424
const O_PETT: fos = O_P12;
425
#[cfg(test)]
426
const O_PET: fos = O_M5;
427
#[cfg(test)]
428
const O_PONT: fos = O_P11;
429
#[cfg(test)]
430
const O_PST: fos = O_M8;
431
#[cfg(test)]
432
const O_PWT: fos = O_P9;
433
#[cfg(test)]
434
const O_VLAT: fos = O_P10;
435
#[cfg(test)]
436
const O_WAT: fos = O_P1;
437
#[cfg(test)]
438
const O_WITA: fos = O_P8;
439
#[cfg(test)]
440
const O_WIT: fos = O_P9;
441
#[cfg(test)]
442
const O_WGST: fos = O_M2;
443
#[cfg(test)]
444
const O_WST: fos = O_P8;
445
#[cfg(test)]
446
const O_YAKT: fos = O_P9;
447
#[cfg(test)]
448
const O_YEKT: fos = O_P5;
449

450
/// tuple of arguments for function `ymdhmsn`
451
#[cfg(test)]
452
pub(crate) type ymdhmsn_args = (
453
    /* fixedoffset: */ fos,
454
    /* year: */ i32,
455
    /* month: */ u32,
456
    /* day: */ u32,
457
    /* hour: */ u32,
458
    /* min: */ u32,
459
    /* sec: */ u32,
460
    /* nano: */ i64,
461
);
462

463
#[cfg(test)]
464
pub(crate) const DUMMY_ARGS: ymdhmsn_args = (0, 1972, 1, 1, 0, 0, 0, 123456789);
465

466
/*
467
selective copy of chrono `strftime` specifier reference table
468
copied from https://docs.rs/chrono/0.4.38/chrono/format/strftime/index.html
469

470
DATE SPECIFIERS:
471

472
%Y  2001    The full proleptic Gregorian year, zero-padded to 4 digits.
473
%C  20      The proleptic Gregorian year divided by 100, zero-padded to 2 digits.
474
%y  01      The proleptic Gregorian year modulo 100, zero-padded to 2 digits.
475

476
%m  07      Month number (01–12), zero-padded to 2 digits.
477
%b  Jul     Abbreviated month name. Always 3 letters.
478
%B  July    Full month name. Also accepts corresponding abbreviation in parsing.
479

480
%d  08      Day number (01–31), zero-padded to 2 digits.
481
%e  8       Same as %d but space-padded. Same as %_d.
482

483
%a  Sun     Abbreviated weekday name. Always 3 letters.
484
%A  Sunday  Full weekday name. Also accepts corresponding abbreviation in parsing.
485
%w  0       Sunday = 0, Monday = 1, …, Saturday = 6.
486
%u  7       Monday = 1, Tuesday = 2, …, Sunday = 7. (ISO 8601)
487

488
TIME SPECIFIERS:
489

490
%H  00  Hour number (00–23), zero-padded to 2 digits.
491
%k  0   Same as %H but space-padded. Same as %_H.
492
%I  12  Hour number in 12-hour clocks (01–12), zero-padded to 2 digits.
493
%l  12  Same as %I but space-padded. Same as %_I.
494

495
%P  am  am or pm in 12-hour clocks.
496
%p  AM  AM or PM in 12-hour clocks.
497

498
%M  34  Minute number (00–59), zero-padded to 2 digits.
499

500
%S  60  Second number (00–60), zero-padded to 2 digits.
501

502
%f      026490000   The fractional seconds (in nanoseconds) since last whole second.
503
%.f     .026490     Similar to .%f but left-aligned. These all consume the leading dot.
504
%.3f    .026        Similar to .%f but left-aligned but fixed to a length of 3.
505
%.6f    .026490     Similar to .%f but left-aligned but fixed to a length of 6.
506
%.9f    .026490000  Similar to .%f but left-aligned but fixed to a length of 9.
507
%3f     026         Similar to %.3f but without the leading dot.
508
%6f     026490      Similar to %.6f but without the leading dot.
509
%9f     026490000   Similar to %.9f but without the leading dot.
510

511
TIME ZONE SPECIFIERS:
512

513
%Z  ACST    Local time zone name. Skips all non-whitespace characters during parsing.
514
%z  +0930   Offset from the local time to UTC (with UTC being +0000).
515
%:z +09:30  Same as %z but with a colon.
516
%#z +09     Parsing only: Same as %z but allows minutes to be missing or present.
517

518
%s  994518299   UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC.
519

520
SPECIAL SPECIFIERS:
521

522
%t  Literal tab (\t).
523
%n  Literal newline (\n).
524
%%  Literal percent sign.
525
*/
526

527
// TODO: [2022/10] Issue #26
528
//       refactor this `datetime.rs` to remove intermediary `DTP_*` variables
529
//       allow more flexible regex grouping and name declarations.
530

531
/// DateTime Format Specifier for a Year.
532
/// Follows chrono `strftime` specifier formatting.
533
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
534
pub enum DTFS_Year {
535
    /// %Y
536
    Y,
537
    /// %y
538
    y,
539
    /// none provided, must be filled.
540
    /// the associated `pattern` should use "%Y`
541
    _fill,
542
    /// no year
543
    _none,
544
}
545

546
/// DateTime Format Specifier for a Month.
547
/// Follows chrono `strftime` specifier formatting.
548
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
549
pub enum DTFS_Month {
550
    /// %m, month numbers 00 to 12
551
    m,
552
    /// %m, month numbers 0 to 12 (`s`ingle-digit)
553
    ///
554
    /// Transformed to form `%m` in function `captures_to_buffer_bytes`.
555
    ms,
556
    /// %b, month abbreviated to three characters, e.g. `"Jan"`.
557
    b,
558
    /// %B, month full name, e.g. `"January"`
559
    ///
560
    /// Transformed to form `%b` in
561
    /// function `month_bB_to_month_m_bytes` called by
562
    /// function `captures_to_buffer_bytes`
563
    B,
564
    /// no month
565
    _none,
566
}
567

568
/// DateTime Format Specifier for a Day.
569
/// Follows chrono `strftime` specifier formatting.
570
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
571
pub enum DTFS_Day {
572
    /// `%d`, day number 01 to 31
573
    /// `%e`, day number 1 to 31
574
    ///
575
    /// Single-digit `" 8"` or `"8"` is transformed to `"08"` in
576
    /// function `captures_to_buffer_bytes`.
577
    _e_or_d,
578
    /// no day
579
    _none,
580
}
581

582
/// DateTime Format Specifier for an Hour.
583
/// Follows chrono `strftime` specifier formatting.
584
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
585
pub enum DTFS_Hour {
586
    /// %H, 24 hour, 00 to 23
587
    H,
588
    /// %k, 24 hour, 0 to 23
589
    k,
590
    /// %I, 12 hour, 01 to 12
591
    I,
592
    /// %l, 12 hour, 1 to 12
593
    l,
594
    /// no hour
595
    _none,
596
}
597

598
/// DateTime Format Specifier for a Minute.
599
/// Follows chrono `strftime` specifier formatting.
600
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
601
pub enum DTFS_Minute {
602
    /// %M, 00 to 59
603
    M,
604
    /// no minute
605
    _none,
606
}
607

608
/// DateTime Format Specifier for a Second.
609
/// Follows chrono `strftime` specifier formatting.
610
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
611
pub enum DTFS_Second {
612
    /// %S, 00 to 60
613
    S,
614
    /// fill with value `0`
615
    _fill,
616
    /// no second
617
    _none,
618
}
619

620
/// DateTime Format Specifier for a Fractional or fractional second.
621
/// Follows chrono `strftime` specifier formatting.
622
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
623
pub enum DTFS_Fractional {
624
    /// %f, subsecond decimal digits
625
    f,
626
    /// no fractional
627
    _none,
628
}
629

630
/// DateTime Format Specifier for a Timezone.
631
/// Follows chrono `strftime` specifier formatting.
632
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
633
pub enum DTFS_Tz {
634
    /// `%z` numeric timezone offset, e.g. `"+0930"`
635
    z,
636
    /// `%:z` numeric timezone offset with colon, e.g. `"+09:30"` ("zee colon")
637
    zc,
638
    /// `%#z`numeric timezone offset shortened, e.g. `"+09"` ("zee pound")
639
    zp,
640
    /// `%Z` named timezone offset, e.g. `"PST"`
641
    Z,
642
    /// none, must be filled.
643
    /// The associated `pattern` should use `%:z` (variable name substring `Zc`)
644
    /// as that is the form displayed by
645
    /// `chrono::FixedOffset::east(0).as_string().to_str()`
646
    _fill,
647
    /// no timezone
648
    _none,
649
}
650

651
/// DateTime Format Specifier for a Unix Epoch.
652
/// Follows chrono `strftime` specifier formatting.
653
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
654
pub enum DTFS_Epoch {
655
    /// `%s` Unix Epoch in seconds
656
    s,
657
    /// none
658
    _none,
659
}
660

661
/// DateTime Format Specifier for an uptime, seconds since system boot.
662
/// e.g. from log message `[    0.001000] kernel: Linux starting...`.
663
/// This the default format for dmesg-style log files.
664
#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
665
pub enum DTFS_Uptime {
666
    /// system uptime in seconds
667
    u,
668
    /// none
669
    _none,
670
}
671

672
/// `DTFSSet`, "DateTime Format Specifer Set", is essentially instructions
673
/// to transcribe regex [`named capture groups`] to a
674
/// chrono [`strftime`]-ready string,
675
/// and ultimately a [`DateTimeL`] instance.
676
///
677
/// Given extracted regular expression named capture groups
678
/// `<year>`, `<month>`, `<day>`, etc. (see `CGN_` vars),
679
/// then what is the format of each such that the data can be readied and then
680
/// passed to [`chrono::DateTime::parse_from_str`]?
681
/// These are effectively mappings to receive extracting datetime substrings
682
/// in a [`&str`](str) then to rearrange those into order suitable for
683
/// [`captures_to_buffer_bytes`].
684
///
685
/// Given the following code for capturing and enumerating some named capture
686
/// groups:
687
/// ```rust
688
/// use ::regex::Regex;
689
/// use ::chrono::{
690
///   NaiveDateTime,
691
///   NaiveDate,
692
/// };
693
/// fn main() {
694
///     let data = r"[2020/Mar/05 12:17:59.631000 PMDT] ../source3/smbd/oplock.c:1340(init_oplocks)";
695
///     let pattern = r"^\[(?P<year>[12][[:digit:]]{3})[ /\-]?(?P<month>(?i)01|02|03|04|05|06|07|08|09|10|11|12|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec(?-i))[ /\-]?(?P<day>01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)[ T]?(?P<hour>00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24)[:]?(?P<minute>[012345][[:digit:]])[:]?(?P<second>[0123456][[:digit:]])[\.,](?P<subsecond>[[:digit:]]{3,9})[[:blank:]](?P<tz>ACDT|ACST|ACT|ADT|AEDT|AEST|AET|AFT|AKDT|AKST|ALMT|AMST|AMT|ANAT|AQTT|ART|AST|AWST|AZOT|AZT|BIOT|BIT|BNT|BOT|BRST|BRT|BST|BTT|CAT|CCT|CDT|CEST|CET|CHADT|CHOT|CHST|CHUT|CIST|CKT|CLST|CLT|COST|COT|CST|CT|CVT|CWST|CXT|DAVT|DDUT|DFT|EAST|EAT|ECT|EDT|EEST|EET|EGST|EGT|EST|ET|FET|FJT|FKST|FKT|FNT|GALT|GAMT|GET|GFT|GILT|GIT|GMT|GST|GYT|HAEC|HDT|HKT|HMT|HOVT|HST|ICT|IDLW|IDT|IOT|IRDT|IRKT|IRST|IST|JST|KALT|KGT|KOST|KRAT|KST|LHST|LINT|MAGT|MART|MAWT|MDT|MEST|MET|MHT|MIST|MIT|MMT|MSK|MST|MUT|MVT|MYT|NCT|NDT|NFT|NOVT|NPT|NST|NT|NUT|NZDT|NZST|OMST|ORAT|PDT|PETT|PET|PGT|PHOT|PHST|PHT|PKT|PMDT|PMST|PONT|PST|PWT|PYST|PYT|RET|ROTT|SAKT|SAMT|SAST|SBT|SCT|SDT|SGT|SLST|SRET|SRT|SST|SYOT|TAHT|TFT|THA|TJT|TKT|TLT|TMT|TOT|TRT|TVT|ULAT|UTC|UT|UYST|UYT|UZT|VET|VLAT|VOLT|VOST|VUT|WAKT|WAST|WAT|WEST|WET|WGST|WGT|WIB|WITA|WIT|WST|YAKT|YEKT)[^[[:upper:]]]";
696
///     let re = Regex::new(pattern).unwrap();
697
///     let captures = match re.captures(data) {
698
///         Some(cap) => cap,
699
///         None => panic!("re.captures failed"),
700
///     };
701
///     for (i, name_opt) in re.capture_names().enumerate() {
702
///         let match_ = match captures.get(i) {
703
///             Some(m_) => m_,
704
///             None => {
705
///                 match name_opt {
706
///                     Some(name) => {
707
///                         eprintln!("{} {:?} None", i, name);
708
///                     },
709
///                     None => {
710
///                         eprintln!("{} None None", i);
711
///                     }
712
///                 }
713
///                 continue;
714
///             }
715
///         };
716
///         match name_opt {
717
///             Some(name) => {
718
///                 eprintln!("{} {:?} {:?}", i, name, match_.as_str());
719
///             },
720
///             None => {
721
///                 eprintln!("{} unnamed {:?}", i, match_.as_str());
722
///             }
723
///         }
724
///     }
725
/// }
726
/// ```
727
/// [(Rust Playground)],
728
///
729
/// should print:
730
/// ```text
731
/// index name        value
732
/// 0     unnamed     "[2020/Mar/05 12:17:59.631000 PMDT]"
733
/// 1     "year"      "2020"
734
/// 2     "month"     "Mar"
735
/// 3     "day"       "05"
736
/// 4     "hour"      "12"
737
/// 5     "minute"    "17"
738
/// 6     "second"    "59"
739
/// 7     "subsecond" "631000"
740
/// 8     "tz"        "PMDT"
741
/// ```
742
///
743
/// A `DTFSSset` provides "instructions" to transform and then pass those
744
/// string values to chrono `parse_from_str`.
745
///
746
/// The `DTFSSset` instance for this example should be:
747
///
748
/// ```ignore
749
/// DTFSSet {
750
///     year: DTFS_Year::Y,     // example value was `"2020"`
751
///     month: DTFS_Month::b,   // example value was `"Mar"`
752
///     day: DTFS_Day::_e_or_d, // example value was `"05"`
753
///     hour: DTFS_Hour::H,     // example value was `"12"`
754
///     minute: DTFS_Minute::M, // example value was `"17"`
755
///     second: DTFS_Second::S, // example value was `"59"`
756
///     fractional: DTFS_Fractional::_none, // example value did not have a fractional
757
///     tz: DTFS_Tz::_fill,     // example value did not have a timezone, it will be filled with the default, or fallback, timezone (which can be passed by the user via `--tz-offset`)
758
///     pattern: "%Y%m%dT%H%M%S%:z", // strftime specifier pattern, notice the %m ?
759
/// };
760
/// ```
761
///
762
/// Here is the tricky part: function `captures_to_buffer_bytes` transforms
763
/// some values. In the example case, value `"Mar"` is written to a buffer
764
/// as `"03"`. The timezone value was not captured, so the default
765
/// timezone offset value is written to the same buffer.
766
/// That buffer is passed to function `datetime_parse_from_str`
767
/// which, in this case, calls chrono [`DateTime::parse_from_str`] (
768
/// function `datetime_parse_from_str` might
769
/// call [`NaiveDateTime::parse_from_str`] in other cases).
770
///
771
/// The enum values `DTFS_*` are interdependent with the value of `pattern`.
772
/// The `pattern` is a chrono `strftime` specifier formatting string
773
/// passed to chrono `datetime_parse_from_str`.
774
///
775
/// ---
776
///
777
/// All `DTFSSet` instances are `const`.
778
///
779
/// All `DTFSSet.pattern` take from `const` declared variables `DTP_*`.
780
///
781
/// Strictly, there are 192 permutations of `DTFSSet`.
782
/// In practice, only a subset is encountered in real-life syslog files.
783
/// Furthermore, some regex capture data is modified to be only one type.
784
/// For example, capture group _day_ will capture pattern specifier for
785
/// `%e` (`" 8"`) and `%d` (`"08"`).
786
/// The captured data will be modified to strftime day format `%d`,
787
/// e.g. captured data `" 8"` becomes `"08"` before passing to `parse_from_str`.
788
///
789
/// Each `DTFSSet` is checked for internal consistency within test
790
/// `test_DATETIME_PARSE_DATAS_builtin` (as much as reasonably possible).
791
///
792
/// [`named capture groups`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Captures.html
793
/// [`chrono::DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
794
/// [`DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
795
/// [`NaiveDateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDateTime.html#method.parse_from_str
796
/// [`strftime`]: https://docs.rs/chrono/0.4.38/chrono/format/strftime/index.html
797
/// [(Rust Playground)]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=00460112beb2a6d078d6bbba72557574
798
#[derive(Eq, Hash, PartialEq, Ord, PartialOrd)]
799
pub struct DTFSSet<'a> {
800
    pub year: DTFS_Year,
801
    pub month: DTFS_Month,
802
    pub day: DTFS_Day,
803
    pub hour: DTFS_Hour,
804
    pub minute: DTFS_Minute,
805
    pub second: DTFS_Second,
806
    pub fractional: DTFS_Fractional,
807
    pub tz: DTFS_Tz,
808
    pub epoch: DTFS_Epoch,
809
    pub uptime: DTFS_Uptime,
810
    /// strftime pattern passed to [`chrono::DateTime::parse_from_str`] or
811
    /// [`chrono::NaiveDateTime::parse_from_str`]
812
    /// in function [`datetime_parse_from_str`]. Directly relates to order of
813
    /// capture group extractions and `push_str` done in private
814
    /// `captures_to_buffer_bytes`.
815
    ///
816
    /// `pattern` is interdependent with other members.
817
    ///
818
    /// Tested in test `test_DATETIME_PARSE_DATAS_builtin`.
819
    ///
820
    /// [`chrono::DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
821
    /// [`chrono::NaiveDateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDate.html#method.parse_from_str
822
    pub pattern: &'a DateTimePattern_str,
823
}
824

825
impl DTFSSet<'_> {
826

827
    /// does the `DTFSSet` expect a year?
828
    pub const fn has_year(&self) -> bool {
237,298✔
829
        match self.year {
237,298✔
830
            DTFS_Year::Y
831
            | DTFS_Year::y => true,
219,664✔
832
            DTFS_Year::_fill
833
            | DTFS_Year::_none => false,
17,634✔
834
        }
835
    }
237,298✔
836

837
    /// does the `DTFSSet` expect a 4-digit year?
838
    pub const fn has_year4(&self) -> bool {
218,103✔
839
        match self.year {
218,103✔
840
            DTFS_Year::Y => true,
205,500✔
841
            DTFS_Year::y
842
            | DTFS_Year::_fill
843
            | DTFS_Year::_none
844
            => false,
12,603✔
845
        }
846
    }
218,103✔
847

848
    /// does the `DTFSSet` expect a month?
849
    pub const fn has_month(&self) -> bool {
175✔
850
        match self.month {
175✔
851
            DTFS_Month::m
852
            | DTFS_Month::ms
853
            | DTFS_Month::b
854
            | DTFS_Month::B => true,
168✔
855
            DTFS_Month::_none => false,
7✔
856
        }
857
    }
175✔
858

859
    /// does the `DTFSSet` expect a day?
860
    pub const fn has_day(&self) -> bool {
175✔
861
        match self.day {
175✔
862
            DTFS_Day::_e_or_d => true,
168✔
863
            DTFS_Day::_none => false,
7✔
864
        }
865
    }
175✔
866

867
    /// does the `DTFSSet` expect a hour?
868
    pub const fn has_hour(&self) -> bool {
175✔
869
        match self.hour {
175✔
870
            DTFS_Hour::H
871
            | DTFS_Hour::k
872
            | DTFS_Hour::I
873
            | DTFS_Hour::l => true,
168✔
874
            DTFS_Hour::_none => false,
7✔
875
        }
876
    }
175✔
877

878
    /// does the `DTFSSet` expect a minute?
879
    pub const fn has_minute(&self) -> bool {
175✔
880
        match self.minute {
175✔
881
            DTFS_Minute::M => true,
168✔
882
            DTFS_Minute::_none => false,
7✔
883
        }
884
    }
175✔
885

886
    /// does the `DTFSSet` expect a timezone?
887
    pub const fn has_tz(&self) -> bool {
261,799✔
888
        match self.tz {
261,799✔
889
            DTFS_Tz::z
890
            | DTFS_Tz::zc
891
            | DTFS_Tz::zp
892
            | DTFS_Tz::Z => true,
168,879✔
893
            DTFS_Tz::_fill
894
            | DTFS_Tz::_none => false,
92,920✔
895
        }
896
    }
261,799✔
897

898
    /// does the `DTFSSet` expect an uptime?
899
    pub const fn has_uptime(&self) -> bool {
237,198✔
900
        match self.uptime {
237,198✔
901
            DTFS_Uptime::u => true,
2,690✔
902
            DTFS_Uptime::_none => false,
234,508✔
903
        }
904
    }
237,198✔
905

906
    /// does the `DTFSSet` expect to capture a sequence of two decimal digits?
907
    pub const fn has_d2(&self) -> bool {
218,103✔
908
        match self.year {
218,103✔
909
            DTFS_Year::Y => return true,
205,500✔
910
            DTFS_Year::y
911
            | DTFS_Year::_fill
912
            | DTFS_Year::_none => {}
12,603✔
913
        }
914
        match self.month {
12,603✔
915
            DTFS_Month::m => return true,
362✔
916
            DTFS_Month::ms
917
            | DTFS_Month::b
918
            | DTFS_Month::B
919
            | DTFS_Month::_none => {}
12,241✔
920
        }
921
        match self.hour {
12,241✔
922
            DTFS_Hour::H
923
            | DTFS_Hour::I => return true,
8,953✔
924
            DTFS_Hour::k
925
            | DTFS_Hour::l
926
            | DTFS_Hour::_none => {}
3,288✔
927
        }
928
        match self.minute {
3,288✔
929
            DTFS_Minute::M => return true,
×
930
            DTFS_Minute::_none => {}
3,288✔
931
        }
932
        match self.second {
3,288✔
933
            DTFS_Second::S => return true,
×
934
            DTFS_Second::_fill
935
            | DTFS_Second::_none => {}
3,288✔
936
        }
937
        match self.tz {
3,288✔
938
            DTFS_Tz::z
939
            | DTFS_Tz::zc
940
            | DTFS_Tz::zp => return true,
×
941
            DTFS_Tz::Z
942
            | DTFS_Tz::_fill
943
            | DTFS_Tz::_none => {}
3,288✔
944
        }
945
        match self.epoch {
3,288✔
946
            DTFS_Epoch::s => return true,
2,632✔
947
            DTFS_Epoch::_none => {}
656✔
948
        }
949
        match self.uptime {
656✔
950
            DTFS_Uptime::u => return true,
656✔
951
            DTFS_Uptime::_none => {}
×
952
        }
953

954
        false
×
955
    }
218,103✔
956
}
957

958
impl fmt::Debug for DTFSSet<'_> {
959
    fn fmt(
235,740✔
960
        &self,
235,740✔
961
        f: &mut fmt::Formatter,
235,740✔
962
    ) -> fmt::Result {
235,740✔
963
        let mut f_ = f.debug_struct("DTFSSet:");
235,740✔
964
        f_.field("year", &self.year)
235,740✔
965
            .field("month", &self.month)
235,740✔
966
            .field("day", &self.day)
235,740✔
967
            .field("hour", &self.hour)
235,740✔
968
            .field("second", &self.second)
235,740✔
969
            .field("fractional", &self.fractional)
235,740✔
970
            .field("tz", &self.tz)
235,740✔
971
            .field("year?", &self.has_year())
235,740✔
972
            .field("uptime?", &self.has_uptime())
235,740✔
973
            .field("tz?", &self.has_tz())
235,740✔
974
            .field("pattern", &self.pattern)
235,740✔
975
            ;
976

977
        f_.finish()
235,740✔
978
    }
235,740✔
979
}
980

981
/// `Instr`uctions for `pars`ing from some unknown [`bytes`](u8) to a
982
/// [`regex::Regex.captures`] instance to a `&str` value that can be passed to
983
/// [`chrono::DateTime::parse_from_str`] or
984
/// [`chrono::NaiveDateTime::parse_from_str`].
985
///
986
/// An explanation of a `DateTimeParseInstr` instance:
987
///
988
/// 1. All `DateTimeParseInstr` instances are declared within the array
989
///    [`pub const DATETIME_PARSE_DATAS`].
990
/// 2. The `DateTimeParseInstr.regex_pattern` is a `&str` for regex matching some
991
///    line of text from the processed file.
992
/// 3. The `DateTimeParseInstr.dtfs` are like instructions for taking the
993
///    regex capture group values, `regex::Regex.captures`, and transforming
994
///    those into a single `&str` value that can be processed by
995
///    `chrono::DateTime::parse_from_str` or
996
///    `chrono::NaiveDateTime::parse_from_str`.
997
///    See [`DTFSSet`].
998
/// 4. The `DateTimeParseInstr.range_regex` is used to slice data provided by
999
///    a [`Line`].
1000
///    Some lines can have many bytes, so this shortens the amount of time
1001
///    the regex spends matching (regex matching is an resource expensive
1002
///    operation).
1003
///    Also, syslogs have a bias toward placing
1004
///    the syslog datetime stamp at the front of the line. slicing the front
1005
///    of the line, for example, the first 50 bytes, makes it less likely an
1006
///    errant match would be made further into the syslog line. e.g. a syslog
1007
///    message may include a datetime string unrelated to the datetime
1008
///    of that syslog message.
1009
/// 5. `DateTimeParseInstr.cgn_first` and `DateTimeParseInstr.cgn_last` are the
1010
///    first and last regex capture groups within the
1011
///    `DateTimeParseInstr.regex_pattern`. These are used to help determine
1012
///    where a datetime substring occurred within the given line. For exampe,
1013
///    given line `"INFO: 2019/01/22 07:55:38 hello!"`, the first regex named
1014
///    capture group is the year, `<year>` (at `"2"`).
1015
///    The year data begins at byte offset 5.
1016
///    The last named capture group is the second, `<second>`.
1017
///    The second data begins at byte offset 23 and, more importantly,
1018
///    ends at byte offset 25 (one byte after `"8"`).
1019
///    Later, in function `bytes_to_regex_to_datetime`, the offsets are
1020
///    returned as a pair, `(Some(5, 25))`.
1021
///    These offsets values are stored by the controlling
1022
///    [`SyslineReader`], and later passed to a [`PrinterLogMessage`] which
1023
///    highlights the datetime substring within the line (if `--color` is
1024
///    enabled).
1025
///
1026
/// A `DateTimeParseInstr` instance is declared with macro [`DTPD!`].
1027
///
1028
/// The values within a `DateTimeParseInstr` instance are mostly entirely
1029
/// interdependent and tricky to declare correctly.
1030
/// The test `test_DATETIME_PARSE_DATAS_builtin`
1031
/// checks for as many irregularities as it can find.
1032
/// The test `test_DATETIME_PARSE_DATAS_test_cases` processes entries in
1033
/// array `DateTimeParseInstr._test_cases`. It checks that
1034
/// a `DateTime` instance is returned, and does a few other checks.
1035
///
1036
/// [`regex::Regex.captures`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Captures.html
1037
/// [`chrono::DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
1038
/// [`chrono::NaiveDateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDateTime.html#method.parse_from_str
1039
/// [chrono `strftime`]: https://docs.rs/chrono/0.4.38/chrono/format/strftime/index.html
1040
/// [`SyslineReader`]: crate::readers::syslinereader::SyslineReader
1041
/// [`PrinterLogMessage`]: crate::printer::printers::PrinterLogMessage
1042
/// [`Line`]: crate::data::line::Line
1043
#[derive(Hash)]
1044
pub struct DateTimeParseInstr<'a> {
1045
    /// Regex pattern for [`captures`].
1046
    ///
1047
    /// [`captures`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Regex.html#method.captures
1048
    pub regex_pattern: &'a DateTimeRegex_str,
1049
    /// In what `strftime` form are the regex `regex_pattern` capture groups?
1050
    pub dtfs: DTFSSet<'a>,
1051
    /// Slice range of widest regex pattern match.
1052
    ///
1053
    /// This range is sliced from the [`Line`] and then a [`Regex`] match is
1054
    /// attempted using it. It must be at least contain the datetime string to
1055
    /// match. It may contain extra characters before or after the datetime
1056
    /// (assuming the `regex_pattern` is correct).
1057
    ///
1058
    /// Attempting a `Regex` match on a smaller subset slice of a `Line`,
1059
    /// instead of the entire `Line`, may improve run-time
1060
    /// performance.
1061
    ///
1062
    /// [`Line`]: crate::data::line::Line
1063
    /// [`Regex`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Regex.html#method.captures
1064
    pub range_regex: RangeLineIndex,
1065
    /// Capture named group first (left-most) position in `regex_pattern`.
1066
    pub cgn_first: &'a CaptureGroupName,
1067
    /// Capture named group last (right-most) position in `regex_pattern`.
1068
    pub cgn_last: &'a CaptureGroupName,
1069
    /// Hardcoded self-test cases.
1070
    #[cfg(test)]
1071
    pub _test_cases: &'a [(LineIndex, LineIndex, ymdhmsn_args, &'a str)],
1072
    /// Source code line number of declaration.
1073
    /// Only to aid humans reviewing failing tests.
1074
    pub _line_num: u32,
1075
}
1076

1077
/// Declare a [`DateTimeParseInstr`] tuple more easily.
1078
///
1079
/// `$test_cases` are not compiled into the release build.
1080
// TODO: [2023/01] replace name `DTPD` with `DTPI`
1081
#[macro_export]
1082
macro_rules! DTPD {
1083
    (
1084
        $dtr:expr,
1085
        $dtfs:expr,
1086
        $sib:literal,
1087
        $sie:literal,
1088
        $cgn_first:ident,
1089
        $cgn_last:ident,
1090
        $test_cases:expr,
1091
        $line_num:expr,
1092
    ) => {
1093
        DateTimeParseInstr {
1094
            regex_pattern: $dtr,
1095
            dtfs: $dtfs,
1096
            range_regex: RangeLineIndex {
1097
                start: $sib,
1098
                end: $sie,
1099
            },
1100
            cgn_first: $cgn_first,
1101
            cgn_last: $cgn_last,
1102
            #[cfg(test)]
1103
            _test_cases: $test_cases,
1104
            _line_num: $line_num,
1105
        }
1106
    };
1107
}
1108
// Allow easy macro import via `use s4lib::data::datetime::DTPD;`
1109
pub use DTPD;
1110

1111
// TODO: [2022/10] create macro for declaring individual test cases,
1112
//       e.g. `DTPDT!`
1113
//       adding `line()` to each test (print the line during test failure),
1114
//       Also, instantiate a `DateTime` instance to compare the result.
1115
//       Pass the datetime initial data as numbers, like
1116
//       (2000, 1, 2, 0, 0, 55, 342), that progressively sets parts of the
1117
//       `DateTime`. There is a macro in chrono that makes this easy.
1118

1119
/// Implement ordering traits to allow sorting collections of
1120
/// `DateTimeParseInstr`.
1121
///
1122
/// Only used for tests.
1123
impl Ord for DateTimeParseInstr<'_> {
1124
    fn cmp(
1,374✔
1125
        &self,
1,374✔
1126
        other: &Self,
1,374✔
1127
    ) -> std::cmp::Ordering {
1,374✔
1128
        (self.regex_pattern, &self.dtfs).cmp(&(other.regex_pattern, &other.dtfs))
1,374✔
1129
    }
1,374✔
1130
}
1131

1132
impl PartialOrd for DateTimeParseInstr<'_> {
1133
    fn partial_cmp(
1,374✔
1134
        &self,
1,374✔
1135
        other: &Self,
1,374✔
1136
    ) -> Option<std::cmp::Ordering> {
1,374✔
1137
        Some(self.cmp(other))
1,374✔
1138
    }
1,374✔
1139
}
1140

1141
impl PartialEq for DateTimeParseInstr<'_> {
1142
    fn eq(
184✔
1143
        &self,
184✔
1144
        other: &Self,
184✔
1145
    ) -> bool {
184✔
1146
        self.regex_pattern == other.regex_pattern && self.dtfs == other.dtfs
184✔
1147
    }
184✔
1148
}
1149

1150
impl Eq for DateTimeParseInstr<'_> {}
1151

1152
impl fmt::Debug for DateTimeParseInstr<'_> {
1153
    fn fmt(
235,740✔
1154
        &self,
235,740✔
1155
        f: &mut fmt::Formatter,
235,740✔
1156
    ) -> fmt::Result {
235,740✔
1157
        // regexp strings can be very long, truncate it
1158
        const MAXLEN: usize = 20;
1159
        let mut rp: String = String::with_capacity(MAXLEN + 5);
235,740✔
1160
        rp.extend(
235,740✔
1161
            self.regex_pattern
235,740✔
1162
                .chars()
235,740✔
1163
                .take(MAXLEN),
235,740✔
1164
        );
1165
        if self.regex_pattern.len() > MAXLEN {
235,740✔
1166
            rp.push('…');
235,740✔
1167
        }
235,740✔
1168
        let mut f_ = f.debug_struct("DateTimeParseInstr:");
235,740✔
1169
        f_.field("regex_pattern", &rp)
235,740✔
1170
            .field("line", &self._line_num)
235,740✔
1171
            .field("range_regex", &self.range_regex)
235,740✔
1172
            .field("cgn_first", &self.cgn_first)
235,740✔
1173
            .field("cgn_last", &self.cgn_last)
235,740✔
1174
            .field("", &self.dtfs);
235,740✔
1175

1176
        f_.finish()
235,740✔
1177
    }
235,740✔
1178
}
1179

1180
impl DateTimeParseInstr<'_> {
1181
    /// wrapper to [`DTFSSet::has_year`]
1182
    pub const fn has_year(&self) -> bool {
×
1183
        self.dtfs.has_year()
×
1184
    }
×
1185

1186
    /// wrapper to [`DTFSSet::has_year4`]
1187
    pub const fn has_year4(&self) -> bool {
8✔
1188
        self.dtfs.has_year4()
8✔
1189
    }
8✔
1190

1191
    /// wrapper to [`DTFSSet::has_tz`]
1192
    pub const fn has_tz(&self) -> bool {
×
1193
        self.dtfs.has_tz()
×
1194
    }
×
1195

1196
    /// wrapper to [`DTFSSet::has_d2`]
1197
    pub const fn has_d2(&self) -> bool {
8✔
1198
        self.dtfs.has_d2()
8✔
1199
    }
8✔
1200
}
1201

1202
// `strftime` patterns used in `DTFSSet!` declarations
1203

1204
// TODO: [2022/10/08] refactor for consistent naming of  `DTP_*` variables:
1205
//       put 'Y' in front, so it matches
1206
//       strftime specifier ordering within the value.
1207
//       e.g one is named `DTP_YmdHMSz` and starts with `%Y`, another is named
1208
//       `DTP_mdHMYZc` and also starts with `%Y`.
1209
//       e.g. variable `DTP_BdHMSYz` has value `"%Y%m%dT%H%M%S%z"`, the `%Y`
1210
//       is in front, so the variable should match the ordering, `DTP_YBdHMSz`.
1211
//       a few less human brain cycles to grok the var.
1212

1213
// TODO: [2022/10/10] refactor for consistent naming of timezone in variables
1214
//       names. Sometimes it is `DTP_YmdHMSzc` (notice `zc`) but then there
1215
//       is `DTP_bdHMSYZc` (noticed `Zc`).
1216

1217
const DTP_YmdHMSzc: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1218
const DTP_YmdHMSz: &DateTimePattern_str = "%Y%m%dT%H%M%S%z";
1219
const DTP_YmdHMSzp: &DateTimePattern_str = "%Y%m%dT%H%M%S%#z";
1220
const DTP_YmdHMSfzc: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%:z";
1221
const DTP_YmdHMSfz: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%z";
1222
const DTP_YmdHMSfzp: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%#z";
1223

1224
/// no second, chrono will set to value 0
1225
const DTP_mdHMYZc: &DateTimePattern_str = "%Y%m%dT%H%M%:z";
1226

1227
/// `%Y` `%:z` is filled
1228
const DTP_mdHMS: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1229
/// `%Y` `%:z` is filled, `%B` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1230
const DTP_BdHMS: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1231
/// `%Y` is filled, `%Z` transformed to `%:z`, `%B` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1232
const DTP_BdHMSZ: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1233
/// `%:z` is filled, `%B` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1234
const DTP_BdHMSY: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1235
/// `%Z` transformed to `%:z`, `%B` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1236
const DTP_BdHMSYZ: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1237
/// `%B` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1238
const DTP_BdHMSYzp: &DateTimePattern_str = "%Y%m%dT%H%M%S%#z";
1239

1240
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1241
const DTP_bdHMSYZc: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1242
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1243
const DTP_bdHMSyZc: &DateTimePattern_str = "%y%m%dT%H%M%S%:z";
1244
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1245
const DTP_bdHMSYZp: &DateTimePattern_str = "%Y%m%dT%H%M%S%#z";
1246
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1247
const DTP_bdHMSYZz: &DateTimePattern_str = "%Y%m%dT%H%M%S%z";
1248

1249
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1250
const DTP_bdHMSYfZc: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%:z";
1251
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1252
const DTP_bdHMSYfZp: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%#z";
1253
/// `%b` value transformed to `%m` value by [`captures_to_buffer_bytes`]
1254
const DTP_bdHMSYfZz: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%z";
1255

1256
/// `%b` value transformed to `%m` value,
1257
/// `%Z` transformed to `%:z` by [`captures_to_buffer_bytes`]
1258
const DTP_bdHMSYZ: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1259
/// `%b` value transformed to `%m` value,
1260
/// `%:z` filled by [`captures_to_buffer_bytes`]
1261
const DTP_bdHMSY: &DateTimePattern_str = "%Y%m%dT%H%M%S%:z";
1262
/// `%b` value transformed to `%m` value,
1263
/// `%:z` filled by [`captures_to_buffer_bytes`]
1264
const DTP_bdHMSYf: &DateTimePattern_str = "%Y%m%dT%H%M%S.%f%:z";
1265

1266
/// `%s` value
1267
const DTP_s: &DateTimePattern_str = "%sT";
1268
/// `%s.%f` value
1269
const DTP_sf: &DateTimePattern_str = "%sT.%f";
1270

1271
// The variable name represents what is available. The value represents it's
1272
// rearranged form using in function `captures_to_buffer_bytes`.
1273

1274
// TODO: rename these `DTFSSet` are named inconsistently.
1275
//       some are `DTFSS__YmdHMS` and others use `DTFSS_BdHMSY`.
1276
//       The lettering in the name is not consistent. Notice where the `Y` is
1277
//       placed. Other parts are inconsistent, too.
1278
//       Name these consistently.
1279

1280
const DTFSS_YmdHMS: DTFSSet = DTFSSet {
1281
    year: DTFS_Year::Y,
1282
    month: DTFS_Month::m,
1283
    day: DTFS_Day::_e_or_d,
1284
    hour: DTFS_Hour::H,
1285
    minute: DTFS_Minute::M,
1286
    second: DTFS_Second::S,
1287
    fractional: DTFS_Fractional::_none,
1288
    tz: DTFS_Tz::_fill,
1289
    epoch: DTFS_Epoch::_none,
1290
    uptime: DTFS_Uptime::_none,
1291
    pattern: DTP_YmdHMSzc,
1292
};
1293
// single-digit month, single-digit hour
1294
const DTFSS_YmsdkMS: DTFSSet = DTFSSet {
1295
    year: DTFS_Year::Y,
1296
    month: DTFS_Month::ms,
1297
    day: DTFS_Day::_e_or_d,
1298
    hour: DTFS_Hour::k,
1299
    minute: DTFS_Minute::M,
1300
    second: DTFS_Second::S,
1301
    fractional: DTFS_Fractional::_none,
1302
    tz: DTFS_Tz::_fill,
1303
    epoch: DTFS_Epoch::_none,
1304
    uptime: DTFS_Uptime::_none,
1305
    pattern: DTP_YmdHMSzc,
1306
};
1307
const DTFSS_YmdHMSz: DTFSSet = DTFSSet {
1308
    year: DTFS_Year::Y,
1309
    month: DTFS_Month::m,
1310
    day: DTFS_Day::_e_or_d,
1311
    hour: DTFS_Hour::H,
1312
    minute: DTFS_Minute::M,
1313
    second: DTFS_Second::S,
1314
    fractional: DTFS_Fractional::_none,
1315
    tz: DTFS_Tz::z,
1316
    epoch: DTFS_Epoch::_none,
1317
    uptime: DTFS_Uptime::_none,
1318
    pattern: DTP_YmdHMSz,
1319
};
1320
const DTFSS_YmdHMSzc: DTFSSet = DTFSSet {
1321
    year: DTFS_Year::Y,
1322
    month: DTFS_Month::m,
1323
    day: DTFS_Day::_e_or_d,
1324
    hour: DTFS_Hour::H,
1325
    minute: DTFS_Minute::M,
1326
    second: DTFS_Second::S,
1327
    fractional: DTFS_Fractional::_none,
1328
    tz: DTFS_Tz::zc,
1329
    epoch: DTFS_Epoch::_none,
1330
    uptime: DTFS_Uptime::_none,
1331
    pattern: DTP_YmdHMSzc,
1332
};
1333
const DTFSS_YmdHMSzp: DTFSSet = DTFSSet {
1334
    year: DTFS_Year::Y,
1335
    month: DTFS_Month::m,
1336
    day: DTFS_Day::_e_or_d,
1337
    hour: DTFS_Hour::H,
1338
    minute: DTFS_Minute::M,
1339
    second: DTFS_Second::S,
1340
    fractional: DTFS_Fractional::_none,
1341
    tz: DTFS_Tz::zp,
1342
    epoch: DTFS_Epoch::_none,
1343
    uptime: DTFS_Uptime::_none,
1344
    pattern: DTP_YmdHMSzp,
1345
};
1346
const DTFSS_YmdHMSZ: DTFSSet = DTFSSet {
1347
    year: DTFS_Year::Y,
1348
    month: DTFS_Month::m,
1349
    day: DTFS_Day::_e_or_d,
1350
    hour: DTFS_Hour::H,
1351
    minute: DTFS_Minute::M,
1352
    second: DTFS_Second::S,
1353
    fractional: DTFS_Fractional::_none,
1354
    tz: DTFS_Tz::Z,
1355
    epoch: DTFS_Epoch::_none,
1356
    uptime: DTFS_Uptime::_none,
1357
    pattern: DTP_YmdHMSz,
1358
};
1359

1360
const DTFSS_YmdHMSf: DTFSSet = DTFSSet {
1361
    year: DTFS_Year::Y,
1362
    month: DTFS_Month::m,
1363
    day: DTFS_Day::_e_or_d,
1364
    hour: DTFS_Hour::H,
1365
    minute: DTFS_Minute::M,
1366
    second: DTFS_Second::S,
1367
    fractional: DTFS_Fractional::f,
1368
    tz: DTFS_Tz::_fill,
1369
    epoch: DTFS_Epoch::_none,
1370
    uptime: DTFS_Uptime::_none,
1371
    pattern: DTP_YmdHMSfzc,
1372
};
1373
const DTFSS_YmdHMSfz: DTFSSet = DTFSSet {
1374
    year: DTFS_Year::Y,
1375
    month: DTFS_Month::m,
1376
    day: DTFS_Day::_e_or_d,
1377
    hour: DTFS_Hour::H,
1378
    minute: DTFS_Minute::M,
1379
    second: DTFS_Second::S,
1380
    fractional: DTFS_Fractional::f,
1381
    tz: DTFS_Tz::z,
1382
    epoch: DTFS_Epoch::_none,
1383
    uptime: DTFS_Uptime::_none,
1384
    pattern: DTP_YmdHMSfz,
1385
};
1386
const DTFSS_YmdHMSfzc: DTFSSet = DTFSSet {
1387
    year: DTFS_Year::Y,
1388
    month: DTFS_Month::m,
1389
    day: DTFS_Day::_e_or_d,
1390
    hour: DTFS_Hour::H,
1391
    minute: DTFS_Minute::M,
1392
    second: DTFS_Second::S,
1393
    fractional: DTFS_Fractional::f,
1394
    tz: DTFS_Tz::zc,
1395
    epoch: DTFS_Epoch::_none,
1396
    uptime: DTFS_Uptime::_none,
1397
    pattern: DTP_YmdHMSfzc,
1398
};
1399
const DTFSS_YmdHMSfzp: DTFSSet = DTFSSet {
1400
    year: DTFS_Year::Y,
1401
    month: DTFS_Month::m,
1402
    day: DTFS_Day::_e_or_d,
1403
    hour: DTFS_Hour::H,
1404
    minute: DTFS_Minute::M,
1405
    second: DTFS_Second::S,
1406
    fractional: DTFS_Fractional::f,
1407
    tz: DTFS_Tz::zp,
1408
    epoch: DTFS_Epoch::_none,
1409
    uptime: DTFS_Uptime::_none,
1410
    pattern: DTP_YmdHMSfzp,
1411
};
1412
const DTFSS_YmdHMSfZ: DTFSSet = DTFSSet {
1413
    year: DTFS_Year::Y,
1414
    month: DTFS_Month::m,
1415
    day: DTFS_Day::_e_or_d,
1416
    hour: DTFS_Hour::H,
1417
    minute: DTFS_Minute::M,
1418
    second: DTFS_Second::S,
1419
    fractional: DTFS_Fractional::f,
1420
    tz: DTFS_Tz::Z,
1421
    epoch: DTFS_Epoch::_none,
1422
    uptime: DTFS_Uptime::_none,
1423
    pattern: DTP_YmdHMSfzc,
1424
};
1425

1426
const DTFSS_mdHMS: DTFSSet = DTFSSet {
1427
    year: DTFS_Year::_fill,
1428
    month: DTFS_Month::m,
1429
    day: DTFS_Day::_e_or_d,
1430
    hour: DTFS_Hour::H,
1431
    minute: DTFS_Minute::M,
1432
    second: DTFS_Second::S,
1433
    fractional: DTFS_Fractional::_none,
1434
    tz: DTFS_Tz::_fill,
1435
    epoch: DTFS_Epoch::_none,
1436
    uptime: DTFS_Uptime::_none,
1437
    pattern: DTP_mdHMS,
1438
};
1439
const DTFSS_BdHMS: DTFSSet = DTFSSet {
1440
    year: DTFS_Year::_fill,
1441
    month: DTFS_Month::B,
1442
    day: DTFS_Day::_e_or_d,
1443
    hour: DTFS_Hour::H,
1444
    minute: DTFS_Minute::M,
1445
    second: DTFS_Second::S,
1446
    fractional: DTFS_Fractional::_none,
1447
    tz: DTFS_Tz::_fill,
1448
    epoch: DTFS_Epoch::_none,
1449
    uptime: DTFS_Uptime::_none,
1450
    pattern: DTP_BdHMS,
1451
};
1452
const DTFSS_BdHMSZ: DTFSSet = DTFSSet {
1453
    year: DTFS_Year::_fill,
1454
    month: DTFS_Month::B,
1455
    day: DTFS_Day::_e_or_d,
1456
    hour: DTFS_Hour::H,
1457
    minute: DTFS_Minute::M,
1458
    second: DTFS_Second::S,
1459
    fractional: DTFS_Fractional::_none,
1460
    tz: DTFS_Tz::Z,
1461
    epoch: DTFS_Epoch::_none,
1462
    uptime: DTFS_Uptime::_none,
1463
    pattern: DTP_BdHMSZ,
1464
};
1465
const DTFSS_BdHMSY: DTFSSet = DTFSSet {
1466
    year: DTFS_Year::Y,
1467
    month: DTFS_Month::B,
1468
    day: DTFS_Day::_e_or_d,
1469
    hour: DTFS_Hour::H,
1470
    minute: DTFS_Minute::M,
1471
    second: DTFS_Second::S,
1472
    fractional: DTFS_Fractional::_none,
1473
    tz: DTFS_Tz::_fill,
1474
    epoch: DTFS_Epoch::_none,
1475
    uptime: DTFS_Uptime::_none,
1476
    pattern: DTP_BdHMSY,
1477
};
1478
const DTFSS_BdHMSYZ: DTFSSet = DTFSSet {
1479
    year: DTFS_Year::Y,
1480
    month: DTFS_Month::B,
1481
    day: DTFS_Day::_e_or_d,
1482
    hour: DTFS_Hour::H,
1483
    minute: DTFS_Minute::M,
1484
    second: DTFS_Second::S,
1485
    fractional: DTFS_Fractional::_none,
1486
    tz: DTFS_Tz::Z,
1487
    epoch: DTFS_Epoch::_none,
1488
    uptime: DTFS_Uptime::_none,
1489
    pattern: DTP_BdHMSYZ,
1490
};
1491
const DTFSS_BdHMSYz: DTFSSet = DTFSSet {
1492
    year: DTFS_Year::Y,
1493
    month: DTFS_Month::B,
1494
    day: DTFS_Day::_e_or_d,
1495
    hour: DTFS_Hour::H,
1496
    minute: DTFS_Minute::M,
1497
    second: DTFS_Second::S,
1498
    fractional: DTFS_Fractional::_none,
1499
    tz: DTFS_Tz::z,
1500
    epoch: DTFS_Epoch::_none,
1501
    uptime: DTFS_Uptime::_none,
1502
    pattern: DTP_BdHMSYZ,
1503
};
1504
const DTFSS_BdHMSYzc: DTFSSet = DTFSSet {
1505
    year: DTFS_Year::Y,
1506
    month: DTFS_Month::B,
1507
    day: DTFS_Day::_e_or_d,
1508
    hour: DTFS_Hour::H,
1509
    minute: DTFS_Minute::M,
1510
    second: DTFS_Second::S,
1511
    fractional: DTFS_Fractional::_none,
1512
    tz: DTFS_Tz::zc,
1513
    epoch: DTFS_Epoch::_none,
1514
    uptime: DTFS_Uptime::_none,
1515
    pattern: DTP_BdHMSYZ,
1516
};
1517
const DTFSS_BdHMSYzp: DTFSSet = DTFSSet {
1518
    year: DTFS_Year::Y,
1519
    month: DTFS_Month::B,
1520
    day: DTFS_Day::_e_or_d,
1521
    hour: DTFS_Hour::H,
1522
    minute: DTFS_Minute::M,
1523
    second: DTFS_Second::S,
1524
    fractional: DTFS_Fractional::_none,
1525
    tz: DTFS_Tz::zp,
1526
    epoch: DTFS_Epoch::_none,
1527
    uptime: DTFS_Uptime::_none,
1528
    pattern: DTP_BdHMSYzp,
1529
};
1530

1531
const DTFSS_bdHMSY: DTFSSet = DTFSSet {
1532
    year: DTFS_Year::Y,
1533
    month: DTFS_Month::b,
1534
    day: DTFS_Day::_e_or_d,
1535
    hour: DTFS_Hour::H,
1536
    minute: DTFS_Minute::M,
1537
    second: DTFS_Second::S,
1538
    fractional: DTFS_Fractional::_none,
1539
    tz: DTFS_Tz::_fill,
1540
    epoch: DTFS_Epoch::_none,
1541
    uptime: DTFS_Uptime::_none,
1542
    pattern: DTP_bdHMSY,
1543
};
1544
const DTFSS_bdHMSYf: DTFSSet = DTFSSet {
1545
    year: DTFS_Year::Y,
1546
    month: DTFS_Month::b,
1547
    day: DTFS_Day::_e_or_d,
1548
    hour: DTFS_Hour::H,
1549
    minute: DTFS_Minute::M,
1550
    second: DTFS_Second::S,
1551
    fractional: DTFS_Fractional::f,
1552
    tz: DTFS_Tz::_fill,
1553
    epoch: DTFS_Epoch::_none,
1554
    uptime: DTFS_Uptime::_none,
1555
    pattern: DTP_bdHMSYf,
1556
};
1557
const DTFSS_bdHMSYZ: DTFSSet = DTFSSet {
1558
    year: DTFS_Year::Y,
1559
    month: DTFS_Month::b,
1560
    day: DTFS_Day::_e_or_d,
1561
    hour: DTFS_Hour::H,
1562
    minute: DTFS_Minute::M,
1563
    second: DTFS_Second::S,
1564
    fractional: DTFS_Fractional::_none,
1565
    tz: DTFS_Tz::Z,
1566
    epoch: DTFS_Epoch::_none,
1567
    uptime: DTFS_Uptime::_none,
1568
    pattern: DTP_bdHMSYZ,
1569
};
1570
const DTFSS_bdHMSYz: DTFSSet = DTFSSet {
1571
    year: DTFS_Year::Y,
1572
    month: DTFS_Month::b,
1573
    day: DTFS_Day::_e_or_d,
1574
    hour: DTFS_Hour::H,
1575
    minute: DTFS_Minute::M,
1576
    second: DTFS_Second::S,
1577
    fractional: DTFS_Fractional::_none,
1578
    tz: DTFS_Tz::z,
1579
    epoch: DTFS_Epoch::_none,
1580
    uptime: DTFS_Uptime::_none,
1581
    pattern: DTP_bdHMSYZz,
1582
};
1583
const DTFSS_bdHMSYzc: DTFSSet = DTFSSet {
1584
    year: DTFS_Year::Y,
1585
    month: DTFS_Month::b,
1586
    day: DTFS_Day::_e_or_d,
1587
    hour: DTFS_Hour::H,
1588
    minute: DTFS_Minute::M,
1589
    second: DTFS_Second::S,
1590
    fractional: DTFS_Fractional::_none,
1591
    tz: DTFS_Tz::zc,
1592
    epoch: DTFS_Epoch::_none,
1593
    uptime: DTFS_Uptime::_none,
1594
    pattern: DTP_bdHMSYZc,
1595
};
1596
const DTFSS_bdHMSYzp: DTFSSet = DTFSSet {
1597
    year: DTFS_Year::Y,
1598
    month: DTFS_Month::b,
1599
    day: DTFS_Day::_e_or_d,
1600
    hour: DTFS_Hour::H,
1601
    minute: DTFS_Minute::M,
1602
    second: DTFS_Second::S,
1603
    fractional: DTFS_Fractional::_none,
1604
    tz: DTFS_Tz::zp,
1605
    epoch: DTFS_Epoch::_none,
1606
    uptime: DTFS_Uptime::_none,
1607
    pattern: DTP_bdHMSYZp,
1608
};
1609
const DTFSS_bdHMSYfz: DTFSSet = DTFSSet {
1610
    year: DTFS_Year::Y,
1611
    month: DTFS_Month::b,
1612
    day: DTFS_Day::_e_or_d,
1613
    hour: DTFS_Hour::H,
1614
    minute: DTFS_Minute::M,
1615
    second: DTFS_Second::S,
1616
    fractional: DTFS_Fractional::f,
1617
    tz: DTFS_Tz::z,
1618
    epoch: DTFS_Epoch::_none,
1619
    uptime: DTFS_Uptime::_none,
1620
    pattern: DTP_bdHMSYfZz,
1621
};
1622
const DTFSS_bdHMSYfzc: DTFSSet = DTFSSet {
1623
    year: DTFS_Year::Y,
1624
    month: DTFS_Month::b,
1625
    day: DTFS_Day::_e_or_d,
1626
    hour: DTFS_Hour::H,
1627
    minute: DTFS_Minute::M,
1628
    second: DTFS_Second::S,
1629
    fractional: DTFS_Fractional::f,
1630
    tz: DTFS_Tz::zc,
1631
    epoch: DTFS_Epoch::_none,
1632
    uptime: DTFS_Uptime::_none,
1633
    pattern: DTP_bdHMSYfZc,
1634
};
1635
const DTFSS_bdHMSYfzp: DTFSSet = DTFSSet {
1636
    year: DTFS_Year::Y,
1637
    month: DTFS_Month::b,
1638
    day: DTFS_Day::_e_or_d,
1639
    hour: DTFS_Hour::H,
1640
    minute: DTFS_Minute::M,
1641
    second: DTFS_Second::S,
1642
    fractional: DTFS_Fractional::f,
1643
    tz: DTFS_Tz::zp,
1644
    epoch: DTFS_Epoch::_none,
1645
    uptime: DTFS_Uptime::_none,
1646
    pattern: DTP_bdHMSYfZp,
1647
};
1648

1649
const DTFSS_YbdHMSzc: DTFSSet = DTFSSet {
1650
    year: DTFS_Year::Y,
1651
    month: DTFS_Month::b,
1652
    day: DTFS_Day::_e_or_d,
1653
    hour: DTFS_Hour::H,
1654
    minute: DTFS_Minute::M,
1655
    second: DTFS_Second::S,
1656
    fractional: DTFS_Fractional::_none,
1657
    tz: DTFS_Tz::zc,
1658
    epoch: DTFS_Epoch::_none,
1659
    uptime: DTFS_Uptime::_none,
1660
    pattern: DTP_bdHMSYZc,
1661
};
1662
const DTFSS_YbdHMSzp: DTFSSet = DTFSSet {
1663
    year: DTFS_Year::Y,
1664
    month: DTFS_Month::b,
1665
    day: DTFS_Day::_e_or_d,
1666
    hour: DTFS_Hour::H,
1667
    minute: DTFS_Minute::M,
1668
    second: DTFS_Second::S,
1669
    fractional: DTFS_Fractional::_none,
1670
    tz: DTFS_Tz::zp,
1671
    epoch: DTFS_Epoch::_none,
1672
    uptime: DTFS_Uptime::_none,
1673
    pattern: DTP_bdHMSYZp,
1674
};
1675
const DTFSS_YbdHMSz: DTFSSet = DTFSSet {
1676
    year: DTFS_Year::Y,
1677
    month: DTFS_Month::b,
1678
    day: DTFS_Day::_e_or_d,
1679
    hour: DTFS_Hour::H,
1680
    minute: DTFS_Minute::M,
1681
    second: DTFS_Second::S,
1682
    fractional: DTFS_Fractional::_none,
1683
    tz: DTFS_Tz::z,
1684
    epoch: DTFS_Epoch::_none,
1685
    uptime: DTFS_Uptime::_none,
1686
    pattern: DTP_bdHMSYZz,
1687
};
1688
const DTFSS_YbdHMSZ: DTFSSet = DTFSSet {
1689
    year: DTFS_Year::Y,
1690
    month: DTFS_Month::b,
1691
    day: DTFS_Day::_e_or_d,
1692
    hour: DTFS_Hour::H,
1693
    minute: DTFS_Minute::M,
1694
    second: DTFS_Second::S,
1695
    fractional: DTFS_Fractional::_none,
1696
    tz: DTFS_Tz::Z,
1697
    epoch: DTFS_Epoch::_none,
1698
    uptime: DTFS_Uptime::_none,
1699
    pattern: DTP_bdHMSYZ,
1700
};
1701
const DTFSS_YbdHMS: DTFSSet = DTFSSet {
1702
    year: DTFS_Year::Y,
1703
    month: DTFS_Month::b,
1704
    day: DTFS_Day::_e_or_d,
1705
    hour: DTFS_Hour::H,
1706
    minute: DTFS_Minute::M,
1707
    second: DTFS_Second::S,
1708
    fractional: DTFS_Fractional::_none,
1709
    tz: DTFS_Tz::_fill,
1710
    epoch: DTFS_Epoch::_none,
1711
    uptime: DTFS_Uptime::_none,
1712
    pattern: DTP_bdHMSYZc,
1713
};
1714

1715
const DTFSS_ybdHMS: DTFSSet = DTFSSet {
1716
    year: DTFS_Year::y,
1717
    month: DTFS_Month::b,
1718
    day: DTFS_Day::_e_or_d,
1719
    hour: DTFS_Hour::H,
1720
    minute: DTFS_Minute::M,
1721
    second: DTFS_Second::S,
1722
    fractional: DTFS_Fractional::_none,
1723
    tz: DTFS_Tz::_fill,
1724
    epoch: DTFS_Epoch::_none,
1725
    uptime: DTFS_Uptime::_none,
1726
    pattern: DTP_bdHMSyZc,
1727
};
1728

1729
const DTFSS_YmdHM: DTFSSet = DTFSSet {
1730
    year: DTFS_Year::Y,
1731
    month: DTFS_Month::m,
1732
    day: DTFS_Day::_e_or_d,
1733
    hour: DTFS_Hour::H,
1734
    minute: DTFS_Minute::M,
1735
    second: DTFS_Second::_none,
1736
    fractional: DTFS_Fractional::_none,
1737
    tz: DTFS_Tz::_fill,
1738
    epoch: DTFS_Epoch::_none,
1739
    uptime: DTFS_Uptime::_none,
1740
    pattern: DTP_mdHMYZc,
1741
};
1742

1743
const DTFSS_s: DTFSSet = DTFSSet {
1744
    year: DTFS_Year::_none,
1745
    month: DTFS_Month::_none,
1746
    day: DTFS_Day::_none,
1747
    hour: DTFS_Hour::_none,
1748
    minute: DTFS_Minute::_none,
1749
    second: DTFS_Second::_none,
1750
    fractional: DTFS_Fractional::_none,
1751
    tz: DTFS_Tz::_none,
1752
    epoch: DTFS_Epoch::s,
1753
    uptime: DTFS_Uptime::_none,
1754
    pattern: DTP_s,
1755
};
1756

1757
/// for epoch syslog lines
1758
const DTFSS_sf: DTFSSet = DTFSSet {
1759
    year: DTFS_Year::_none,
1760
    month: DTFS_Month::_none,
1761
    day: DTFS_Day::_none,
1762
    hour: DTFS_Hour::_none,
1763
    minute: DTFS_Minute::_none,
1764
    second: DTFS_Second::_none,
1765
    fractional: DTFS_Fractional::f,
1766
    tz: DTFS_Tz::_none,
1767
    epoch: DTFS_Epoch::s,
1768
    uptime: DTFS_Uptime::_none,
1769
    pattern: DTP_sf,
1770
};
1771

1772
/// for `dmesg` syslog lines, e.g.
1773
/// `[    0.4074] kernel: Linux version 5.15.0-47-generic (buildd@lcy02-amd64-060)`
1774
pub(crate) const DTFSS_u: DTFSSet = DTFSSet {
1775
    year: DTFS_Year::_none,
1776
    month: DTFS_Month::_none,
1777
    day: DTFS_Day::_none,
1778
    hour: DTFS_Hour::_none,
1779
    minute: DTFS_Minute::_none,
1780
    second: DTFS_Second::_none,
1781
    fractional: DTFS_Fractional::f,
1782
    tz: DTFS_Tz::_none,
1783
    epoch: DTFS_Epoch::_none,
1784
    uptime: DTFS_Uptime::u,
1785
    pattern: DTP_sf,
1786
};
1787

1788
/// to aid testing
1789
// check `DTP_ALL` has all `DTP_` vars
1790
//
1791
//     grep -Fe ' DTP_' ./src/data/datetime.rs  | grep const | grep -oEe 'DTP_[[:alnum:]]+' | sed 's/$/,/'
1792
//
1793
#[doc(hidden)]
1794
#[cfg(test)]
1795
pub(crate) const DTP_ALL: &[&DateTimePattern_str] = &[
1796
    DTP_YmdHMSzc,
1797
    DTP_YmdHMSz,
1798
    DTP_YmdHMSzp,
1799
    DTP_YmdHMSfzc,
1800
    DTP_YmdHMSfz,
1801
    DTP_YmdHMSfzp,
1802
    DTP_BdHMS,
1803
    DTP_BdHMSZ,
1804
    DTP_BdHMSY,
1805
    DTP_BdHMSYZ,
1806
    DTP_BdHMSYzp,
1807
    DTP_BdHMS,
1808
    DTP_BdHMSZ,
1809
    DTP_BdHMSY,
1810
    DTP_BdHMSYZ,
1811
];
1812

1813
// `regex::Captures` capture group names
1814

1815
/// corresponds to `strftime` specifier `%Y`
1816
const CGN_YEAR: &CaptureGroupName = "year";
1817
/// corresponds to `strftime` specifier `%m`
1818
const CGN_MONTH: &CaptureGroupName = "month";
1819
/// corresponds to `strftime` specifier `%d`
1820
const CGN_DAY: &CaptureGroupName = "day";
1821
/// corresponds to `strftime` specifier `%a`
1822
const CGN_DAYa: &CaptureGroupName = "dayIgnore";
1823
/// corresponds to `strftime` specifier `%H`
1824
const CGN_HOUR: &CaptureGroupName = "hour";
1825
/// corresponds to `strftime` specifier `%M`
1826
const CGN_MINUTE: &CaptureGroupName = "minute";
1827
/// corresponds to `strftime` specifier `%S`
1828
const CGN_SECOND: &CaptureGroupName = "second";
1829
/// corresponds to `strftime` specifier `%f`
1830
const CGN_FRACTIONAL: &CaptureGroupName = "fractional";
1831
/// corresponds to `strftime` specifier `%Z`, `%z`, `%:z`, `%#z`
1832
const CGN_TZ: &CaptureGroupName = "tz";
1833
/// special case: Unix epoch seconds
1834
const CGN_EPOCH: &CaptureGroupName = "epoch";
1835
/// special case: `dmesg` uptime
1836
const CGN_UPTIME: &CaptureGroupName = "uptime";
1837

1838
/// all capture group names, for testing
1839
#[doc(hidden)]
1840
#[cfg(test)]
1841
pub(crate) const CGN_ALL: [&CaptureGroupName; 11] = [
1842
    CGN_YEAR,
1843
    CGN_MONTH,
1844
    CGN_DAY,
1845
    CGN_DAYa,
1846
    CGN_HOUR,
1847
    CGN_MINUTE,
1848
    CGN_SECOND,
1849
    CGN_FRACTIONAL,
1850
    CGN_TZ,
1851
    CGN_UPTIME,
1852
    CGN_EPOCH,
1853
];
1854

1855
// saved rust playground for quick testing regex patterns
1856
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=00460112beb2a6d078d6bbba72557574
1857

1858
// Names used in the upcoming capture group pattern variable values (`CGP_*`)
1859
// *MUST* match the values of previous capture group name values (`CGN_*`).
1860

1861
/// Regex capture group pattern for `strftime` year specifier `%Y`, as
1862
/// four decimal number characters.
1863
pub const CGP_YEAR: &CaptureGroupPattern = r"(?P<year>1969|19[789][[:digit:]]|20[[:digit:]]{2})";
1864
/// Regex capture group pattern for `strftime` year specifier `%y`, as
1865
/// two decimal number characters.
1866
pub const CGP_YEARy: &CaptureGroupPattern = r"(?P<year>[[:digit:]]{2})";
1867
/// Regex capture group pattern for `strftime` month specifier `%m`, but using
1868
/// single digit month numbers `"1"` to `"12"`.
1869
pub const CGP_MONTHms: &CaptureGroupPattern = r"(?P<month>1|2|3|4|5|6|7|8|9|10|11|12)";
1870
/// Regex capture group pattern for `strftime` month specifier `%m`,
1871
/// month numbers `"01"` to `"12"`.
1872
pub const CGP_MONTHm: &CaptureGroupPattern = r"(?P<month>01|02|03|04|05|06|07|08|09|10|11|12)";
1873
/// Regex capture group pattern for `strftime` month specifier `%b`,
1874
/// month name abbreviated to three characters, e.g. `Jan`.
1875
pub const CGP_MONTHb: &CaptureGroupPattern = r"(?P<month>(jan|Jan|JAN|feb|Feb|FEB|mar|Mar|MAR|apr|Apr|APR|may|May|MAY|jun|Jun|JUN|jul|Jul|JUL|aug|Aug|AUG|sep|Sep|SEP|oct|Oct|OCT|nov|Nov|NOV|dec|Dec|DEC)[\.]?)";
1876
/// Regex capture group pattern for `strftime` month specifier `%B`,
1877
/// month name long, e.g. `January`.
1878
pub const CGP_MONTHB: &CaptureGroupPattern = r"(?P<month>january|January|JANUARY|february|February|FEBRUARY|march|March|MARCH|april|April|APRIL|may|May|MAY|june|June|JUNE|july|July|JULY|august|August|AUGUST|september|September|SEPTEMBER|october|October|OCTOBER|november|November|NOVEMBER|december|December|DECEMBER)";
1879
/// Regex capture group pattern for `strftime` month specifier `%B` and `%b`,
1880
/// e.g. `January` or `Jan`.
1881
pub const CGP_MONTHBb: &CaptureGroupPattern = r"(?P<month>january|January|JANUARY|jan[\.]?|Jan[\.]?|JAN[\.]?|february|February|FEBRUARY|feb[\.]?|Feb[\.]?|FEB[\.]?|march|March|MARCH|mar[\.]?|Mar[\.]?|MAR[\.]?|april|April|APRIL|apr[\.]?|Apr[\.]?|APR[\.]?|may|May|MAY|june|June|JUNE|jun[\.]?|Jun[\.]?|JUN[\.]?|july|July|JULY|jul[\.]?|Jul[\.]?|JUL[\.]?|august|August|AUGUST|aug[\.]?|Aug[\.]?|AUG[\.]?|september|September|SEPTEMBER|sep[\.]?|Sep[\.]?|SEP[\.]?|october|October|OCTOBER|oct[\.]?|Oct[\.]?|OCT[\.]?|november|November|NOVEMBER|nov[\.]?|Nov[\.]?|NOV[\.]?|december|December|DECEMBER|dec[\.]?|Dec[\.]?|DEC[\.]?)";
1882
/// Regex capture group pattern for `strftime` day specifier `%d`,
1883
/// number day of month with leading zero, e.g. `"02"` or `"31"`.
1884
/// Regex capture group pattern for `strftime` day specifier `%e`,
1885
/// number day of month, 1 to 31, e.g. `"2"` or `"31"`.
1886
/// Transformed to equivalent `%d` form within function
1887
/// `captures_to_buffer_bytes` (i.e. `'0'` is prepended if necessary).
1888
pub const CGP_DAYde: &CaptureGroupPattern = r"(?P<day>01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|1|2|3|4|5|6|7|8|9| 1| 2| 3| 4| 5| 6| 7| 8| 9)";
1889
/// Regex capture group pattern for `strftime` day specifier `%a`,
1890
/// named day of week, either long name or abbreviated three character name,
1891
/// e.g. `"Mon"`.
1892
pub const CGP_DAYa3: &RegexPattern = r"(?P<dayIgnore>(mon|Mon|MON|tue|Tue|TUE|wed|Wed|WED|thu|Thu|THU|fri|Fri|FRI|sat|Sat|SAT|sun|Sun|SUN)[\.]?)";
1893
/// Regex capture group pattern for `strftime` day specifier `%a`,
1894
/// named day of week, either long name or abbreviated three character name,
1895
/// e.g. `"Mon"` or `"Monday"`.
1896
pub const CGP_DAYa: &RegexPattern = r"(?P<dayIgnore>monday|Monday|MONDAY|mon[\.]?|Mon[\.]?|MON[\.]?|tuesday|Tuesday|TUESDAY|tue[\.]?|Tue[\.]?|TUE[\.]?|wednesday|Wednesday|WEDNESDAY|wed[\.]?|Wed[\.]?|WED[\.]?|thursday|Thursday|THURSDAY|thu[\.]?|Thu[\.]?|THU[\.]?|friday|Friday|FRIDAY|fri[\.]?|Fri[\.]?|FRI[\.]?|saturday|Saturday|SATURDAY|sat[\.]?|Sat[\.]?|SAT[\.]?|sunday|Sunday|SUNDAY|sun[\.]?|Sun[\.]?|SUN[\.]?)";
1897
/// Regex capture group pattern for `strftime` hour specifier `%H`, 00 to 24.
1898
pub const CGP_HOUR: &CaptureGroupPattern = r"(?P<hour>00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24)";
1899
/// Regex capture group pattern for `strftime` hour specifier `%H`, 0 to 24.
1900
/// single-digit
1901
pub const CGP_HOURs: &CaptureGroupPattern = r"(?P<hour>0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24)";
1902
/// Regex capture group pattern for `strftime` hour specifier `%h`, 1 to 12.
1903
pub const CGP_HOURh: &CaptureGroupPattern = r"(?P<hour>|1|2|3|4|5|6|7|8|9|10|11|12)";
1904
/// Regex capture group pattern for `strftime` minute specifier `%M`, 00 to 59.
1905
pub const CGP_MINUTE: &CaptureGroupPattern = r"(?P<minute>[012345][[:digit:]])";
1906
/// Regex capture group pattern for `strftime` second specifier `%S`, 00 to 60.
1907
/// Includes leap second "60".
1908
pub const CGP_SECOND: &CaptureGroupPattern = r"(?P<second>[012345][[:digit:]]|60)";
1909
/// Regex capture group pattern for `strftime` fractional specifier `%f`.
1910
/// Matches all `strftime` specifiers `%f`, `%3f`, `%6f`, and `%9f`, a sequence
1911
/// of decimal number characters.
1912
///
1913
/// Function `datetime_parse_from_str` will match with strftime specifier `%f`.
1914
/// Function `captures_to_buffer_bytes` will fill a too short or too long
1915
/// fractionals to 9 digits to match the correct precision.
1916
/// For example, fractional data "123" is transformed to "123000000" in
1917
/// function `captures_to_buffer_bytes`. Then it is parsed by
1918
/// `datetime_parse_from_str` using `%f` specifier.
1919
pub const CGP_FRACTIONAL: &CaptureGroupPattern = r"(?P<fractional>[[:digit:]]{1,9})";
1920
/// Like [`CGP_FRACTIONAL`] but only matches 2 digits, `%2f`.
1921
pub const CGP_FRACTIONAL23: &CaptureGroupPattern = r"(?P<fractional>[[:digit:]]{2,3})";
1922
/// Like [`CGP_FRACTIONAL`] but only matches 3 digits, `%3f`.
1923
pub const CGP_FRACTIONAL3: &CaptureGroupPattern = r"(?P<fractional>[[:digit:]]{3})";
1924
/// Like [`CGP_FRACTIONAL`] but only matches 6 digits, `%6f`.
1925
pub const CGP_FRACTIONAL6: &CaptureGroupPattern = r"(?P<fractional>[[:digit:]]{6})";
1926
/// Like [`CGP_FRACTIONAL`] but only matches 9 digits, `%9f`.
1927
pub const CGP_FRACTIONAL9: &CaptureGroupPattern = r"(?P<fractional>[[:digit:]]{9})";
1928
/// Like [`CGP_FRACTIONAL`] but matches 3 to 9 digits, `%f`.
1929
pub const CGP_FRACTIONAL39: &CaptureGroupPattern = r"(?P<fractional>[[:digit:]]{3,9})";
1930
/// Regex capture group pattern for dmesg uptime seconds.
1931
/// 9 digits of seconds covers uptime of 11574 days,
1932
/// e.g. `[    1.407] kernel: Linux version 5.15.0-47-generic (buildd@lcy02-amd64-060)`
1933
/// the `1`
1934
pub const CGP_UPTIME: &CaptureGroupPattern = r"(?P<uptime>[[:digit:]]{1,9})";
1935
/// Regex capture group pattern for dmesg uptime seconds + fractional,
1936
/// e.g. from `[    1.407456] kernel: Linux version 5.15.0-47-generic (buildd@lcy02-amd64-060)`
1937
/// the `1.407456`
1938
pub const CGP_UPTIME_F: &CaptureGroupPattern = concatcp!(CGP_UPTIME, r"\.", CGP_FRACTIONAL39);
1939
/// Shorter version of [`CGP_UPTIME_F`], for dmesg uptime seconds + fractional,
1940
pub const CGP_UPTIME_F23: &CaptureGroupPattern = concatcp!(CGP_UPTIME, r"\.", CGP_FRACTIONAL23);
1941
/// Regex capture group pattern for Unix epoch in seconds
1942
/// limited to datetimes from year 2000 and afterward.
1943
/// Low numbers are likely to be errant matches, e.g. random string "55"
1944
/// is most likely not meant to signify an epoch datetime.
1945
///
1946
/// Datetime _2000-01-01 00:00:00_, value `946684800`, is a reasonable past datetime limit.
1947
///
1948
/// Datetime _2038-01-19 03:14:06_, value `2147483647`, is a reasonable high datetime limit.
1949
pub const CGP_EPOCH: &CaptureGroupPattern = r"(?P<epoch>9[[:digit:]]{8}|[12][[:digit:]]{9})";
1950

1951
/// for testing
1952
#[doc(hidden)]
1953
#[cfg(test)]
1954
pub(crate) const CGP_MONTH_ALL: &[&CaptureGroupPattern] = &[
1955
    CGP_MONTHm,
1956
    CGP_MONTHms,
1957
    CGP_MONTHb,
1958
    CGP_MONTHB,
1959
    CGP_MONTHBb,
1960
];
1961

1962
/// for testing
1963
#[doc(hidden)]
1964
#[cfg(test)]
1965
pub(crate) const CGP_DAY_ALL: &[&CaptureGroupPattern] = &[
1966
    CGP_DAYde,
1967
    CGP_DAYa3,
1968
    CGP_DAYa,
1969
];
1970

1971
/// for testing
1972
#[doc(hidden)]
1973
#[cfg(test)]
1974
pub(crate) const CGP_HOUR_ALL: &[&CaptureGroupPattern] = &[
1975
    CGP_HOUR,
1976
    CGP_HOURs,
1977
    CGP_HOURh,
1978
];
1979

1980
// Regarding timezone formatting, ISO 8601 allows Unicode "minus sign".
1981
// See https://en.wikipedia.org/w/index.php?title=ISO_8601&oldid=1114291504#Time_offsets_from_UTC
1982
// Unicode "minus sign" will be replaced with ASCII "hyphen-minus" for
1983
// processing by chrono `DateTime::parse_from_str`.
1984
// See https://github.com/chronotope/chrono/issues/835
1985

1986
/// Unicode MINUS SIGN `U+2212`
1987
const MINUS_SIGN: &[u8] = "−".as_bytes();
1988
/// Unicode/ASCII HYPHEN-MINUS `U+002D`
1989
const HYPHEN_MINUS: &[u8] = "-".as_bytes();
1990

1991
// The "|" operator will match in order. And pattern ordering matters.
1992
// So patterns should attempt longer matches first, e.g.
1993
//   Pattern "PETT" should occur before "PET"; "PETT|PET"
1994

1995
/// `strftime` specifier `%z` e.g. `"+0930"`
1996
const CGP_TZz: &CaptureGroupPattern = r"(?P<tz>[\+\-−][012][[:digit:]]{3})";
1997
/// `strftime` specifier `%:z` e.g. `"+09:30"`
1998
const CGP_TZzc: &CaptureGroupPattern = r"(?P<tz>[\+\-−][012][[:digit:]]:[[:digit:]]{2})";
1999
/// `strftime` specifier `%#z` e.g. `"+09"`
2000
const CGP_TZzp: &CaptureGroupPattern = r"(?P<tz>[\+\-−][012][[:digit:]])";
2001
/// `strftime` specifier `%Z` e.g. `"ACST"`, lowercase also allowed
2002
/// ordering is important for more complete matches,
2003
/// e.g. `"PETT"` should occur before `"PET"`
2004
pub(crate) const CGP_TZZ: &CaptureGroupPattern = "(?P<tz>\
2005
ACDT|ACST|ACT|ACWST|ADT|AEDT|AEST|AET|AFT|AKDT|AKST|ALMT|AMST|AMT|ANAT|AQTT|ART|AST|AWST|AZOST|AZOT|AZT|BIOT|BIT|BNT|BOT|BRST|BRT|BST|BTT|CAT|CCT|CDT|CEST|CET|CHADT|CHAST|CHOST|CHOT|CHST|CHUT|CIST|CKT|CLST|CLT|COST|COT|CST|CT|CVT|CWST|CXT|DAVT|DDUT|DFT|EASST|EAST|EAT|ECT|EDT|EEST|EET|EGST|EGT|EST|ET|FET|FJT|FKST|FKT|FNT|GALT|GAMT|GET|GFT|GILT|GIT|GMT|GST|GYT|HAEC|HDT|HKT|HMT|HOVST|HOVT|HST|ICT|IDLW|IDT|IOT|IRDT|IRKT|IRST|IST|JST|KALT|KGT|KOST|KRAT|KST|LHST|LINT|MAGT|MART|MAWT|MDT|MEST|MET|MHT|MIST|MIT|MMT|MSK|MST|MUT|MVT|MYT|NCT|NDT|NFT|NOVT|NPT|NST|NT|NUT|NZDT|NZST|OMST|ORAT|PDT|PETT|PET|PGT|PHOT|PHST|PHT|PKT|PMDT|PMST|PONT|PST|PWT|PYST|PYT|RET|ROTT|SAKT|SAMT|SAST|SBT|SCT|SDT|SGT|SLST|SRET|SRT|SST|SYOT|TAHT|TFT|THA|TJT|TKT|TLT|TMT|TOT|TRT|TVT|ULAST|ULAT|UTC|UT|UYST|UYT|UZT|VET|VLAT|VOLT|VOST|VUT|WAKT|WAST|WAT|WEST|WET|WGST|WGT|WIB|WITA|WIT|WST|YAKT|YEKT|ZULU|Z|\
2006
acdt|acst|act|acwst|adt|aedt|aest|aet|aft|akdt|akst|almt|amst|amt|anat|aqtt|art|ast|awst|azost|azot|azt|biot|bit|bnt|bot|brst|brt|bst|btt|cat|cct|cdt|cest|cet|chadt|chast|chost|chot|chst|chut|cist|ckt|clst|clt|cost|cot|cst|ct|cvt|cwst|cxt|davt|ddut|dft|easst|east|eat|ect|edt|eest|eet|egst|egt|est|et|fet|fjt|fkst|fkt|fnt|galt|gamt|get|gft|gilt|git|gmt|gst|gyt|haec|hdt|hkt|hmt|hovst|hovt|hst|ict|idlw|idt|iot|irdt|irkt|irst|ist|jst|kalt|kgt|kost|krat|kst|lhst|lint|magt|mart|mawt|mdt|mest|met|mht|mist|mit|mmt|msk|mst|mut|mvt|myt|nct|ndt|nft|novt|npt|nst|nt|nut|nzdt|nzst|omst|orat|pdt|pett|pet|pgt|phot|phst|pht|pkt|pmdt|pmst|pont|pst|pwt|pyst|pyt|ret|rott|sakt|samt|sast|sbt|sct|sdt|sgt|slst|sret|srt|sst|syot|taht|tft|tha|tjt|tkt|tlt|tmt|tot|trt|tvt|ulast|ulat|utc|ut|uyst|uyt|uzt|vet|vlat|volt|vost|vut|wakt|wast|wat|west|wet|wgst|wgt|wib|wita|wit|wst|yakt|yekt|zulu|z\
2007
)";
2008
/// same as `CGP_TZZ` but only uppercase
2009
pub(crate) const CGP_TZZ_U: &CaptureGroupPattern = "(?P<tz>\
2010
ACDT|ACST|ACT|ACWST|ADT|AEDT|AEST|AET|AFT|AKDT|AKST|ALMT|AMST|AMT|ANAT|AQTT|ART|AST|AWST|AZOST|AZOT|AZT|BIOT|BIT|BNT|BOT|BRST|BRT|BST|BTT|CAT|CCT|CDT|CEST|CET|CHADT|CHAST|CHOST|CHOT|CHST|CHUT|CIST|CKT|CLST|CLT|COST|COT|CST|CT|CVT|CWST|CXT|DAVT|DDUT|DFT|EASST|EAST|EAT|ECT|EDT|EEST|EET|EGST|EGT|EST|ET|FET|FJT|FKST|FKT|FNT|GALT|GAMT|GET|GFT|GILT|GIT|GMT|GST|GYT|HAEC|HDT|HKT|HMT|HOVST|HOVT|HST|ICT|IDLW|IDT|IOT|IRDT|IRKT|IRST|IST|JST|KALT|KGT|KOST|KRAT|KST|LHST|LINT|MAGT|MART|MAWT|MDT|MEST|MET|MHT|MIST|MIT|MMT|MSK|MST|MUT|MVT|MYT|NCT|NDT|NFT|NOVT|NPT|NST|NT|NUT|NZDT|NZST|OMST|ORAT|PDT|PETT|PET|PGT|PHOT|PHST|PHT|PKT|PMDT|PMST|PONT|PST|PWT|PYST|PYT|RET|ROTT|SAKT|SAMT|SAST|SBT|SCT|SDT|SGT|SLST|SRET|SRT|SST|SYOT|TAHT|TFT|THA|TJT|TKT|TLT|TMT|TOT|TRT|TVT|ULAST|ULAT|UTC|UT|UYST|UYT|UZT|VET|VLAT|VOLT|VOST|VUT|WAKT|WAST|WAT|WEST|WET|WGST|WGT|WIB|WITA|WIT|WST|YAKT|YEKT|ZULU|Z\
2011
)";
2012

2013
/// for testing
2014
#[doc(hidden)]
2015
#[cfg(test)]
2016
pub(crate) const CGP_TZ_ALL: &[&CaptureGroupPattern] = &[
2017
    CGP_TZz, CGP_TZzc, CGP_TZzp, CGP_TZZ, CGP_TZZ_U,
2018
];
2019

2020
/// no alphabetic or line end, helper to `CGP_TZZ`
2021
const RP_NOALPHA: &RegexPattern = r"([[:^alpha:]]|$)";
2022

2023
/// no alphabetic or line begin, helper to `CGP_TZZ` and `CGP_YEAR`
2024
const RP_NOALPHAb: &RegexPattern = r"([[:^alpha:]]|^)";
2025

2026
/// no alphanumeric or line end, helper to `CGP_TZZ` and `CGP_YEAR`
2027
const RP_NOALNUM: &RegexPattern = r"([[:^alnum:]]|$)";
2028

2029
/// no alphanumeric or line begin, helper to `CGP_TZZ` and `CGP_YEAR`
2030
const RP_NOALNUMb: &RegexPattern = r"(^|[[:^alnum:]])";
2031

2032
/// no alphanumeric plus minus or line end, helper to `CGP_TZZ` and `CGP_YEAR`
2033
const RP_NOALNUMpm: &RegexPattern = r"([^[[:alnum:]]\+\-]|$)";
2034

2035
/// no numeric or line end, helper to `CGP_TZZ` and `CGP_YEAR`
2036
const RP_NODIGIT: &RegexPattern = r"([[:^digit:]]|$)";
2037

2038
/// no numeric or line begin, helper to `CGP_TZZ` and `CGP_YEAR`
2039
const RP_NODIGITb: &RegexPattern = r"([[:^digit:]]|^)";
2040

2041
/// one or more digits
2042
pub const RP_DIGITS: &RegexPattern = "[[:digit:]]+";
2043

2044
/// one to three digits
2045
pub const RP_DIGITS3: &RegexPattern = r"[[:digit:]]{1,3}";
2046

2047
/// field name header for date in RFC 2822 line-oriented message,
2048
/// not matching wacky-case variants like `dAte:`
2049
pub const RP_RFC2822_DATE: &RegexPattern = "(date|Date|DATE):";
2050

2051
/// All named timezone abbreviations, maps all chrono strftime `%Z` values
2052
/// (e.g. `"EDT"`) to equivalent `%:z` value (e.g. `"-04:00"`).
2053
/// Crate `chrono` does not parse named timezone. This mapping bridges that gap.
2054
///
2055
/// _Super Speedy Syslog Searcher_ attempts to be more lenient than chrono
2056
/// about matching named abbreviated timezones, e.g. `"EDT"`.
2057
/// Chrono provides `%Z` strftime specifier
2058
/// yet rejects named timezones when passed to [`DateTime::parse_from_str`].
2059
/// `MAP_TZZ_TO_TZz` provides the necessary mapping.
2060
///
2061
/// However, due to duplicate timezone names, some valid timezone names
2062
/// will result in the default timezone. For example, there are three named
2063
/// timezones `"IST"` that refer to different timezone offsets. If `"IST"` is
2064
/// parsed as a timezone in a sysline then the resultant value will be the
2065
/// default timezone offset value, e.g. the value passed to `--tz-offset`.
2066
/// See the opening paragraph in [_List of time zone abbreviations_].
2067
///
2068
/// In this structure, ambiguous timezone names have their values set to empty
2069
/// string, e.g. `"SST"` maps to `""`. See [Issue #59].
2070
///
2071
/// The listing of timezone abbreviations and values can be scraped from
2072
/// Wikipedia with this code snippet:
2073
///
2074
/// ```text
2075
/// $ curl "https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations" \
2076
///     | grep -Ee '^<td>[[:upper:]]{2,4}</td>' \
2077
///     | grep -oEe '[[:upper:]]{2,4}' \
2078
///     | sort \
2079
///     | uniq \
2080
///     | sed -Ee ':a;N;$!ba;s/\n/|/g'
2081
///
2082
/// $ curl "https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations" \
2083
///     | rg -or '$1 $2' -e '^<td>([[:upper:]]{2,5})</td>' -e '^<td data-sort-value.*>UTC(.*)</a>' \
2084
///     | sed -e '/^$/d' \
2085
///     | rg -r '("$1", ' -e '^([[:upper:]]{2,5})' -C5 \
2086
///     | rg -r '"$1"), ' -e '^[[:blank:]]*([[:print:]−±+]*[[:digit:]]{1,4}.*$)' -C5 \
2087
///     | rg -r '"$1:00"' -e '"(.?[[:digit:]][[:digit:]])"' -C5 \
2088
///     | sed -e 's/\n"/"/g' -e 'N;s/\n/ /' -e 's/−/-/g' -e 's/±/-/g' \
2089
///     | tr -s ' '
2090
/// ```
2091
///
2092
/// See also:
2093
/// - Applicable tz offsets <https://en.wikipedia.org/wiki/List_of_UTC_offsets>
2094
/// - Applicable tz abbreviations <https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations>
2095
///
2096
/// [Issue #59]: https://github.com/jtmoon79/super-speedy-syslog-searcher/issues/59
2097
/// [_List of time zone abbreviations_]: https://en.wikipedia.org/w/index.php?title=List_of_time_zone_abbreviations&oldid=1106679802
2098
/// [`DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/format/strftime/#fn7
2099
// TODO: why not map directly to a `FixedOffset` to skip having chrono do another
2100
//       step of translating from `str` to `FixedOffset`?
2101
pub static MAP_TZZ_TO_TZz: PhfMap<&'static str, &'static str> = phf_map! {
2102
        // uppercase
2103
        "ACDT" => "+10:30",
2104
        "ACST" => "+09:30",
2105
        "ACT" => "",
2106
        //"ACT" => "-05:00",
2107
        //"ACT" => "+08:00",
2108
        "ACWST" => "+08:45",
2109
        "ADT" => "-03:00",
2110
        "AEDT" => "+11:00",
2111
        "AEST" => "+10:00",
2112
        "AET" => "+11:00",
2113
        "AFT" => "+04:30",
2114
        "AKDT" => "-08:00",
2115
        "AKST" => "-09:00",
2116
        "ALMT" => "+06:00",
2117
        "AMST" => "-03:00",
2118
        "AMT" => "",
2119
        //"AMT" => "-04:00",
2120
        //"AMT" => "+04:00",
2121
        "ANAT" => "+12:00",
2122
        "AQTT" => "+05:00",
2123
        "ART" => "-03:00",
2124
        "AST" => "",
2125
        //"AST" => "+03:00",
2126
        //"AST" => "-04:00",
2127
        "AWST" => "+08:00",
2128
        "AZOST" => "+00:00",
2129
        "AZOT" => "-01:00",
2130
        "AZT" => "+04:00",
2131
        "BNT" => "+08:00",
2132
        "BIOT" => "+06:00",
2133
        "BIT" => "-12:00",
2134
        "BOT" => "-04:00",
2135
        "BRST" => "-02:00",
2136
        "BRT" => "-03:00",
2137
        "BST" => "",
2138
        //"BST" => "+06:00",
2139
        //"BST" => "+11:00",
2140
        //"BST" => "+01:00",
2141
        "BTT" => "+06:00",
2142
        "CAT" => "+02:00",
2143
        "CCT" => "+06:30",
2144
        "CDT" => "",
2145
        //"CDT" => "-05:00",
2146
        //"CDT" => "-04:00",
2147
        "CEST" => "+02:00",
2148
        "CET" => "+01:00",
2149
        "CHADT" => "+13:45",
2150
        "CHAST" => "+12:45",
2151
        "CHOT" => "+08:00",
2152
        "CHOST" => "+09:00",
2153
        "CHST" => "+10:00",
2154
        "CHUT" => "+10:00",
2155
        "CIST" => "-08:00",
2156
        "CKT" => "-10:00",
2157
        "CLST" => "-03:00",
2158
        "CLT" => "-04:00",
2159
        "COST" => "-04:00",
2160
        "COT" => "-05:00",
2161
        "CST" => "",
2162
        //"CST" => "-06:00",
2163
        //"CST" => "+08:00",
2164
        //"CST" => "-05:00",
2165
        "CT" => "-05:00",
2166
        "CVT" => "-01:00",
2167
        "CWST" => "+08:45",
2168
        "CXT" => "+07:00",
2169
        "DAVT" => "+07:00",
2170
        "DDUT" => "+10:00",
2171
        "DFT" => "+01:00",
2172
        "EASST" => "-05:00",
2173
        "EAST" => "-06:00",
2174
        "EAT" => "+03:00",
2175
        "ECT" => "",
2176
        //"ECT" => "-04:00",
2177
        //"ECT" => "-05:00",
2178
        "EDT" => "-04:00",
2179
        "EEST" => "+03:00",
2180
        "EET" => "+02:00",
2181
        "EGST" => "-00:00",
2182
        "EGT" => "-01:00",
2183
        "EST" => "-05:00",
2184
        "ET" => "-04:00",
2185
        "FET" => "+03:00",
2186
        "FJT" => "+12:00",
2187
        "FKST" => "-03:00",
2188
        "FKT" => "-04:00",
2189
        "FNT" => "-02:00",
2190
        "GALT" => "-06:00",
2191
        "GAMT" => "-09:00",
2192
        "GET" => "+04:00",
2193
        "GFT" => "-03:00",
2194
        "GILT" => "+12:00",
2195
        "GIT" => "-09:00",
2196
        "GMT" => "-00:00",
2197
        "GST" => "",
2198
        //"GST" => "-02:00",
2199
        //"GST" => "+04:00",
2200
        "GYT" => "-04:00",
2201
        "HDT" => "-09:00",
2202
        "HAEC" => "+02:00",
2203
        "HST" => "-10:00",
2204
        "HKT" => "+08:00",
2205
        "HMT" => "+05:00",
2206
        "HOVST" => "+08:00",
2207
        "HOVT" => "+07:00",
2208
        "ICT" => "+07:00",
2209
        "IDLW" => "-12:00",
2210
        "IDT" => "+03:00",
2211
        "IOT" => "+03:00",
2212
        "IRDT" => "+04:30",
2213
        "IRKT" => "+08:00",
2214
        "IRST" => "+03:30",
2215
        "IST" => "",
2216
        //"IST" => "+05:30",
2217
        //"IST" => "+01:00",
2218
        //"IST" => "+02:00",
2219
        "JST" => "+09:00",
2220
        "KALT" => "+02:00",
2221
        "KGT" => "+06:00",
2222
        "KOST" => "+11:00",
2223
        "KRAT" => "+07:00",
2224
        "KST" => "+09:00",
2225
        "LHST" => "",
2226
        //"LHST" => "+10:30",
2227
        //"LHST" => "+11:00",
2228
        "LINT" => "+14:00",
2229
        "MAGT" => "+12:00",
2230
        "MART" => "-09:30",
2231
        "MAWT" => "+05:00",
2232
        "MDT" => "-06:00",
2233
        "MET" => "+01:00",
2234
        "MEST" => "+02:00",
2235
        "MHT" => "+12:00",
2236
        "MIST" => "+11:00",
2237
        "MIT" => "-09:30",
2238
        "MMT" => "+06:30",
2239
        "MSK" => "+03:00",
2240
        "MST" => "",
2241
        //"MST" => "+08:00",
2242
        //"MST" => "-07:00",
2243
        "MUT" => "+04:00",
2244
        "MVT" => "+05:00",
2245
        "MYT" => "+08:00",
2246
        "NCT" => "+11:00",
2247
        "NDT" => "-02:30",
2248
        "NFT" => "+11:00",
2249
        "NOVT" => "+07:00",
2250
        "NPT" => "+05:45",
2251
        "NST" => "-03:30",
2252
        "NT" => "-03:30",
2253
        "NUT" => "-11:00",
2254
        "NZDT" => "+13:00",
2255
        "NZST" => "+12:00",
2256
        "OMST" => "+06:00",
2257
        "ORAT" => "+05:00",
2258
        "PDT" => "-07:00",
2259
        "PET" => "-05:00",
2260
        "PETT" => "+12:00",
2261
        "PGT" => "+10:00",
2262
        "PHOT" => "+13:00",
2263
        "PHT" => "+08:00",
2264
        "PHST" => "+08:00",
2265
        "PKT" => "+05:00",
2266
        "PMDT" => "-02:00",
2267
        "PMST" => "-03:00",
2268
        "PONT" => "+11:00",
2269
        "PST" => "-08:00",
2270
        "PWT" => "+09:00",
2271
        "PYST" => "-03:00",
2272
        "PYT" => "-04:00",
2273
        "RET" => "+04:00",
2274
        "ROTT" => "-03:00",
2275
        "SAKT" => "+11:00",
2276
        "SAMT" => "+04:00",
2277
        "SAST" => "+02:00",
2278
        "SBT" => "+11:00",
2279
        "SCT" => "+04:00",
2280
        "SDT" => "-10:00",
2281
        "SGT" => "+08:00",
2282
        "SLST" => "+05:30",
2283
        "SRET" => "+11:00",
2284
        "SRT" => "-03:00",
2285
        "SST" => "",
2286
        //"SST" => "-11:00",
2287
        //"SST" => "+08:00",
2288
        "SYOT" => "+03:00",
2289
        "TAHT" => "-10:00",
2290
        "THA" => "+07:00",
2291
        "TFT" => "+05:00",
2292
        "TJT" => "+05:00",
2293
        "TKT" => "+13:00",
2294
        "TLT" => "+09:00",
2295
        "TMT" => "+05:00",
2296
        "TRT" => "+03:00",
2297
        "TOT" => "+13:00",
2298
        "TVT" => "+12:00",
2299
        "ULAST" => "+09:00",
2300
        "ULAT" => "+08:00",
2301
        "UT" => "-00:00",
2302
        "UTC" => "-00:00",
2303
        "UYST" => "-02:00",
2304
        "UYT" => "-03:00",
2305
        "UZT" => "+05:00",
2306
        "VET" => "-04:00",
2307
        "VLAT" => "+10:00",
2308
        "VOLT" => "+03:00",
2309
        "VOST" => "+06:00",
2310
        "VUT" => "+11:00",
2311
        "WAKT" => "+12:00",
2312
        "WAST" => "+02:00",
2313
        "WAT" => "+01:00",
2314
        "WEST" => "+01:00",
2315
        "WET" => "-00:00",
2316
        "WIB" => "+07:00",
2317
        "WIT" => "+09:00",
2318
        "WITA" => "+08:00",
2319
        "WGST" => "-02:00",
2320
        "WGT" => "-03:00",
2321
        "WST" => "+08:00",
2322
        "YAKT" => "+09:00",
2323
        "YEKT" => "+05:00",
2324
        "ZULU" => "+00:00",
2325
        "Z" => "+00:00",
2326
        // lowercase
2327
        "acdt" => "+10:30",
2328
        "acst" => "+09:30",
2329
        "act" => "",
2330
        //"act" => "-05:00",
2331
        //"act" => "+08:00",
2332
        "acwst" => "+08:45",
2333
        "adt" => "-03:00",
2334
        "aedt" => "+11:00",
2335
        "aest" => "+10:00",
2336
        "aet" => "+11:00",
2337
        "aft" => "+04:30",
2338
        "akdt" => "-08:00",
2339
        "akst" => "-09:00",
2340
        "almt" => "+06:00",
2341
        "amst" => "-03:00",
2342
        "amt" => "",
2343
        //"amt" => "-04:00",
2344
        //"amt" => "+04:00",
2345
        "anat" => "+12:00",
2346
        "aqtt" => "+05:00",
2347
        "art" => "-03:00",
2348
        "ast" => "",
2349
        //"ast" => "+03:00",
2350
        //"ast" => "-04:00",
2351
        "awst" => "+08:00",
2352
        "azost" => "-00:00",
2353
        "azot" => "-01:00",
2354
        "azt" => "+04:00",
2355
        "bnt" => "+08:00",
2356
        "biot" => "+06:00",
2357
        "bit" => "-12:00",
2358
        "bot" => "-04:00",
2359
        "brst" => "-02:00",
2360
        "brt" => "-03:00",
2361
        "bst" => "",
2362
        //"bst" => "+06:00",
2363
        //"bst" => "+11:00",
2364
        //"bst" => "+01:00",
2365
        "btt" => "+06:00",
2366
        "cat" => "+02:00",
2367
        "cct" => "+06:30",
2368
        "cdt" => "",
2369
        //"cdt" => "-05:00",
2370
        //"cdt" => "-04:00",
2371
        "cest" => "+02:00",
2372
        "cet" => "+01:00",
2373
        "chadt" => "+13:45",
2374
        "chast" => "+12:45",
2375
        "chot" => "+08:00",
2376
        "chost" => "+09:00",
2377
        "chst" => "+10:00",
2378
        "chut" => "+10:00",
2379
        "cist" => "-08:00",
2380
        "ckt" => "-10:00",
2381
        "clst" => "-03:00",
2382
        "clt" => "-04:00",
2383
        "cost" => "-04:00",
2384
        "cot" => "-05:00",
2385
        "cst" => "",
2386
        //"cst" => "-06:00",
2387
        //"cst" => "+08:00",
2388
        //"cst" => "-05:00",
2389
        "ct" => "-05:00",
2390
        "cvt" => "-01:00",
2391
        "cwst" => "+08:45",
2392
        "cxt" => "+07:00",
2393
        "davt" => "+07:00",
2394
        "ddut" => "+10:00",
2395
        "dft" => "+01:00",
2396
        "easst" => "-05:00",
2397
        "east" => "-06:00",
2398
        "eat" => "+03:00",
2399
        "ect" => "",
2400
        //"ect" => "-04:00",
2401
        //"ect" => "-05:00",
2402
        "edt" => "-04:00",
2403
        "eest" => "+03:00",
2404
        "eet" => "+02:00",
2405
        "egst" => "-00:00",
2406
        "egt" => "-01:00",
2407
        "est" => "-05:00",
2408
        "et" => "-04:00",
2409
        "fet" => "+03:00",
2410
        "fjt" => "+12:00",
2411
        "fkst" => "-03:00",
2412
        "fkt" => "-04:00",
2413
        "fnt" => "-02:00",
2414
        "galt" => "-06:00",
2415
        "gamt" => "-09:00",
2416
        "get" => "+04:00",
2417
        "gft" => "-03:00",
2418
        "gilt" => "+12:00",
2419
        "git" => "-09:00",
2420
        "gmt" => "-00:00",
2421
        "gst" => "",
2422
        //"gst" => "-02:00",
2423
        //"gst" => "+04:00",
2424
        "gyt" => "-04:00",
2425
        "hdt" => "-09:00",
2426
        "haec" => "+02:00",
2427
        "hst" => "-10:00",
2428
        "hkt" => "+08:00",
2429
        "hmt" => "+05:00",
2430
        "hovst" => "+08:00",
2431
        "hovt" => "+07:00",
2432
        "ict" => "+07:00",
2433
        "idlw" => "-12:00",
2434
        "idt" => "+03:00",
2435
        "iot" => "+03:00",
2436
        "irdt" => "+04:30",
2437
        "irkt" => "+08:00",
2438
        "irst" => "+03:30",
2439
        "ist" => "",
2440
        //"ist" => "+05:30",
2441
        //"ist" => "+01:00",
2442
        //"ist" => "+02:00",
2443
        "jst" => "+09:00",
2444
        "kalt" => "+02:00",
2445
        "kgt" => "+06:00",
2446
        "kost" => "+11:00",
2447
        "krat" => "+07:00",
2448
        "kst" => "+09:00",
2449
        "lhst" => "",
2450
        //"lhst" => "+10:30",
2451
        //"lhst" => "+11:00",
2452
        "lint" => "+14:00",
2453
        "magt" => "+12:00",
2454
        "mart" => "-09:30",
2455
        "mawt" => "+05:00",
2456
        "mdt" => "-06:00",
2457
        "met" => "+01:00",
2458
        "mest" => "+02:00",
2459
        "mht" => "+12:00",
2460
        "mist" => "+11:00",
2461
        "mit" => "-09:30",
2462
        "mmt" => "+06:30",
2463
        "msk" => "+03:00",
2464
        "mst" => "",
2465
        //"mst" => "+08:00",
2466
        //"mst" => "-07:00",
2467
        "mut" => "+04:00",
2468
        "mvt" => "+05:00",
2469
        "myt" => "+08:00",
2470
        "nct" => "+11:00",
2471
        "ndt" => "-02:30",
2472
        "nft" => "+11:00",
2473
        "novt" => "+07:00",
2474
        "npt" => "+05:45",
2475
        "nst" => "-03:30",
2476
        "nt" => "-03:30",
2477
        "nut" => "-11:00",
2478
        "nzdt" => "+13:00",
2479
        "nzst" => "+12:00",
2480
        "omst" => "+06:00",
2481
        "orat" => "+05:00",
2482
        "pdt" => "-07:00",
2483
        "pet" => "-05:00",
2484
        "pett" => "+12:00",
2485
        "pgt" => "+10:00",
2486
        "phot" => "+13:00",
2487
        "pht" => "+08:00",
2488
        "phst" => "+08:00",
2489
        "pkt" => "+05:00",
2490
        "pmdt" => "-02:00",
2491
        "pmst" => "-03:00",
2492
        "pont" => "+11:00",
2493
        "pst" => "-08:00",
2494
        "pwt" => "+09:00",
2495
        "pyst" => "-03:00",
2496
        "pyt" => "-04:00",
2497
        "ret" => "+04:00",
2498
        "rott" => "-03:00",
2499
        "sakt" => "+11:00",
2500
        "samt" => "+04:00",
2501
        "sast" => "+02:00",
2502
        "sbt" => "+11:00",
2503
        "sct" => "+04:00",
2504
        "sdt" => "-10:00",
2505
        "sgt" => "+08:00",
2506
        "slst" => "+05:30",
2507
        "sret" => "+11:00",
2508
        "srt" => "-03:00",
2509
        "sst" => "",
2510
        //"sst" => "-11:00",
2511
        //"sst" => "+08:00",
2512
        "syot" => "+03:00",
2513
        "taht" => "-10:00",
2514
        "tha" => "+07:00",
2515
        "tft" => "+05:00",
2516
        "tjt" => "+05:00",
2517
        "tkt" => "+13:00",
2518
        "tlt" => "+09:00",
2519
        "tmt" => "+05:00",
2520
        "trt" => "+03:00",
2521
        "tot" => "+13:00",
2522
        "tvt" => "+12:00",
2523
        "ulast" => "+09:00",
2524
        "ulat" => "+08:00",
2525
        "ut" => "-00:00",
2526
        "utc" => "-00:00",
2527
        "uyst" => "-02:00",
2528
        "uyt" => "-03:00",
2529
        "uzt" => "+05:00",
2530
        "vet" => "-04:00",
2531
        "vlat" => "+10:00",
2532
        "volt" => "+03:00",
2533
        "vost" => "+06:00",
2534
        "vut" => "+11:00",
2535
        "wakt" => "+12:00",
2536
        "wast" => "+02:00",
2537
        "wat" => "+01:00",
2538
        "west" => "+01:00",
2539
        "wet" => "-00:00",
2540
        "wib" => "+07:00",
2541
        "wit" => "+09:00",
2542
        "wita" => "+08:00",
2543
        "wgst" => "-02:00",
2544
        "wgt" => "-03:00",
2545
        "wst" => "+08:00",
2546
        "yakt" => "+09:00",
2547
        "yekt" => "+05:00",
2548
        "zulu" => "+00:00",
2549
        "z" => "+00:00",
2550
};
2551

2552
/// [`RegexPattern`] divider _date?_ `2020/01/01` or `2020-01-01` or
2553
/// `2020 01 01` or `2020-01-01` or `20200101`
2554
const D_Dq: &RegexPattern = r"[ /\-]?";
2555
/// [`RegexPattern`] divider _date?_ `2020/01/01` or `2020-01-01` or
2556
/// `2020 01 01` or `20200101` or `2020\01\01`
2557
/// Uses `\` which is typically only seen in messier datetime formats, like
2558
/// those without timezones.
2559
const D_Deq: &RegexPattern = r"[ /\-\\]?";
2560
/// [`RegexPattern`] divider _date_, `2020/01/01` or `2020-01-01` or
2561
/// `2020 01 01`
2562
const D_D: &RegexPattern = r"[ /\-]";
2563
/// [`RegexPattern`] divider _time_, `20:30:00`
2564
const D_T: &RegexPattern = "[:]?";
2565
/// [`RegexPattern`] divider _time_ with extras, `20:30:00` or `20-00`
2566
const D_Te: &RegexPattern = r"[:\-]?";
2567
/// [`RegexPattern`] divider _day_ to _hour_, `2020/01/01T20:30:00`
2568
const D_DHq: &RegexPattern = "[ T]?";
2569
/// [`RegexPattern`] divider _day_ to _hour_ with dash, `2020:01:01-20:30:00`.
2570
const D_DHdq: &RegexPattern = r"[ T\-]?";
2571
/// [`RegexPattern`] divider _day_ to _hour_ with colon or dash,
2572
/// `2020:01:01-20:30:00`.
2573
const D_DHcdq: &RegexPattern = r"[ T\-:]?";
2574
/// [`RegexPattern`] divider _day_ to _hour_ with colon or dash or underline,
2575
/// `2020:01:01_20:30:00`.
2576
const D_DHcdqu: &RegexPattern = r"[ T\-:_]?";
2577
/// [`RegexPattern`] divider _day_ to _hour_ with colon or dash or underline
2578
/// or slash, `2020:01:01\20:30:00`.
2579
const D_DHcdqus: &RegexPattern = r"[ T/\\\-:_]?";
2580
/// [`RegexPattern`] divider _fractional_, `2020/01/01T20:30:00,123456`
2581
const D_SF: &RegexPattern = r"[\.,]";
2582

2583
/// [`RegexPattern`] dot or comma?
2584
const RP_dcq: &RegexPattern = r"[\.,]?";
2585
/// [`RegexPattern`] comma?
2586
const RP_cq: &RegexPattern = "[,]?";
2587
/// [`RegexPattern`] of commonly found syslog level names
2588
///
2589
/// References:
2590
/// - <https://www.rfc-editor.org/rfc/rfc5427#section-3>
2591
/// - <https://learningnetwork.cisco.com/s/article/syslog-severity-amp-level>
2592
/// - <https://learningnetwork.cisco.com/s/feed/0D53i00000KsKHECA3>
2593
/// - <https://success.trendmicro.com/dcx/s/solution/TP000086250>
2594
const RP_LEVELS: &RegexPattern = r"((?i)DEBUG[[[:digit:]]]|DEBUG|INFO[[[:digit:]]]|INFO|ERROR[[[:digit:]]]|ERROR|ERR|TRACE[[[:digit:]]]|TRACE|WARN[[[:digit:]]]|WARN|WARNING|VERBOSE[[[:digit:]]]|VERBOSE|EMERGENCY|EMERG|NOTICE|CRIT|CRITICAL|ALERT[[[:digit:]]]|ALERT(?-i)|PANIC)";
2595
/// [`RegexPattern`] blank
2596
const RP_BLANK: &RegexPattern = "[[:blank:]]";
2597
/// [`RegexPattern`] blank?
2598
const RP_BLANKq: &RegexPattern = "[[:blank:]]?";
2599
/// [`RegexPattern`] blank or end
2600
const RP_BLANKe: &RegexPattern = "([[:blank:]]|$)";
2601
/// [`RegexPattern`] blank, 1 or 2
2602
const RP_BLANK12: &RegexPattern = r"[[:blank:]]{1,2}";
2603
/// [`RegexPattern`] blank, 1 or 2?
2604
const RP_BLANK12q: &RegexPattern = r"([[:blank:]]{1,2})?";
2605
/// [`RegexPattern`] blanks
2606
const RP_BLANKS: &RegexPattern = "[[:blank:]]+";
2607
/// [`RegexPattern`] blanks?
2608
const RP_BLANKSq: &RegexPattern = "[[:blank:]]*";
2609
/// [`RegexPattern`] _not_ blank
2610
const RP_BLANK_NO: &RegexPattern = "[^[:blank:]]";
2611
/// [`RegexPattern`] anything plus
2612
const RP_ANYp: &RegexPattern = ".+";
2613
/// [`RegexPattern`] left-side brackets
2614
pub(crate) const RP_LB: &RegexPattern = r"[\[\(<{]";
2615
/// [`RegexPattern`] right-side brackets
2616
pub(crate) const RP_RB: &RegexPattern = r"[\]\)>}]";
2617

2618
// -----------------------------------------------------------
2619
// the global list of built-in Datetime parsing "instructions"
2620

2621
/// Index into the global [`DATETIME_PARSE_DATAS`]
2622
pub type DateTimeParseInstrsIndex = usize;
2623

2624
/// A run-time created vector of [`DateTimeRegex`] instances that is a
2625
/// counterpart to [`DATETIME_PARSE_DATAS`]
2626
pub type DateTimeParseInstrsRegexVec = Vec<OnceCell<DateTimeRegex>>;
2627

2628
/// Length of [`DATETIME_PARSE_DATAS`] (one past last index)
2629
// XXX: do not forget to update test `test_DATETIME_PARSE_DATAS_test_cases`
2630
//      in `datetime_tests.rs`. The `test_matrix` range end value must match
2631
//      this value.
2632
pub const DATETIME_PARSE_DATAS_LEN: usize = 175;
2633

2634
/// Built-in [`DateTimeParseInstr`] datetime parsing patterns.
2635
///
2636
/// These are all regular expression patterns that will be attempted on
2637
/// each [`Line`] of a processed file.
2638
///
2639
/// Order of declaration matters: during initial parsing of a syslog file, all
2640
/// of these regex patterns are attempted in order of their declaration.
2641
/// Listing a general regex pattern before a specific regex pattern may result
2642
/// in a loss of datetime information.
2643
///
2644
/// For example, given sysline
2645
/// ```text
2646
/// 2001-02-03T04:05:06 -1100 hello
2647
/// ```
2648
///
2649
/// A regex that attempts to match from year to second (and not the timezone),
2650
/// will match `"2001-02-03T04:05:06"`, dropping the timezone information.
2651
/// Generally, more specific regex patterns should be listed before
2652
/// general regex patterns.
2653
///
2654
/// Notice that local sequences of `DateTimeParseInstr`
2655
/// generally match from more specific to more general
2656
/// to no timezone. i.e. match attempt ordering is
2657
/// `%:z` (`"-04:00"`), to `%z` (`"-0400"`),
2658
/// to `%#z` (`"-04"`), to `%Z` (`"EDT"`),
2659
/// to no timezone.
2660
///
2661
/// A drawback of this specific-to-general approach:
2662
/// during [`SyslineReader`] initial reading stage,
2663
/// it may try *all* the patterns (from index 0 of
2664
/// `DATETIME_PARSE_DATAS` to wherever it finds a match).
2665
/// So if a file has a datetime pattern that matches the last entry in
2666
/// `DATETIME_PARSE_DATAS` then the `SyslineReader` will try *all*
2667
/// the `DateTimeParseInstr` within `DATETIME_PARSE_DATAS` several times.
2668
/// Once the controlling `SyslineReader` calls
2669
/// [`dt_patterns_analysis`] for the last time, then only one
2670
/// `DateTimeParseInstr` is tried for each `Line`.
2671
/// But until `dt_patterns_analysis` is called, there will be many missed
2672
/// matches. Regular expression creation and matching uses a large amount of
2673
/// compute and time resources.
2674
///
2675
/// [`SyslineReader`]: crate::readers::syslinereader::SyslineReader
2676
/// [`dt_patterns_analysis`]: crate::readers::syslinereader::SyslineReader#method.dt_patterns_analysis
2677
/// [`Line`]: crate::data::line::Line
2678
// XXX: yet another rust playground for testing regex
2679
//      https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=93a47ceb225bae73dfcbce2574b65e91
2680
// TODO: [2023/01/14] add test of shortest possible match for all DTPD!
2681
// TODO: [2023/05/11] modify `_tests` to allow testing invalid patterns, e.g.
2682
//       ("2000-XY-01T00:00:00Z")
2683
//       consider a two-value enum `DateTimeParseInstrTest` with variants
2684
//       `Valid` and `Invalid`
2685
// TODO: [2023/09/03] cost-savings: regular expression initialization requires a large amount of startup
2686
//       time during `s4` program startup. Many of the regular expressions here could combine
2687
//       the timezone matching. The timezones matching could be reduced for the
2688
//       numeric timezones, CGP_TZz, CGP_TZzc, CGP_TZzp, into one `CGP_TZz_`.
2689
//       Leave the alphabetic timezone CGP_TZZ because it's matching is a little more delicate.
2690
//       Later, the `fn captures_to_buffer_bytes` would modify the numeric timezone into a
2691
//       format acceptable for passing the data to `chrono::DateTime::parse_from_str`.
2692
pub const DATETIME_PARSE_DATAS: [DateTimeParseInstr; DATETIME_PARSE_DATAS_LEN] = [
2693
    // ---------------------------------------------------------------------------------------------
2694
    // from file `./logs/Ubuntu18/xrdp.log`
2695
    // example with offset:
2696
    //               1
2697
    //     01234567890123456789
2698
    //     [20200113-11:03:06] [DEBUG] Closed socket 7 (AF_INET6 :: port 3389)
2699
    //
2700
    // from file `./logs/Ubuntu18/samba/log.10.1.1.2` (multi-line)
2701
    // example with offset:
2702
    //               1         2         3
2703
    //     0123456789012345678901234567890
2704
    //     [2020/03/05 12:17:59.631000,  3] ../source3/smbd/oplock.c:1340(init_oplocks)
2705
    //        init_oplocks: initializing messages.
2706
    //     [2000/01/01 00:00:04.123456] ../source3/smbd/oplock.c:1340(init_oplocks)
2707
    //
2708
    DTPD!(
2709
        concatcp!("^", RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_RB),
2710
        DTFSS_YmdHMSf, 0, 40, CGN_YEAR, CGN_FRACTIONAL,
2711
        &[
2712
            (1, 24, (O_L, 2000, 1, 1, 0, 0, 1, 123000000), "[2000/01/01 00:00:01.123] ../source3/smbd/oplock.c:1340(init_oplocks)"),
2713
            (1, 27, (O_L, 2000, 1, 1, 0, 0, 1, 123456000), "[2000/01/01 00:00:01.123456] ../source3/smbd/oplock.c:1340(init_oplocks)"),
2714
            (1, 30, (O_L, 2000, 1, 1, 0, 0, 1, 123456789), "[2000/01/01 00:00:01.123456789] ../source3/smbd/oplock.c:1340(init_oplocks)"),
2715
        ],
2716
        line!(),
2717
    ),
2718
    DTPD!(
2719
        concatcp!("^", RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, RP_RB),
2720
        DTFSS_YmdHMSfz, 0, 40, CGN_YEAR, CGN_TZ,
2721
        &[
2722
            (1, 28, (O_M11, 2000, 1, 1, 0, 0, 2, 100000000), "(2000/01/01 00:00:02.1 -1100) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2723
            (1, 29, (O_M11, 2000, 1, 1, 0, 0, 2, 120000000), "(2000/01/01 00:00:02.12 -1100) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2724
            (1, 30, (O_M11, 2000, 1, 1, 0, 0, 2, 123000000), "(2000/01/01 00:00:02.123 -1100) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2725
            (1, 33, (O_M11, 2000, 1, 1, 0, 0, 2, 123456000), "(2000/01/01 00:00:02.123456 -1100) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2726
            (1, 36, (O_M11, 2000, 1, 1, 0, 0, 2, 123456789), "(2000/01/01 00:00:02.123456789 -1100) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2727
        ],
2728
        line!(),
2729
    ),
2730
    DTPD!(
2731
        concatcp!("^", RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, RP_RB),
2732
        DTFSS_YmdHMSfzc, 0, 40, CGN_YEAR, CGN_TZ,
2733
        &[
2734
            (1, 37, (O_M1130, 2000, 1, 1, 0, 0, 3, 123456789), r"{2000/01/01 00:00:03.123456789 -11:30} ../source3/smbd/oplock.c:1340(init_oplocks)"),
2735
            (1, 31, (O_M1130, 2000, 1, 1, 0, 0, 3, 123000000), r"{2000/01/01 00:00:03.123 -11:30} ../source3/smbd/oplock.c:1340(init_oplocks)"),
2736
        ],
2737
        line!(),
2738
    ),
2739
    DTPD!(
2740
        concatcp!("^", RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, RP_RB),
2741
        DTFSS_YmdHMSfzp, 0, 40, CGN_YEAR, CGN_TZ,
2742
        &[
2743
            (1, 34, (O_M11, 2000, 1, 1, 0, 0, 4, 123456789), "(2000/01/01 00:00:04.123456789 -11) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2744
            (1, 34, (O_M11, 2000, 1, 1, 0, 0, 4, 123456789), "(2000/01/01 00:00:04.123456789 -11)"),
2745
        ],
2746
        line!(),
2747
    ),
2748
    DTPD!(
2749
        concatcp!("^", RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, RP_RB),
2750
        DTFSS_YmdHMSfZ, 0, 40, CGN_YEAR, CGN_TZ,
2751
        &[
2752
            (1, 35, (O_VLAT, 2000, 1, 1, 0, 0, 5, 123456789), "(2000/01/01 00:00:05.123456789 VLAT) ../source3/smbd/oplock.c:1340(init_oplocks)"),
2753
            (1, 34, (O_WAT, 2000, 1, 1, 0, 0, 5, 123456789), "<2000/01/01 00:00:05.123456789 WAT> ../source3/smbd/oplock.c:1340(init_oplocks)"),
2754
            (1, 34, (O_PST, 2000, 1, 1, 0, 0, 5, 123456789), "<2000/01/01 00:00:05.123456789 PST> ../source3/smbd/oplock.c:1340(init_oplocks)"),
2755
            (1, 34, (O_PST, 2000, 1, 1, 0, 0, 5, 123456789), "<2000/01/01 00:00:05.123456789 pst> ../source3/smbd/oplock.c:1340(init_oplocks)"),
2756
            (1, 33, (O_PST, 2000, 1, 1, 0, 0, 5, 123456789), "<2000/01/01 00:00:05.123456789pst> ../source3/smbd/oplock.c:1340(init_oplocks)"),
2757
        ],
2758
        line!(),
2759
    ),
2760
    DTPD!(
2761
        concatcp!("^", RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, r"[,\.\| \t]", RP_BLANKSq, r"[[:word:]]{1,20}", RP_RB),
2762
        DTFSS_YmdHMSf, 0, 40, CGN_YEAR, CGN_FRACTIONAL,
2763
        &[
2764
            (1, 27, (O_L, 2020, 3, 5, 12, 17, 59, 631000000), "[2020/03/05 12:17:59.631000, FOO] ../source3/smbd/oplock.c:1340(init_oplocks)"),
2765
            (1, 27, (O_L, 2020, 3, 5, 12, 17, 59, 631000000), "[2020/03/05 12:17:59.631000, bubba] ../source3/smbd/oplock.c:1340(init_oplocks)"),
2766
        ],
2767
        line!(),
2768
    ),
2769
    // ---------------------------------------------------------------------------------------------
2770
    // from file `./logs/synology-DS6/opentftp.log`
2771
    // example with offset:
2772
    //
2773
    //               1         2
2774
    //     012345678901234567890
2775
    //     [22-Feb-17 21:24:20] Section [ALLOWED-CLIENTS] Invalid entry 192.168.0.0-192.168.0.255 in ini file, ignored
2776
    //
2777
    // The `"17"` is the shortened year and `"22"` the day of the month, see
2778
    // source code
2779
    //      https://sourceforge.net/projects/tftp-server/files/tftp%20server%20single%20port/opentftpspV1.66.tar.gz/download
2780
    // file path
2781
    //      opentftpspV1.66.tar.gz:opentftpspV1.66.tar:opentftp/opentftpd.cpp
2782
    // function
2783
    //     void logMess(request *req, MYBYTE logLevel)
2784
    // line
2785
    //     strftime(extbuff, sizeof(extbuff), "%d-%b-%y %X", ttm);
2786
    //
2787
    DTPD!(
2788
        concatcp!("^", RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEARy, D_DHq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_RB),
2789
        DTFSS_ybdHMS, 0, 40, CGN_DAY, CGN_SECOND,
2790
        &[
2791
            (1, 19, (O_L, 2017, 2, 22, 21, 24, 20, 0), "[22-Feb-17 21:24:20] Section [ALLOWED-CLIENTS] Invalid entry 192.168.0.0-192.168.0.255 in ini file, ignored")
2792
        ],
2793
        line!(),
2794
    ),
2795
    // ---------------------------------------------------------------------------------------------
2796
    // syslog messages using datetime format ISO 8601 / RFC 3339 (real "syslog" messages)
2797
    //
2798
    //      <14>2023-01-01T15:00:36-08:00 (HOST) (192.168.0.1) [dropbear[23732]: authpriv:info] [23732]:  Exit (root): Disconnect received ⸨<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received⸩
2799
    //      <29>2023-01-01T14:21:13-08:00 (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩
2800
    //
2801
    // Helpful reference https://ijmacd.github.io/rfc3339-iso8601/
2802
    //
2803
    // Highest value syslog priority is 191.
2804
    // From https://github.com/rsyslog/rsyslog/blob/v8.2212.0/runtime/rsyslog.h#L197
2805
    //
2806
    //      #define LOG_MAXPRI 191  /* highest supported valid PRI value --> RFC3164, RFC5424 */
2807
    //
2808
    // an example with fractional seconds
2809
    //
2810
    //               1         2         3         4         5
2811
    //     012345678901234567890123456789012345678901234567890
2812
    //     <31>2023-01-06T14:35:00.506282-08:00 (host) (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826
2813
    //     <128> 2023-01-06T14:35:00.506282871 -08:00[host]
2814
    //
2815
    // syslog format with fractional seconds
2816
    DTPD!(
2817
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, RP_NODIGIT),
2818
        DTFSS_YmdHMSfzc, 0, 50, CGN_YEAR, CGN_TZ,
2819
        &[
2820
            (4, 36, (O_M8, 2023, 1, 6, 14, 35, 0, 506282000), "<31>2023-01-06T14:35:00.506282-08:00 (host) (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2821
            (6, 42, (O_M8, 2023, 1, 6, 14, 35, 0, 506282871), "<128> 2023-01-06T14:35:00.506282871 -08:00[host] (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2822
        ],
2823
        line!(),
2824
    ),
2825
    DTPD!(
2826
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, RP_NODIGIT),
2827
        DTFSS_YmdHMSfz, 0, 50, CGN_YEAR, CGN_TZ,
2828
        &[
2829
            (4, 35, (O_P8, 2023, 1, 6, 14, 35, 0, 506282000), "<31>2023-01-06T14:35:00.506282+0800 (host) (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2830
            (6, 41, (O_P8, 2023, 1, 6, 14, 35, 0, 506282871), "<128> 2023-01-06T14:35:00.506282871 +0800[host] (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2831
        ],
2832
        line!(),
2833
    ),
2834
    DTPD!(
2835
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, RP_NODIGIT),
2836
        DTFSS_YmdHMSfzp, 0, 50, CGN_YEAR, CGN_TZ,
2837
        &[
2838
            (4, 33, (O_P8, 2023, 1, 6, 14, 35, 0, 506282000), "<31>2023-01-06T14:35:00.506282+08 (host) (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2839
            (6, 39, (O_P8, 2023, 1, 6, 14, 35, 0, 506282871), "<128> 2023-01-06T14:35:00.506282871 +08[host] (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2840
        ],
2841
        line!(),
2842
    ),
2843
    DTPD!(
2844
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, RP_NODIGIT),
2845
        DTFSS_YmdHMSfZ, 0, 50, CGN_YEAR, CGN_TZ,
2846
        &[
2847
            (4, 34, (O_PST, 2023, 1, 6, 14, 35, 0, 506282000), "<31>2023-01-06T14:35:00.506282 PST (host) (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2848
            (6, 40, (O_WITA, 2023, 1, 6, 14, 35, 0, 506282871), "<128> 2023-01-06T14:35:00.506282871 WITA[host] (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2849
            (6, 39, (O_WITA, 2023, 1, 6, 14, 35, 0, 506282871), "<128> 2023-01-06T14:35:00.506282871WITA[host] (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2850
        ],
2851
        line!(),
2852
    ),
2853
    DTPD!(
2854
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_NOALNUM),
2855
        DTFSS_YmdHMSf, 0, 50, CGN_YEAR, CGN_FRACTIONAL,
2856
        &[
2857
            (4, 30, (O_L, 2023, 1, 6, 14, 35, 0, 506282000), "<31>2023-01-06T14:35:00.506282 (host) (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2858
            (6, 35, (O_L, 2023, 1, 6, 14, 35, 0, 506282871), "<128> 2023-01-06T14:35:00.506282871[host] (192.168.0.1) [unbound[63893] daemon:debug] [63893]:  [63893:0] debug: cache memory msg=76002 rrset=120560 infra=18065 val=78826"),
2859
        ],
2860
        line!(),
2861
    ),
2862
    // syslog format without fractional seconds
2863
    DTPD!(
2864
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_D, CGP_MONTHm, D_D, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzc),
2865
        DTFSS_YmdHMSzc, 0, 46, CGN_YEAR, CGN_TZ,
2866
        &[
2867
            (4, 29, (O_M8, 2023, 2, 1, 15, 0, 36, 0), "<14>2023-02-01T15:00:36-08:00 (HOST) (192.168.0.1) [dropbear[23732]: authpriv:info] [23732]:  Exit (root): Disconnect received ⸨<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received⸩"),
2868
            (4, 29, (O_0, 2023, 2, 1, 15, 0, 36, 0), "<14>2023-02-01T15:00:36+00:00 (HOST) (192.168.0.1) [dropbear[23732]: authpriv:info] [23732]:  Exit (root): Disconnect received ⸨<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received⸩"),
2869
            (4, 30, (O_M8, 2023, 2, 1, 14, 21, 13, 0), "<29>2023-02-01T14:21:13 -08:00 (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2870
        ],
2871
        line!(),
2872
    ),
2873
    DTPD!(
2874
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_D, CGP_MONTHm, D_D, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzp),
2875
        DTFSS_YmdHMSzp, 0, 50, CGN_YEAR, CGN_TZ,
2876
        &[
2877
            (4, 26, (O_M8, 2023, 2, 1, 15, 0, 36, 0), "<14>2023-02-01T15:00:36-08 (HOST) (192.168.0.1) [dropbear[23732]: authpriv:info] [23732]:  Exit (root): Disconnect received ⸨<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received⸩"),
2878
            (4, 27, (O_M8, 2023, 2, 1, 14, 21, 13, 0), "<29>2023-02-01T14:21:13 -08 (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2879
        ],
2880
        line!(),
2881
    ),
2882
    DTPD!(
2883
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_D, CGP_MONTHm, D_D, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZZ, RP_NOALNUM),
2884
        DTFSS_YmdHMSZ, 0, 50, CGN_YEAR, CGN_TZ,
2885
        &[
2886
            (4, 27, (O_PST, 2023, 2, 1, 15, 0, 36, 0), "<14>2023-02-01T15:00:36 PST (HOST) (192.168.0.1) [dropbear[23732]: authpriv:info] [23732]:  Exit (root): Disconnect received ⸨<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received⸩"),
2887
            (4, 28, (O_CIST, 2023, 2, 1, 14, 21, 13, 0), "<29>2023-02-01T14:21:13 CIST (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2888
            (4, 27, (O_CIST, 2023, 2, 1, 14, 21, 13, 0), "<29>2023-02-01T14:21:13CIST (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2889
            (4, 24, (O_Z, 2023, 2, 1, 14, 21, 13, 0), "<29>2023-02-01T14:21:13Z (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2890
        ],
2891
        line!(),
2892
    ),
2893
    DTPD!(
2894
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_YEAR, D_D, CGP_MONTHm, D_D, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
2895
        DTFSS_YmdHMS, 0, 45, CGN_YEAR, CGN_SECOND,
2896
        &[
2897
            (4, 23, (O_L, 2023, 2, 1, 15, 0, 36, 0), "<14>2023-02-01T15:00:36 (HOST) (192.168.0.1) [dropbear[23732]: authpriv:info] [23732]:  Exit (root): Disconnect received ⸨<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received⸩"),
2898
            (4, 23, (O_L, 2023, 2, 1, 14, 21, 13, 0), "<29>2023-02-01T14:21:13(HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2899
            (5, 24, (O_L, 2023, 2, 1, 14, 21, 13, 0), "<191>2023-02-01T14:21:13 (HOST) (192.168.0.1) [netifd: daemon:notice] [-]:  Network device 'eth0' link is up ⸨<29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up⸩"),
2900
        ],
2901
        line!(),
2902
    ),
2903
    //
2904
    // TODO: [2023/01/09] not all TIMESTAMP patterns or other valid message formats are covered.
2905
    //       see RFC 5424 https://www.rfc-editor.org/rfc/rfc5424.html#page-12
2906
    //                    https://www.rfc-editor.org/rfc/rfc5424.html#section-6.5
2907
    //                    https://www.rfc-editor.org/rfc/rfc5424.html#appendix-A.4
2908
    //
2909
    // Syslog also uses datetime stamp format RFC 3164
2910
    // The contrived rsyslog messages are derived from the example in RFC 3164 (BSD Syslog Protocol)
2911
    // https://www.rfc-editor.org/rfc/rfc3164.html#section-5.4
2912
    //
2913
    //      <14>Jan  1 15:00:36 2023 HOST dropbear[23732]: Exit (root): Disconnect received
2914
    //      <29>Jan  1 14:21:13 2023 HOST netifd: Network device 'eth0' link is up
2915
    //
2916
    DTPD!(
2917
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKSq, CGP_TZzc, RP_NODIGIT),
2918
        DTFSS_BdHMSYzc, 0, 40, CGN_MONTH, CGN_TZ,
2919
        &[
2920
            (4, 31, (O_M2, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 2023 -02:00 HOST dropbear[23732]: Exit (root): Disconnect received"),
2921
            (4, 31, (O_M2, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan 01 15:00:36 2023 -02:00 HOST dropbear[23732]: Exit (root): Disconnect received"),
2922
            (3, 30, (O_M2, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 2023 -02:00 HOST netifd: Network device 'eth0' link is up"),
2923
            (5, 32, (O_M2, 2023, 1, 31, 14, 21, 13, 0), "<135>Jan 31 14:21:13 2023 -02:00 HOST netifd: Network device 'eth0' link is up"),
2924
        ],
2925
        line!(),
2926
    ),
2927
    DTPD!(
2928
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKSq, CGP_TZzp, RP_NODIGIT),
2929
        DTFSS_BdHMSYzp, 0, 40, CGN_MONTH, CGN_TZ,
2930
        &[
2931
            (4, 28, (O_M2, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 2023 -02 HOST dropbear[23732]: Exit (root): Disconnect received"),
2932
            (4, 28, (O_M2, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan 01 15:00:36 2023 -02 HOST dropbear[23732]: Exit (root): Disconnect received"),
2933
            (3, 27, (O_M2, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 2023 -02 HOST netifd: Network device 'eth0' link is up"),
2934
        ],
2935
        line!(),
2936
    ),
2937
    DTPD!(
2938
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKSq, CGP_TZZ, RP_NOALNUM),
2939
        DTFSS_BdHMSYZ, 0, 40, CGN_MONTH, CGN_TZ,
2940
        &[
2941
            (4, 29, (O_WGST, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 2023 WGST HOST dropbear[23732]: Exit (root): Disconnect received"),
2942
            (4, 29, (O_WGST, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan 01 15:00:36 2023 WGST HOST dropbear[23732]: Exit (root): Disconnect received"),
2943
            (3, 28, (O_WGST, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 2023 WGST HOST netifd: Network device 'eth0' link is up"),
2944
            (3, 27, (O_WGST, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 2023WGST HOST netifd: Network device 'eth0' link is up"),
2945
        ],
2946
        line!(),
2947
    ),
2948
    DTPD!(
2949
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_NODIGIT),
2950
        DTFSS_BdHMSY, 0, 35, CGN_MONTH, CGN_YEAR,
2951
        &[
2952
            (4, 24, (O_L, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 2023 HOST dropbear[23732]: Exit (root): Disconnect received"),
2953
            (3, 23, (O_L, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 2023 HOST netifd: Network device 'eth0' link is up"),
2954
        ],
2955
        line!(),
2956
    ),
2957
    //
2958
    //      <14>Jan  1 15:00:36 WIST 2023 HOST dropbear[23732]: Exit (root): Disconnect received
2959
    //
2960
    DTPD!(
2961
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_TZzc, RP_BLANKS, CGP_YEAR, RP_NODIGIT),
2962
        DTFSS_BdHMSYzc, 0, 40, CGN_MONTH, CGN_YEAR,
2963
        &[
2964
            (4, 31, (O_M2, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 -02:00 2023 HOST dropbear[23732]: Exit (root): Disconnect received"),
2965
            (3, 30, (O_M2, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 -02:00 2023 HOST netifd: Network device 'eth0' link is up"),
2966
        ],
2967
        line!(),
2968
    ),
2969
    DTPD!(
2970
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_TZzp, RP_BLANKS, CGP_YEAR, RP_NODIGIT),
2971
        DTFSS_BdHMSYzp, 0, 40, CGN_MONTH, CGN_YEAR,
2972
        &[
2973
            (4, 28, (O_M2, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 -02 2023 HOST dropbear[23732]: Exit (root): Disconnect received"),
2974
            (3, 27, (O_M2, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 -02 2023 HOST netifd: Network device 'eth0' link is up"),
2975
        ],
2976
        line!(),
2977
    ),
2978
    DTPD!(
2979
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_TZZ, RP_BLANKS, CGP_YEAR, RP_NODIGIT),
2980
        DTFSS_BdHMSYZ, 0, 40, CGN_MONTH, CGN_YEAR,
2981
        &[
2982
            (4, 29, (O_WGST, 2023, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 WGST 2023 HOST dropbear[23732]: Exit (root): Disconnect received"),
2983
            (3, 28, (O_WGST, 2023, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 WGST 2023 HOST netifd: Network device 'eth0' link is up"),
2984
        ],
2985
        line!(),
2986
    ),
2987
    //
2988
    //      <14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received
2989
    //      <29>Jan  1 14:21:13 HOST netifd: Network device 'eth0' link is up
2990
    //      <7>Mar 23 09:35:30 localhost DPKG [9332:<console>.<module>:2] debug info
2991
    //
2992
    DTPD!(
2993
        concatcp!("^<", RP_DIGITS3, ">", RP_BLANKq, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKSq),
2994
        DTFSS_BdHMS, 0, 30, CGN_MONTH, CGN_SECOND,
2995
        &[
2996
            (4, 19, (O_L, YD, 1, 1, 15, 0, 36, 0), "<14>Jan  1 15:00:36 HOST dropbear[23732]: Exit (root): Disconnect received"),
2997
            (3, 18, (O_L, YD, 1, 31, 14, 21, 13, 0), "<1>Jan 31 14:21:13 HOST netifd: Network device 'eth0' link is up"),
2998
            (3, 18, (O_L, YD, 3, 23, 9, 35, 30, 0), "<7>Mar 23 09:35:30 localhost DPKG [9332:<console>.<module>:2] debug info"),
2999
            (5, 20, (O_L, YD, 3, 23, 9, 35, 30, 0), "<144>Mar 23 09:35:30 localhost DPKG [9332:<console>.<module>:2] debug info"),
3000
            // example scraped from RFC 3164 (https://www.rfc-editor.org/rfc/rfc3164.html#section-5.4)
3001
            (4, 19, (O_L, YD, 10, 11, 22, 14, 15, 0), "<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8"),
3002
        ],
3003
        line!(),
3004
    ),
3005
    // ---------------------------------------------------------------------------------------------
3006
    //
3007
    // from file `/var/log/unattended-upgrades/unattended-upgrades-dpkg.log`
3008
    // example with offset:
3009
    //
3010
    //               1         2         3         4
3011
    //     01234567890123456789012345678901234567890
3012
    //     Log started: 2022-07-14  06:48:58
3013
    //     (Reading database ...
3014
    //     Preparing to unpack .../linux-tools-common_5.15.0-41.44_all.deb ...
3015
    //     Unpacking linux-tools-common (5.15.0-41.44) over (5.15.0-40.43) ...
3016
    //     Setting up linux-tools-common (5.15.0-41.44) ...
3017
    //     Processing triggers for man-db (2.10.2-1) ...
3018
    //     NEEDRESTART-VER: 3.5
3019
    //     NEEDRESTART-KCUR: 5.10.102.1-microsoft-standard-WSL2
3020
    //     NEEDRESTART-KSTA: 0
3021
    //     Log ended: 2022-07-14  06:49:02
3022
    //
3023
    DTPD!(
3024
        concatcp!("^((log|Log|LOG) (started|Started|STARTED|ended|Ended|ENDED))", RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, "(T|[[:blank:]]+)", CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
3025
        DTFSS_YmdHMS, 0, 50, CGN_YEAR, CGN_SECOND,
3026
        &[
3027
            (13, 33, (O_L, 2022, 7, 14, 6, 48, 58, 0), "Log started: 2022-07-14  06:48:58\n(Reading database …"),
3028
            (13, 32, (O_L, 2022, 7, 14, 6, 48, 58, 0), "Log started: 2022-07-14 06:48:58 Reading database ..."),
3029
            (13, 32, (O_L, 2022, 7, 14, 6, 48, 58, 0), "Log started: 2022-07-14T06:48:58"),
3030
            (11, 31, (O_L, 2022, 7, 14, 6, 49, 59, 0), "Log ended: 2022-07-14  06:49:59"),
3031
            (11, 30, (O_L, 2022, 7, 14, 6, 49, 59, 0), "Log ended:\t2022-07-14\t06:49:59"),
3032
        ],
3033
        line!(),
3034
    ),
3035
    //
3036
    // ---------------------------------------------------------------------------------------------
3037
    // from file `logs/Windows10Pro/debug/mrt.log`
3038
    // example with offset:
3039
    //
3040
    //               1         2         3         4          5         6         7         8         9
3041
    //     01234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890
3042
    //     ---------------------------------------------------------------------------------------
3043
    //     Microsoft Windows Malicious Software Removal Tool v5.83, (build 5.83.13532.1)
3044
    //     Started On Thu Sep 10 10:08:35 2020
3045
    //     ...
3046
    //     Results Summary:
3047
    //     ----------------
3048
    //     No infection found.
3049
    //     Successfully Submitted Heartbeat Report
3050
    //     Microsoft Windows Malicious Software Removal Tool Finished On Tue Nov 10 18:54:47 2020
3051
    //
3052
    DTPD!(
3053
        concatcp!("(Started On|Started on|started on|STARTED|Started|started|Finished On|Finished on|finished on|FINISHED|Finished|finished)[:]?", RP_BLANK, CGP_DAYa, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_NOALNUM),
3054
        DTFSS_YbdHMS, 0, 140, CGN_DAYa, CGN_YEAR,
3055
        &[
3056
            (11, 35, (O_L, 2020, 9, 10, 10, 8, 35, 0), "Started On Thu Sep 10 10:08:35 2020"),
3057
            (11, 34, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On Thu Sep 1 10:08:35 2020"),
3058
            (11, 35, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On Thu Sep  1 10:08:35 2020"),
3059
            (11, 35, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On Thu Sep. 1 10:08:35 2020"),
3060
            (11, 35, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On Thu. Sep 1 10:08:35 2020"),
3061
            (11, 36, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On Thu. Sep. 1 10:08:35 2020"),
3062
            (11, 36, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On thu. sep. 1 10:08:35 2020"),
3063
            (11, 36, (O_L, 2020, 9, 1, 10, 8, 35, 0), "Started On THU. SEP. 1 10:08:35 2020"),
3064
            (62, 86, (O_L, 2020, 11, 10, 18, 54, 47, 0), "Microsoft Windows Malicious Software Removal Tool Finished On Tue Nov 10 18:54:47 2020"),
3065
        ],
3066
        line!(),
3067
    ),
3068
    //
3069
    // ---------------------------------------------------------------------------------------------
3070
    // from file `logs/Windows10Pro/comsetup.log`
3071
    // example with offset:
3072
    //
3073
    //      COM+[12:24:34]: Setup started - [DATE:05,27,2020 TIME: 12:24 pm]
3074
    //      COM+[12:24:34]: ********************************************************************************
3075
    //      COM+[12:24:34]: Start CComMig::Discover
3076
    //      COM+[12:24:34]: Return XML stream: <migXml xmlns=""><rules context="system"><include><objectSet></objectSet></include></rules></migXml>
3077
    //      COM+[12:24:34]: End CComMig::Discover - Return 0x00000000
3078
    //      COM+[12:24:38]: ********************************************************************************
3079
    //      COM+[12:24:38]: Setup (COMMIG) finished - [DATE:05,27,2020 TIME: 12:24 pm]
3080
    //
3081
    // ---------------------------------------------------------------------------------------------
3082
    // from file `logs/Windows10Pro/System32/wbem/WMIMigration.log`
3083
    // example with offset:
3084
    //
3085
    //      (08/10/2019-01:46:44.0042) Filtering object "\\HOST\ROOT\CIMV2\mdm\dmmap:MDM_Policy_Config01_Location02" during apply
3086
    //      (05/27/2020-12:25:43.0877) Total number of objects successfully migrated :2346, failed objects :16
3087
    //
3088
    DTPD!(
3089
        concatcp!("^", RP_LB, CGP_MONTHm, D_D, CGP_DAYde, D_D, CGP_YEAR, D_DHdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_RB),
3090
        DTFSS_YmdHMSf, 0, 40, CGN_MONTH, CGN_FRACTIONAL,
3091
        &[
3092
            (1, 25, (O_L, 2019, 8, 10, 1, 46, 44, 4200000), r#"(08/10/2019-01:46:44.0042) Filtering object "\\HOST\ROOT\CIMV2\mdm\dmmap:MDM_Policy_Config01_Location02" during apply"#),
3093
            (1, 25, (O_L, 2020, 5, 27, 12, 25, 43, 87700000), "(05/27/2020-12:25:43.0877) Total number of objects successfully migrated :2346, failed objects :16"),
3094
            (1, 24, (O_L, 2020, 5, 27, 12, 25, 43, 87000000), "<05/27/2020-12:25:43.087> Total number of objects successfully migrated :2346, failed objects :16"),
3095
            // XXX: brackets can be mismatched which is not preferred. However, adding a regexp
3096
            //      for every bracket type would be too many regexp.
3097
            // TODO: find the regexp recipe that can correlate the left and right brackets.
3098
            (1, 24, (O_L, 2020, 5, 27, 12, 25, 43, 87000000), "(05/27/2020-12:25:43.087> Total number of objects successfully migrated :2346, failed objects :16"),
3099
            (1, 24, (O_L, 2020, 5, 27, 12, 25, 43, 87000000), "{05/27/2020-12:25:43.087] Total number of objects successfully migrated :2346, failed objects :16"),
3100
            (1, 24, (O_L, 2020, 5, 27, 12, 25, 43, 87000000), "[05/27/2020-12:25:43.087> Total number of objects successfully migrated :2346, failed objects :16"),
3101
            (1, 24, (O_L, 2020, 5, 27, 12, 25, 43, 87000000), "{05/27/2020-12:25:43.087] Total number of objects successfully migrated :2346, failed objects :16"),
3102
        ],
3103
        line!(),
3104
    ),
3105
    //
3106
    // ---------------------------------------------------------------------------------------------
3107
    // from the beginning of file `C:/Windows/INF/setupapi.setup.log`,
3108
    // the `Boot Session` is printed once at the beginning, then it prints
3109
    // `Section [start|end]`
3110
    //
3111
    //     [Device Install Log]
3112
    //          OS Version = 10.0.22621
3113
    //          Service Pack = 0.0
3114
    //          Suite = 0x0100
3115
    //          ProductType = 1
3116
    //          Architecture = amd64
3117
    //
3118
    //     [BeginLog]
3119
    //
3120
    //     [Boot Session: 2023/02/21 07:06:52.500]
3121
    //
3122
    //     >>>  [Sysprep Specialize - {8effc0f9-2fb5-a7ad-8a21-581222c83283}]
3123
    //     >>>  Section start 2023/02/21 07:06:59.461
3124
    //       set: System Information:
3125
    //       set:      BIOS Release Date: 11/11/2023
3126
    //       set:      BIOS Vendor: American Megatrends Inc.
3127
    //       set:      BIOS Version: 2003
3128
    //       set:      System Family: To be filled by O.E.M.
3129
    //       set:      System Manufacturer: ASUS
3130
    //         set: Initialized PnP data. Time = 47 ms. 07:06:59.524
3131
    //         set: Cleaned up unneeded PnP data. Time = 0 ms. 07:07:05.689
3132
    //         set: Installed primitive drivers. Time = 15 ms. 07:07:05.706
3133
    //     <<<  Section end 2023/02/21 07:07:05.710
3134
    //     <<<  [Exit status: SUCCESS]
3135

3136
    //
3137
    // ---------------------------------------------------------------------------------------------
3138
    //
3139
    // from file `./logs/Ubuntu18/vmware-installer.log`
3140
    // example with offset:
3141
    //
3142
    //               1         2
3143
    //     012345678901234567890123456789
3144
    //     [2019-05-06 11:24:34,074] Successfully loaded GTK libraries.
3145
    //
3146
    // ---------------------------------------------------------------------------------------------
3147
    //               1         2         3         4
3148
    //     01234567890123456789012345678901234567890
3149
    //     [ERROR] 2000-01-02 12:33:01 -1200 1
3150
    //     [WARNING] 2000-01-02 12:33:02 -1130 22
3151
    //     [INFO] 2000-01-02 12:33:03 +1100 333
3152
    //     [VERBOSE] 2000-01-02T12:33:04 -1030 4444
3153
    //     [TRACE] 2000-01-02T12:33:05 -1000 55555
3154
    //
3155
    // ---------------------------------------------------------------------------------------------
3156
    // from file `./logs/synology/usbcopyd.log`
3157
    //
3158
    // example with offset:
3159
    //
3160
    //               1         2         3
3161
    //     0123456789012345678901234567890
3162
    //     2017-05-24T19:14:38-07:00 hostname1 usb-copy-starter
3163
    //
3164
    // ---------------------------------------------------------------------------------------------
3165
    // from NTP statistics logging files `loopstats`, `clockstats`, `peerstats`, etc...
3166
    //
3167
    // example with offset:
3168
    //
3169
    //               1         2
3170
    //     012345678901234567890
3171
    //     59955 725.605 -0.002167105 47.876 0.012528010 1.558579 9
3172
    //
3173
    // according to https://docs.ntpsec.org/latest/monopt.html
3174
    //
3175
    //     59955 is the modified Julian Day number
3176
    //     725.605 time of day (s) past midnight UTC
3177
    //
3178
    // other examples from http://www.ntp.org/ntpfaq/NTP-s-trouble.htm
3179
    //
3180
    // ---------------------------------------------------------------------------------------------
3181
    //
3182
    // prescripted datetime+tz
3183
    //
3184
    //               1         2
3185
    //     012345678901234567890123456789
3186
    //     2000-01-02 12:33:05 -0400 foo
3187
    //     2000-01-02 12:33:05 -04:00 foo
3188
    //     2000-01-02T12:33:05 -0400 foo
3189
    //     2000-01-02T12:33:05 -04:00 foo
3190
    //
3191
    //               1         2
3192
    //     012345678901234567890123456789
3193
    //     2000-01-02 12:33:05,123 -0400 foo
3194
    //     2000-01-02 12:33:05,123 -04:00 foo
3195
    //     2000-01-02T12:33:05,123 -0400 foo
3196
    //     2000-01-02T12:33:05,123 -04:00 foo
3197
    //
3198
    //               1         2
3199
    //     012345678901234567890123456789
3200
    //     2000-01-02 12:33:05.123456 foo
3201
    //
3202
    //               1         2         3
3203
    //     0123456789012345678901234567890
3204
    //     2000-01-02 12:33:05 foo
3205
    //     2000-01-02 12:33:05 foo
3206
    //     2000-01-02T12:33:05 foo
3207
    //     2000-01-02T12:33:05 foo
3208
    //
3209
    // ---------------------------------------------------------------------------------------------
3210
    //               1         2         3
3211
    //     0123456789012345678901234567890
3212
    //     [ERROR] 2000-01-02 12:33:01 1
3213
    //     [WARNING] 2000-01-02T12:33:02 22
3214
    //     [INFO] 2000-01-02T12:33:03 333
3215
    //     [VERBOSE] 2000-01-02 12:33:04 4444
3216
    //     [TRACE] 2000-01-02 12:33:05 55555
3217
    //
3218
    // ---------------------------------------------------------------------------------------------
3219
    // from file `./logs/Ubuntu18/vmware/hostd-62.log`
3220
    // example with offset:
3221
    //
3222
    //               1         2         3         4
3223
    //     01234567890123456789012345678901234567890
3224
    //     2019-07-26T10:40:29.682-07:00 info hostd[03210] [Originator@6876 sub=Default] Current working directory: /usr/bin
3225
    //
3226
    DTPD!(
3227
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZZ, RP_NOALPHA),
3228
        DTFSS_BdHMSYZ, 0, 40, CGN_MONTH, CGN_TZ,
3229
        &[
3230
            (0, 30, (O_PWT, 2000, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 2000 PWT hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3231
            (0, 24, (O_PWT, 2000, 1, 3, 1, 2, 4, 0), "Jan 03 01:02:04 2000 PWT 😀"),
3232
            (0, 24, (O_PWT, 2000, 1, 3, 1, 2, 4, 0), "Jan  3 01:02:04 2000 PWT 😀"),
3233
            (0, 23, (O_PWT, 2000, 1, 3, 1, 2, 4, 0), "Jan 3 01:02:04 2000 PWT 😀"),
3234
            (0, 24, (O_PWT, 2000, 2, 29, 1, 2, 4, 0), "Feb 29 01:02:04 2000 pwt 😀"),
3235
        ],
3236
        line!(),
3237
    ),
3238
    DTPD!(
3239
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZzc, RP_NOALPHA),
3240
        DTFSS_BdHMSYzc, 0, 40, CGN_MONTH, CGN_TZ,
3241
        &[
3242
            (0, 33, (O_P3, 2000, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 2000 +03:00 hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3243
            (0, 27, (O_P3, 2000, 1, 3, 1, 2, 4, 0), "Jan 03 01:02:04 2000 +03:00 😀"),
3244
            (0, 26, (O_M730, 2000, 1, 3, 1, 2, 4, 0), "Jan 3 01:02:04 2000 -07:30 😀"),
3245
            (0, 27, (O_M730, 2000, 1, 3, 1, 2, 4, 0), "Jan  3 01:02:04 2000 -07:30 😀"),
3246
            (0, 27, (O_P3, 2000, 2, 29, 1, 2, 4, 0), "Feb 29 01:02:04 2000 +03:00 😀"),
3247
        ],
3248
        line!(),
3249
    ),
3250
    DTPD!(
3251
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZz, RP_NOALPHA),
3252
        DTFSS_BdHMSYz, 0, 40, CGN_MONTH, CGN_TZ,
3253
        &[
3254
            (0, 32, (O_P3, 2000, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 2000 +0300 hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3255
            (0, 26, (O_P3, 2000, 1, 3, 1, 2, 4, 0), "Jan 03 01:02:04 2000 +0300 😀"),
3256
            (0, 26, (O_P3, 2000, 1, 3, 1, 2, 4, 0), "Jan  3 01:02:04 2000 +0300 😀"),
3257
            (0, 25, (O_M730, 2000, 1, 2, 3, 4, 5, 0), "Jan 2 03:04:05 2000 -0730 😀"),
3258
            (0, 26, (O_P3, 2000, 2, 29, 1, 2, 4, 0), "Feb 29 01:02:04 2000 +0300 😀"),
3259
        ],
3260
        line!(),
3261
    ),
3262
    DTPD!(
3263
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZzp, RP_NOALPHA),
3264
        DTFSS_BdHMSYzp, 0, 40, CGN_MONTH, CGN_TZ,
3265
        &[
3266
            (0, 30, (O_P3, 2000, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 2000 +03 hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3267
            (0, 24, (O_P3, 2000, 1, 5, 1, 2, 4, 0), "Jan 05 01:02:04 2000 +03 😀"),
3268
            (0, 24, (O_P3, 2000, 2, 29, 1, 2, 4, 0), "Feb 29 01:02:04 2000 +03 😀"),
3269
        ],
3270
        line!(),
3271
    ),
3272
    DTPD!(
3273
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_NOALNUM),
3274
        DTFSS_BdHMSY, 0, 35, CGN_MONTH, CGN_YEAR,
3275
        &[
3276
            (0, 26, (O_L, 2000, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 2000:hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3277
            (0, 20, (O_L, 2000, 1, 2, 3, 4, 5, 0), "Jan 02 03:04:05 2000|😀"),
3278
            (0, 19, (O_L, 2000, 1, 5, 1, 2, 4, 0), "Jan 5 01:02:04 2000 😀"),
3279
            (0, 20, (O_L, 2000, 2, 29, 1, 2, 4, 0), "Feb 29 01:02:04 2000 😀"),
3280
        ],
3281
        line!(),
3282
    ),
3283
    DTPD!(
3284
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_TZZ, RP_NOALPHA),
3285
        DTFSS_BdHMSZ, 0, 35, CGN_MONTH, CGN_TZ,
3286
        &[
3287
            (0, 25, (O_PWT, YD, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 PWT hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3288
            (0, 19, (O_PWT, YD, 1, 2, 3, 4, 5, 0), "Jan 02 03:04:05 pwt 😀"),
3289
            (0, 18, (O_PWT, YD, 1, 2, 3, 4, 5, 0), "Jan 2 03:04:05 PWT 😀"),
3290
            (0, 19, (O_PWT, YD, 2, 29, 1, 2, 4, 0), "Feb 29 01:02:04 PWT 😀"),
3291
        ],
3292
        line!(),
3293
    ),
3294
    // ---------------------------------------------------------------------------------------------
3295
    // from file `./logs/Ubuntu18/kernel.log`, no year
3296
    // example with offset:
3297
    //
3298
    //               1         2
3299
    //     012345678901234567890
3300
    //     Mar  9 08:10:29 hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode
3301
    //     Mar 09 08:10:29 hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode
3302
    //
3303
    // example with offset:
3304
    //
3305
    //               1         2
3306
    //     012345678901234567890123456789
3307
    //     Sep 03 13:47:07 server1 kern.warn kernel: [57377.167342] DROP IN=eth0 OUT= MAC=ff:ff:ff:ff:ff:ff:01:cc:d0:a8:c8:32:08:00 SRC=68.161.226.20 DST=255.255.255.255 LEN=139 TOS=0x00 PREC=0x20 TTL=64 ID=0 DF PROTO=UDP SPT=33488 DPT=10002 LEN=119
3308
    //     September  3 13:47:07 server1 kern.warn kernel: [57377.167342] DROP IN=eth0 OUT= MAC=ff:ff:ff:ff:ff:ff:01:cc:d0:a8:c8:32:08:00 SRC=68.161.226.20 DST=255.255.255.255 LEN=139 TOS=0x00 PREC=0x20 TTL=64 ID=0 DF PROTO=UDP SPT=33488 DPT=10002 LEN=119
3309
    //     September 3 13:47:07 server1 kern.warn kernel: [57377.167342] DROP IN=eth0 OUT= MAC=ff:ff:ff:ff:ff:ff:01:cc:d0:a8:c8:32:08:00 SRC=68.161.226.20 DST=255.255.255.255 LEN=139 TOS=0x00 PREC=0x20 TTL=64 ID=0 DF PROTO=UDP SPT=33488 DPT=10002 LEN=119
3310
    //
3311
    DTPD!(
3312
        concatcp!("^", CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKSq),
3313
        DTFSS_BdHMS, 0, 22, CGN_MONTH, CGN_SECOND,
3314
        &[
3315
            (0, 21, (O_L, YD, 9, 3, 8, 10, 29, 0), "September 03 08:10:29 hostname1 kernel: [1013319.252568] device vethb356a02 entered promiscuous mode"),
3316
            (0, 15, (O_L, YD, 1, 2, 3, 4, 5, 0), "Jan 02 03:04:05 1900 😀"),
3317
            (0, 14, (O_L, YD, 1, 2, 3, 4, 5, 0), "Jan 2 03:04:05 1900 😀"),
3318
            (0, 19, (O_L, YD, 1, 3, 13, 47, 7, 0), "January 03 13:47:07 server1 kern.warn kernel: [57377.167342] DROP IN=eth0 OUT= MAC=ff:ff:ff:ff:ff:ff:01:cc:d0:a8:c8:32:08:00 SRC=68.161.226.20 DST=255.255.255.255 LEN=139 TOS=0x00 PREC=0x20 TTL=64 ID=0 DF PROTO=UDP SPT=33488 DPT=10002 LEN=119"),
3319
        ],
3320
        line!(),
3321
    ),
3322
    // ---------------------------------------------------------------------------------------------
3323
    // from file `/var/log/aptitude`
3324
    // example with offset:
3325
    //
3326
    //               1         2
3327
    //     012345678901234567890
3328
    //     ===============================================================================
3329
    //
3330
    //     Aptitude 0.8.13: log report
3331
    //     Tue, Jun 28 2022 01:51:12 +0000
3332
    //
3333
    //       IMPORTANT: this log only lists intended actions; actions which fail
3334
    //       due to dpkg problems may not be completed.
3335
    //
3336
    //     Will install 1 packages, and remove 0 packages.
3337
    //     4833 kB of disk space will be used
3338
    //     ========================================
3339
    //     [HOLD, DEPENDENCIES] libnss-systemd:amd64 249.11-0ubuntu3.1
3340
    //     [HOLD, DEPENDENCIES] libpam-systemd:amd64 249.11-0ubuntu3.1
3341
    //     [HOLD, DEPENDENCIES] libsystemd0:amd64 249.11-0ubuntu3.1
3342
    //     [HOLD, DEPENDENCIES] libudev1:amd64 249.11-0ubuntu3.1
3343
    //     [HOLD, DEPENDENCIES] systemd:amd64 249.11-0ubuntu3.1
3344
    //     [HOLD, DEPENDENCIES] systemd-sysv:amd64 249.11-0ubuntu3.1
3345
    //     [HOLD, DEPENDENCIES] systemd-timesyncd:amd64 249.11-0ubuntu3.1
3346
    //     [HOLD, DEPENDENCIES] udev:amd64 249.11-0ubuntu3.1
3347
    //     [INSTALL] p7zip-full:amd64 16.02+dfsg-8
3348
    //     ========================================
3349
    //
3350
    //     Log complete.
3351
    //
3352
    //     ===============================================================================
3353
    //
3354
    DTPD!(
3355
        concatcp!("^", CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZz, RP_NODIGIT),
3356
        DTFSS_BdHMSYz, 0, 45, CGN_DAYa, CGN_TZ,
3357
        &[
3358
            (0, 30, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "mon Jun 28 2022 01:51:12 +1230"),
3359
            (0, 31, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "tue. Jun 28 2022 01:51:12 +1230"),
3360
            (0, 31, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Mon. Jun 28 2022 01:51:12 +1230"),
3361
            (0, 30, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Mon Jun 28 2022 01:51:12 +1230"),
3362
            (0, 31, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Mon. Jun 28 2022 01:51:12 +1230"),
3363
            (0, 31, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Mon Jun. 28 2022 01:51:12 +1230"),
3364
            (0, 32, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Mon. Jun. 28 2022 01:51:12 +1230"),
3365
            (0, 30, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "Wed Jun 02 2022 01:51:12 +1230"),
3366
            (0, 29, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "thu Jun 2 2022 01:51:12 +1230"),
3367
            (0, 33, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Friday Jun 28 2022 01:51:12 +1230"),
3368
            (0, 34, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "monday, Jun 28 2022 01:51:12 +1230"),
3369
            (0, 30, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Tue Jun 28 2022 01:51:12 +1230 FOOBAR"),
3370
            (0, 31, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Tue, Jun 28 2022 01:51:12 +1230"),
3371
            (0, 35, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Tuesday. Jun 28 2022 01:51:12 +1230 FOOBAR"),
3372
            (0, 35, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "TUESDAY. Jun 28 2022 01:51:12 +1230 FOOBAR"),
3373
        ],
3374
        line!(),
3375
    ),
3376
    DTPD!(
3377
        concatcp!("^", CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZzc, RP_NODIGIT),
3378
        DTFSS_BdHMSYzc, 0, 45, CGN_DAYa, CGN_TZ,
3379
        &[
3380
            (0, 31, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "WED Jun 28 2022 01:51:12 +01:30"),
3381
            (0, 32, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "Wed, Jun 28 2022 01:51:12 +01:30"),
3382
            (0, 32, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "wed. Jun 28 2022 01:51:12 +01:30 FOOBAR"),
3383
            (0, 32, (O_P1_30, 2022, 6, 2, 1, 51, 12, 0), "wed. Jun 02 2022 01:51:12 +01:30 FOOBAR"),
3384
            (0, 31, (O_P1_30, 2022, 6, 2, 1, 51, 12, 0), "wed. Jun 2 2022 01:51:12 +01:30 FOOBAR"),
3385
            (0, 37, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "Wednesday Jun 28 2022 01:51:12 +01:30"),
3386
            (0, 38, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "Wednesday, Jun 28 2022 01:51:12 +01:30"),
3387
            (0, 31, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "thu Jun 28 2022 01:51:12 +01:30 FOOBAR"),
3388
        ],
3389
        line!(),
3390
    ),
3391
    DTPD!(
3392
        concatcp!("^", CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZzp, RP_NODIGIT),
3393
        DTFSS_BdHMSYzp, 0, 45, CGN_DAYa, CGN_TZ,
3394
        &[
3395
            (0, 34, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "THURSDAY, Jun 28 2022 01:51:12 +01"),
3396
            (0, 34, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "thursday, Jun 28 2022 01:51:12 +01"),
3397
            (0, 29, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "fri. Jun 28 2022 01:51:12 +01 FOOBAR"),
3398
            (0, 29, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "fri, Jun 28 2022 01:51:12 +01"),
3399
            (0, 28, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "fri, Jun 2 2022 01:51:12 +01"),
3400
            (0, 31, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "FRIDAY Jun 28 2022 01:51:12 +01 FOOBAR"),
3401
        ],
3402
        line!(),
3403
    ),
3404
    DTPD!(
3405
        concatcp!("^", CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZZ, RP_NOALPHA),
3406
        DTFSS_BdHMSYZ, 0, 45, CGN_DAYa, CGN_TZ,
3407
        &[
3408
            (0, 34, (O_WIT, 2022, 6, 28, 1, 51, 12, 0), "Saturday, Jun 28 2022 01:51:12 WIT"),
3409
            (0, 30, (O_WITA, 2022, 6, 28, 1, 51, 12, 0), "SAT, Jun 28 2022 01:51:12 WITA:FOOBAR"),
3410
            (0, 29, (O_WST, 2022, 6, 28, 1, 51, 12, 0), "SAT. Jun 28 2022 01:51:12 WST FOOBAR"),
3411
            (0, 29, (O_YAKT, 2022, 6, 28, 1, 51, 12, 0), "sun Jun 28 2022 01:51:12 YAKT"),
3412
            (0, 28, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "sun Jun 2 2022 01:51:12 YAKT"),
3413
            (0, 32, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "sunday Jun 28 2022 01:51:12 YEKT FOOBAR"),
3414
            (0, 32, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "sunday Jun 28 2022 01:51:12 yekt FOOBAR"),
3415
            (0, 32, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "SUNDAY Jun 28 2022 01:51:12 YEKT FOOBAR"),
3416
            (0, 33, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "SUNDAY, Jun 28 2022 01:51:12 YEKT FOOBAR"),
3417
            //(0, 30, (O_EDT, 2018, 11, 13, 22, 55, 18, 0), "Sat, 13 Oct 2018 22:55:18 EDT hello this datetime stamp from https://dencode.com/date/rfc2822")
3418
        ],
3419
        line!(),
3420
    ),
3421
    // ---------------------------------------------------------------------------------------------
3422
    //
3423
    // RFC 2822
3424
    //
3425
    // https://dencode.com/date/rfc2822 uses leading zero for day of month
3426
    // https://www.rfc-editor.org/rfc/rfc2822.html#page-41 no leading zero for day of month
3427
    //
3428
    DTPD!(
3429
        concatcp!("^", CGP_DAYa3, ",", RP_BLANK, CGP_DAYde, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_YEAR, RP_cq, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZz, RP_NODIGIT),
3430
        DTFSS_BdHMSYz, 0, 45, CGN_DAYa, CGN_TZ,
3431
        &[
3432
            (0, 31, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Mon, 28 Jun 2022 01:51:12 +1230"),
3433
            (0, 31, (O_M5, 2018, 10, 7, 22, 55, 18, 0), "Sat, 07 Oct 2018 22:55:18 -0500 hello this datetime stamp from https://dencode.com/date/rfc2822"),
3434
            (0, 30, (O_P2, 2003, 7, 1, 10, 52, 37, 0), "Tue, 1 Jul 2003 10:52:37 +0200 from https://www.rfc-editor.org/rfc/rfc2822.html#page-41"),
3435
        ],
3436
        line!(),
3437
    ),
3438
    DTPD!(
3439
        concatcp!("^", CGP_DAYa3, ",", RP_BLANK, CGP_DAYde, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_YEAR, RP_cq, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZzc, RP_NODIGIT),
3440
        DTFSS_BdHMSYzc, 0, 45, CGN_DAYa, CGN_TZ,
3441
        &[
3442
            (0, 32, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "Mon, 28 Jun 2022 01:51:12 +01:30"),
3443
            (0, 32, (O_M5, 2018, 10, 7, 22, 55, 18, 0), "Sat, 07 Oct 2018 22:55:18 -05:00 hello this datetime stamp from https://dencode.com/date/rfc2822"),
3444
            (0, 31, (O_P2, 2003, 7, 1, 10, 52, 37, 0), "Tue, 1 Jul 2003 10:52:37 +02:00 from https://www.rfc-editor.org/rfc/rfc2822.html#page-41"),
3445
        ],
3446
        line!(),
3447
    ),
3448
    DTPD!(
3449
        concatcp!("^", CGP_DAYa3, ",", RP_BLANK, CGP_DAYde, RP_BLANK, CGP_MONTHb, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZZ, RP_NOALPHA),
3450
        DTFSS_BdHMSYZ, 0, 45, CGN_DAYa, CGN_TZ,
3451
        &[
3452
            (0, 29, (O_WIT, 2022, 6, 28, 1, 51, 12, 0), "Mon, 28 Jun 2022 01:51:12 WIT"),
3453
            (0, 29, (O_EDT, 2018, 10, 7, 22, 55, 18, 0), "Sat, 07 Oct 2018 22:55:18 EDT hello this datetime stamp from https://dencode.com/date/rfc2822"),
3454
            (0, 29, (O_CAT, 2003, 7, 1, 10, 52, 37, 0), "Tue, 1 Jul 2003 10:52:37  CAT from https://www.rfc-editor.org/rfc/rfc2822.html#page-41"),
3455
        ],
3456
        line!(),
3457
    ),
3458
    // RFC 2822 with leading field title "Date:"
3459
    // taken from https://www.rfc-editor.org/rfc/rfc2822#appendix-A.1
3460
    DTPD!(
3461
        concatcp!("^", RP_RFC2822_DATE, RP_BLANKq, CGP_DAYa3, ",", RP_BLANK, CGP_DAYde, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_YEAR, RP_cq, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZz, RP_NODIGIT),
3462
        DTFSS_BdHMSYz, 0, 45, CGN_DAYa, CGN_TZ,
3463
        &[
3464
            (6, 37, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "Date:        Mon, 28 Jun 2022 01:51:12 +1230"),
3465
            (6, 37, (O_M5, 2018, 10, 7, 22, 55, 18, 0), "DATE: Sat, 07 Oct 2018 22:55:18 -0500 hello this datetime stamp from https://dencode.com/date/rfc2822"),
3466
            (5, 35, (O_P2, 2003, 7, 1, 10, 52, 37, 0), "date:tue, 1 jul 2003 10:52:37 +0200 from https://www.rfc-editor.org/rfc/rfc2822.html#page-41"),
3467
        ],
3468
        line!(),
3469
    ),
3470
    DTPD!(
3471
        concatcp!("^", RP_RFC2822_DATE, RP_BLANKq, CGP_DAYa3, ",", RP_BLANK, CGP_DAYde, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_YEAR, RP_cq, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZzc, RP_NODIGIT),
3472
        DTFSS_BdHMSYzc, 0, 45, CGN_DAYa, CGN_TZ,
3473
        &[
3474
            (6, 38, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "Date:        Mon, 28 Jun 2022 01:51:12 +01:30"),
3475
            (6, 38, (O_M5, 2018, 10, 7, 22, 55, 18, 0), "DATE: Sat, 07 Oct 2018 22:55:18 -05:00 hello this datetime stamp from https://dencode.com/date/rfc2822"),
3476
            (5, 36, (O_P2, 2003, 7, 1, 10, 52, 37, 0), "date:tue, 1 jul 2003 10:52:37 +02:00 from https://www.rfc-editor.org/rfc/rfc2822.html#page-41"),
3477
        ],
3478
        line!(),
3479
    ),
3480
    DTPD!(
3481
        concatcp!("^", RP_RFC2822_DATE, RP_BLANKq, CGP_DAYa3, ",", RP_BLANK, CGP_DAYde, RP_BLANK, CGP_MONTHb, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZZ, RP_NOALPHA),
3482
        DTFSS_BdHMSYZ, 0, 45, CGN_DAYa, CGN_TZ,
3483
        &[
3484
            (6, 35, (O_WIT, 2022, 6, 28, 1, 51, 12, 0), "Date:        Mon, 28 Jun 2022 01:51:12 WIT"),
3485
            (6, 35, (O_EDT, 2018, 10, 7, 22, 55, 18, 0), "DATE: Sat, 07 Oct 2018 22:55:18 EDT hello this datetime stamp from https://dencode.com/date/rfc2822"),
3486
            (5, 34, (O_CAT, 2003, 7, 1, 10, 52, 37, 0), "date:tue, 1 jul 2003 10:52:37  CAT from https://www.rfc-editor.org/rfc/rfc2822.html#page-41"),
3487
        ],
3488
        line!(),
3489
    ),
3490
    // ---------------------------------------------------------------------------------------------
3491
    // from file `/var/log/apt/history.log`
3492
    // example with offset:
3493
    //
3494
    //               1         2         3         4
3495
    //     01234567890123456789012345678901234567890
3496
    //     Start-Date: 2022-07-18  19:34:46
3497
    //     Commandline: apt-get install -y gnupg2
3498
    //     Install: gnupg2:amd64 (2.2.27-3ubuntu2.1)
3499
    //     End-Date: 2022-07-18  19:35:04
3500
    //     Start-Date: 2022-07-31  19:13:42
3501
    //     Commandline: apt-get -qq install -y ca-certificates gnupg2 apt-utils apt-transport-https curl
3502
    //     Install: apt-transport-https:amd64 (2.4.6)
3503
    //     Upgrade: apt:amd64 (2.4.5, 2.4.6), libapt-pkg6.0:amd64 (2.4.5, 2.4.6), apt-utils:amd64 (2.4.5, 2.4.6)
3504
    //
3505
    DTPD!(
3506
        concatcp!("^(start|Start|START|end|End|END)[- ]?(date|Date|DATE)", D_T, RP_BLANKSq, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NODIGIT),
3507
        DTFSS_YmdHMS, 0, 35, CGN_YEAR, CGN_SECOND,
3508
        &[
3509
            (12, 32, (O_L, 2022, 7, 18, 19, 35, 1, 0), "Start-Date: 2022-07-18  19:35:01\nCommandline: apt-get install -y gnupg2\nInstall: gnupg2:amd64 (2.2.27-3ubuntu2.1)\n"),
3510
            (10, 30, (O_L, 2022, 7, 18, 19, 35, 2, 0), "End-Date: 2022-07-18  19:35:02\n"),
3511
            (10, 30, (O_L, 2022, 7, 18, 19, 35, 3, 0), "End-Date: 2022-07-18  19:35:03"),
3512
            (9, 29, (O_L, 2022, 7, 18, 19, 35, 4, 0), "End-Date:2022-07-18  19:35:04"),
3513
            (9, 28, (O_L, 2022, 7, 18, 19, 35, 5, 0), "End Date:2022-07-18 19:35:05\n"),
3514
            (9, 28, (O_L, 2022, 7, 18, 19, 35, 6, 0), "End-Date 2022-07-18 19:35:06\n"),
3515
            (10, 29, (O_L, 2022, 7, 18, 19, 35, 7, 0), "END-DATE  2022-07-18 19:35:07 Foobar"),
3516
            (10, 29, (O_L, 2022, 7, 18, 19, 35, 7, 0), "END DATE                2022-07-18 19:35:07        Foobar"),
3517
            (9, 28, (O_L, 2022, 7, 18, 19, 35, 7, 0), "END-DATE        2022-07-18 19:35:07 Foobar"),
3518
            (10, 29, (O_L, 2022, 7, 18, 19, 35, 7, 0), "END-DATE:        2022-07-18 19:35:07 Foobar"),
3519
            (9, 28, (O_L, 2022, 7, 18, 19, 35, 8, 0), "end-date 2022-07-18T19:35:08 Foobar"),
3520
            (14, 33, (O_L, 2022, 7, 18, 19, 35, 9, 0), "START-DATE:   2022-07-18 19:35:09\nCommandline: apt-get install -y gnupg2\n"),
3521
            (11, 30, (O_L, 2022, 7, 18, 19, 35, 9, 0), "STARTDATE:        2022/07/18 19:35:09\nCommandline: apt-get install -y gnupg2\n"),
3522
        ],
3523
        line!(),
3524
    ),
3525
    // ---------------------------------------------------------------------------------------------    // from file `./logs/debian9/alternatives.log`
3526
    // example with offset:
3527
    //
3528
    //               1         2         3         4
3529
    //     01234567890123456789012345678901234567890123456789
3530
    //     update-alternatives 2020-02-03 13:56:07: run with --install /usr/bin/jjs jjs /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs 1111
3531
    //
3532
    // ---------------------------------------------------------------------------------------------
3533
    // from file `./logs/Ubuntu18/cups/error_log`
3534
    // example with offset:
3535
    //
3536
    //               1         2         3
3537
    //     0123456789012345678901234567890
3538
    //     E [09/Aug/2019:00:09:01 -0700] Unable to open listen socket for address [v1.::1]:631 - Cannot assign requested address.
3539
    //
3540
    // ---------------------------------------------------------------------------------------------
3541
    // from file `./logs/OpenSUSE15/zypper.log`
3542
    // example with offset:
3543
    //
3544
    //               1         2
3545
    //     012345678901234567890
3546
    //     2019-05-23 16:53:43 <1> trenker(24689) [zypper] main.cc(main):74 ===== Hi, me zypper 1.14.27
3547
    //
3548
    // ---------------------------------------------------------------------------------------------
3549
    // from file `./logs/synology-DS6/synoreport.log`
3550
    // example with offset:
3551
    //
3552
    //               1         2
3553
    //     012345678901234567890
3554
    //     2017-05-14 04-00-07: -------------------- report start
3555
    DTPD!(
3556
        concatcp!(CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, ":", RP_BLANKe),
3557
        DTFSS_YmdHMS, 0, 30, CGN_YEAR, CGN_SECOND,
3558
        &[
3559
            (0, 19, (O_L, 2017, 5, 14, 4, 0, 7, 0), "2017-05-14 04-00-07:"),
3560
            (0, 19, (O_L, 2017, 5, 14, 4, 0, 8, 0), "2017-05-14 04-00-08: "),
3561
            (0, 19, (O_L, 2017, 5, 14, 4, 0, 9, 0), "2017-05-14 04-00-09: -------------------- report start"),
3562
        ],
3563
        line!(),
3564
    ),
3565
    // ---------------------------------------------------------------------------------------------
3566
    //
3567
    // matches of datetimes within a "guard" symbols.
3568
    //
3569
    // from file `./logs/Debian11/apache2/access.log`
3570
    // example with offset:
3571
    //
3572
    //               1         2         3         4         5
3573
    //     012345678901234567890123456789012345678901234567890
3574
    //     192.168.0.172 - - [11/Oct/2022:00:10:26 +0000] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1"
3575
    //
3576
    // from file `./logs/Debian9/nginx/access.log`
3577
    // example with offset:
3578
    //
3579
    //               1         2         3         4         5
3580
    //     012345678901234567890123456789012345678901234567890
3581
    //     192.168.0.8 - - [06/Mar/2020:06:30:43 -0800] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72"
3582
    //
3583
    DTPD!(
3584
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZz, RP_RB),
3585
        DTFSS_bdHMSYz, 0, 300, CGN_DAY, CGN_TZ,
3586
        &[
3587
            (19, 45, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - [11/Oct/2022:00:10:26 +0100] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3588
            (19, 45, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - {11/oct/2022 00:10:26 +0100} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3589
            (15, 40, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172        <11-oct-2022 00:10:26+0100>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3590
            (17, 43, (O_M8, 2020, 3, 7, 6, 30, 43, 0), r#"192.168.0.8 - - [07/Mar/2020:06:30:43 -0800] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3591
        ],
3592
        line!(),
3593
    ),
3594
    DTPD!(
3595
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZZ, RP_RB),
3596
        DTFSS_bdHMSYZ, 0, 300, CGN_DAY, CGN_TZ,
3597
        &[
3598
            (19, 44, (O_CHUT, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - [11/Oct/2022:00:10:26 CHUT] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3599
            (19, 44, (O_CHUT, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - {11/oct/2022 00:10:26 CHUT} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3600
            (15, 40, (O_CHUT, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172        <11-oct-2022 00:10:26 CHUT>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3601
            (15, 32, (O_Z, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172        <11OCT2022T001026Z>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3602
            (17, 42, (O_CHUT, 2020, 3, 7, 6, 30, 43, 0), r#"192.168.0.8 - - [07/Mar/2020:06:30:43 CHUT] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3603
        ],
3604
        line!(),
3605
    ),
3606
    DTPD!(
3607
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzc, RP_RB),
3608
        DTFSS_bdHMSYzc, 0, 300, CGN_DAY, CGN_TZ,
3609
        &[
3610
            (19, 46, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - [11/Oct/2022:00:10:26 +01:00] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3611
            (19, 46, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - {11/oct/2022 00:10:26 +01:00} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3612
            (15, 41, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172        <11-oct-2022 00:10:26+01:00>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3613
            (17, 44, (O_M8, 2020, 3, 7, 6, 30, 43, 0), r#"192.168.0.8 - - [07/Mar/2020:06:30:43 -08:00] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3614
        ],
3615
        line!(),
3616
    ),
3617
    DTPD!(
3618
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzp, RP_RB),
3619
        DTFSS_bdHMSYzp, 0, 300, CGN_DAY, CGN_TZ,
3620
        &[
3621
            (19, 43, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - [11/Oct/2022:00:10:26 +01] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3622
            (19, 43, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172 - - {11/oct/2022 00:10:26 +01} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3623
            (15, 38, (O_P1, 2022, 10, 11, 0, 10, 26, 0), r#"192.168.0.172        <11-oct-2022 00:10:26+01>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3624
            (17, 41, (O_M8, 2020, 3, 7, 6, 30, 43, 0), r#"192.168.0.8 - - [07/Mar/2020:06:30:43 -08] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3625
        ],
3626
        line!(),
3627
    ),
3628
    DTPD!(
3629
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, RP_RB),
3630
        DTFSS_bdHMSY, 0, 300, CGN_DAY, CGN_SECOND,
3631
        &[
3632
            // Flask web server default log format
3633
            (15, 35, (O_L, 2024, 3, 22, 15, 11, 28, 0), r#"127.0.0.1 - - [22/Mar/2024 15:11:28] "GET / HTTP/1.1" 200 -"#),
3634
        ],
3635
        line!(),
3636
    ),
3637
    // prior patterns with fractionals 1-9
3638
    DTPD!(
3639
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, RP_RB),
3640
        DTFSS_bdHMSYfz, 0, 300, CGN_DAY, CGN_TZ,
3641
        &[
3642
            (19, 49, (O_P1, 2022, 10, 11, 0, 10, 26, 123000000), r#"192.168.0.172 - - [11/Oct/2022:00:10:26.123 +0100] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3643
            (19, 48, (O_P1, 2022, 10, 11, 0, 10, 26, 110000000), r#"192.168.0.172 - - {11/oct/2022 00:10:26.11 +0100} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3644
            (15, 42, (O_P1, 2022, 10, 11, 0, 10, 26, 100000000), r#"192.168.0.172        <11-oct-2022 00:10:26.1+0100>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3645
            (17, 53, (O_M8, 2020, 3, 7, 6, 30, 43, 123456789), r#"192.168.0.8 - - [07/Mar/2020:06:30:43.123456789 -0800] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3646
        ],
3647
        line!(),
3648
    ),
3649
    DTPD!(
3650
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, RP_RB),
3651
        DTFSS_bdHMSYfzc, 0, 300, CGN_DAY, CGN_TZ,
3652
        &[
3653
            (19, 50, (O_P1, 2022, 10, 11, 0, 10, 26, 123000000), r#"192.168.0.172 - - [11/Oct/2022:00:10:26.123 +01:00] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3654
            (19, 49, (O_P1, 2022, 10, 11, 0, 10, 26, 110000000), r#"192.168.0.172 - - {11/oct/2022 00:10:26.11 +01:00} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3655
            (15, 43, (O_P1, 2022, 10, 11, 0, 10, 26, 100000000), r#"192.168.0.172        <11-oct-2022 00:10:26.1+01:00>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3656
            (17, 54, (O_M8, 2020, 3, 7, 6, 30, 43, 123456789), r#"192.168.0.8 - - [07/Mar/2020:06:30:43.123456789 -08:00] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3657
        ],
3658
        line!(),
3659
    ),
3660
    DTPD!(
3661
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, RP_RB),
3662
        DTFSS_bdHMSYfzp, 0, 300, CGN_DAY, CGN_TZ,
3663
        &[
3664
            (19, 47, (O_P1, 2022, 10, 11, 0, 10, 26, 123000000), r#"192.168.0.172 - - [11/Oct/2022:00:10:26.123 +01] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3665
            (19, 46, (O_P1, 2022, 10, 11, 0, 10, 26, 110000000), r#"192.168.0.172 - - {11/oct/2022 00:10:26.11 +01} "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3666
            (15, 40, (O_P1, 2022, 10, 11, 0, 10, 26, 100000000), r#"192.168.0.172        <11-oct-2022 00:10:26.1+01>        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3667
            (17, 51, (O_M8, 2020, 3, 7, 6, 30, 43, 123456789), r#"192.168.0.8 - - [07/Mar/2020:06:30:43.123456789 -08] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3668
        ],
3669
        line!(),
3670
    ),
3671
    DTPD!(
3672
        concatcp!(RP_LB, CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKSq, RP_RB),
3673
        DTFSS_bdHMSYf, 0, 300, CGN_DAY, CGN_FRACTIONAL,
3674
        &[
3675
            (19, 43, (O_L, 2022, 10, 11, 0, 10, 26, 123000000), r#"192.168.0.172 - - [11/Oct/2022:00:10:26.123] "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3676
            (19, 42, (O_L, 2022, 10, 11, 0, 10, 26, 110000000), r#"192.168.0.172 - - {11/oct/2022 00:10:26.11 } "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3677
            (15, 37, (O_L, 2022, 10, 11, 0, 10, 26, 100000000), r#"192.168.0.172        <11-oct-2022 00:10:26.1        >        "GET / HTTP/1.0" 200 3343 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1""#),
3678
            (17, 47, (O_L, 2020, 3, 7, 6, 30, 43, 123456789), r#"192.168.0.8 - - [07/Mar/2020:06:30:43.123456789] "GET /path2/feed.rss HTTP/1.1" 404 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 OPR/66.0.3515.72""#),
3679
        ],
3680
        line!(),
3681
    ),
3682
    // prior patterns with numeric month, no timezone
3683
    //
3684
    // from file `./logs/Windows11Pro/setupact.log`
3685
    //
3686
    //      [02/21/2023 07:07.05.259] WudfCoInstaller: ReadWdfSection: Checking WdfSection [Basic_Install.Wdf]
3687
    //
3688
    DTPD!(
3689
        concatcp!(RP_LB, CGP_MONTHm, D_Dq, CGP_DAYde, D_Dq, CGP_YEAR, RP_BLANKS, CGP_HOUR, D_T, CGP_MINUTE, r"[:\.]?", CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_RB),
3690
        DTFSS_YmdHMSf, 0, 300, CGN_MONTH, CGN_FRACTIONAL,
3691
        &[
3692
            (1, 24, (O_L, 2023, 2, 21, 7, 7, 5, 262000000), "[02/21/2023 07:07.05.262] WudfCoInstaller: Configuring UMDF Service WpdFs.\n\n"),
3693
            (18, 41, (O_L, 2023, 2, 21, 7, 7, 5, 263000000), "WudfCoInstaller: [02/21/2023 07:07.05.263] ImpersonationLevel set to 2\n\n"),
3694
        ],
3695
        line!(),
3696
    ),
3697
    //
3698
    // from file `./logs/Debian11/apache/error.log`
3699
    // example with offset:
3700
    //
3701
    //               1         2         3         4
3702
    //     01234567890123456789012345678901234567890
3703
    //     [Mon Oct 10 23:56:29.204202 2022] [mpm_event:notice] [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations
3704
    //
3705
    DTPD!(
3706
        concatcp!(RP_LB, CGP_DAYa, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_DAYde, RP_BLANKS, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANK, CGP_YEAR, RP_RB),
3707
        DTFSS_bdHMSYf, 0, 300, CGN_DAYa, CGN_YEAR,
3708
        &[
3709
            (1, 32, (O_L, 2022, 10, 10, 23, 56, 29, 204202000), "[Mon Oct 10 23:56:29.204202 2022] [mpm_event:notice] [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations"),
3710
            (20, 51, (O_L, 2022, 10, 10, 23, 56, 29, 204202000), "[mpm_event:notice]        <Mon Oct 10        23:56:29.204202 2022> [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations"),
3711
            (20, 48, (O_L, 2022, 10, 30, 23, 56, 29, 204000000), "[mpm_event:notice]        <sun Oct 30        23:56:29.204 2022> [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations"),
3712
            (20, 54, (O_L, 2022, 10, 5, 23, 56, 29, 204948193), "[mpm_event:notice]        <WED oct 05        23:56:29.204948193 2022> [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations"),
3713
        ],
3714
        line!(),
3715
    ),
3716
    DTPD!(
3717
        concatcp!(RP_LB, CGP_DAYa, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_DAYde, RP_BLANKS, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_RB),
3718
        DTFSS_bdHMSY, 0, 300, CGN_DAYa, CGN_YEAR,
3719
        &[
3720
            (1, 25, (O_L, 2022, 10, 10, 23, 56, 29, 0), "[Mon Oct 10 23:56:29 2022] [mpm_event:notice] [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations"),
3721
            (20, 44, (O_L, 2022, 10, 10, 23, 56, 29, 0), "[mpm_event:notice]        (Mon Oct 10        23:56:29 2022) [pid 11709:tid 140582486756672] AH00489: Apache/2.4.54 (Debian) configured -- resuming normal operations"),
3722
        ],
3723
        line!(),
3724
    ),
3725
    // ---------------------------------------------------------------------------------------------
3726
    //
3727
    // tomcat catalina stdout log format, from file `./logs/programs/tomcat/catalina.out`
3728
    // example with offset:
3729
    //
3730
    //               1         2         3
3731
    //     0123456789012345678901234567890
3732
    //     08-Feb-2023 12:12:09.827 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio2-0.0.0.0-8080"]
3733
    //
3734
    DTPD!(
3735
        concatcp!("^", CGP_DAYde, D_Dq, CGP_MONTHb, D_Dq, CGP_YEAR, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL3, RP_NOALNUMpm),
3736
        DTFSS_bdHMSYf, 0, 30, CGN_DAY, CGN_FRACTIONAL,
3737
        &[
3738
            (0, 24, (O_L, 2023, 2, 8, 12, 13, 9, 827000000), r#"08-Feb-2023 12:13:09.827 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio2-0.0.0.0-8080"]"#),
3739
            (0, 24, (O_L, 2023, 2, 8, 12, 13, 20, 63000000), "08-Feb-2023 12:13:20.063 INFO [localhost-startStop-1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.\nSLF4J: Class path contains multiple SLF4J bindings."),
3740
        ],
3741
        line!(),
3742
    ),
3743
    DTPD!(
3744
        concatcp!(RP_NOALNUMb, "(START|END|Start|End|start|end)", RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_YEAR, D_Deq, CGP_MONTHms, D_Deq, CGP_DAYde, D_DHcdqu, CGP_HOURs, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NODIGIT),
3745
        DTFSS_YmsdkMS, 0, 1024, CGN_YEAR, CGN_SECOND,
3746
        &[
3747
            // from `C:/Windows/Performance/WinSAT/winsat.log`
3748
            // a datetime format with redundant `AM` and `PM`, see Issue #64
3749
            // with single-digit month and single-month hour
3750
            (50, 67, (O_L, 2023, 2, 22, 4, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- START 2023\2\22 4:05:07 AM ---1"),
3751
            (50, 67, (O_L, 2023, 2, 22, 1, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- START 2023\2\22 1:05:07 AM ---2"),
3752
            (50, 68, (O_L, 2023, 2, 22, 12, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- Start 2023\2\22 12:05:07 AM ---3"),
3753
            (50, 69, (O_L, 2023, 11, 22, 12, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- start 2023\11\22 12:05:07 AM ---4"),
3754
            (48, 67, (O_L, 2023, 11, 22, 12, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- End 2023\11\22 12:05:07 AM ---5"),
3755
        ],
3756
        line!(),
3757
    ),
3758
    // TODO: add new `DTPD!` copied from prior `DTPD!` with varying timezones and more lenient fractional
3759
    //
3760
    // ---------------------------------------------------------------------------------------------
3761
    // from file `./logs/synology/synoupdate.log`
3762
    // example with offset:
3763
    //
3764
    //               1         2         3
3765
    //     0123456789012345678901234567890
3766
    //     2016/12/05 21:34:43        Start of the update…
3767
    //
3768
    // ---------------------------------------------------------------------------------------------
3769
    //
3770
    // from file `./logs/synology-DS6/synolog/synobackup.log`
3771
    // example with offset:
3772
    //
3773
    //               1         2         3         4
3774
    //     01234567890123456789012345678901234567890
3775
    //     info        2017/02/21 21:50:48        SYSTEM:        [Local][Backup Task Backup1] Backup task started.
3776
    //     err        2017/02/23 02:55:58        SYSTEM:        [Local][Backup Task Backup1] Exception occurred while backing up data. (Capacity at destination is insufficient.) [Path: /share4/usbshare/Backup1.hbk]
3777
    //     err        2017/02/23 02:56:03        SYSTEM:        [Local][Backup Task Backup1] Failed to backup data.
3778
    //     info        2017/02/24 02:30:04        SYSTEM:        [Local][Backup Task Backup1] Backup task started.
3779
    //     warning        2017/02/24 03:43:57        SYSTEM:        [Local][Backup Task Backup1] Backup folder [Vol/DS] failed. (The backup source shared folder is encrypted and not mounted. Please mount the backup source shared folder and try again.)
3780
    //
3781
    // other examples:
3782
    //
3783
    //               1         2         3         4
3784
    //     01234567890123456789012345678901234567890
3785
    //     INFO: Thu Feb 20 00:59:59 2020 info
3786
    //     ERROR: Thu Feb 20 00:59:59 2020 error
3787
    //     DEBUG: Thu Feb 20 00:59:59 2020 debug
3788
    //     VERBOSE: Thu Feb 20 00:59:59 2020 verbose
3789
    //
3790
    //               1         2         3         4
3791
    //     01234567890123456789012345678901234567890
3792
    //     INFO: Sat Jan 01 2000 08:45:55 info
3793
    //     WARN: Sat Jan 01 2000 08:45:55 warn
3794
    //     ERROR: Sat Jan 01 2000 08:45:55 error
3795
    //     DEBUG: Sun Jan 02 2000 21:00:00 debug
3796
    //     VERBOSE: Sat Jan 01 2000 08:45:55 verbose
3797
    //
3798
    DTPD!(
3799
        concatcp!("^", RP_LEVELS, "[:]?", RP_BLANKSq, CGP_DAYa, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzc, RP_NODIGIT),
3800
        DTFSS_YbdHMSzc, 0, 60, CGN_DAYa, CGN_TZ,
3801
        &[
3802
            (7, 38, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "TRACE:        Sat Jan 01 2000 08:45:55 +09:00 TRACE: ⇥ ×1‼"),
3803
            (8, 39, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "trace0:        sat jan 01 2000 08:45:55 +09:00 trace0: ⇥ ×1‼"),
3804
            (9, 40, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "TRACE1:                Sat Jan 01 2000 08:45:55 +09:00 TRACE1: ⇥ ×2‼"),
3805
            (8, 39, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "TRACE2:        Sat Jan 01 2000 08:45:55 +09:00 TRACE2: ⇥ ×1‼"),
3806
            (7, 38, (O_P9, 2000, 1, 2, 21, 0, 0, 0), "DEBUG: Sun Jan 02 2000 21:00:00 +09:00 DEBUG:‼"),
3807
            (7, 38, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "debug: sat jan 01 2000 08:45:55 +09:00 debug:‼"),
3808
            (7, 38, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "DEBUG0 Sat Jan 01 2000 08:45:55 +09:00 debug0‼"),
3809
            (8, 39, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "DEBUG9: Sat Jan 01 2000 08:45:55 +09:00 debug9:‼"),
3810
            (6, 37, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "INFO: Sat Jan 01 2000 08:45:55 -09:00 info:‼"),
3811
            (7, 38, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "INFO2: Sat Jan 01 2000 08:45:55 -09:00 info2:‼"),
3812
            (9, 40, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning: Sat Jan 01 2000 08:45:55 -09:00 warning:‼"),
3813
            (8, 39, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning sat jan 01 2000 08:45:55 -09:00 warning‼"),
3814
            (8, 39, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning        Sat Jan 01 2000 08:45:55 -09:00 warning ⇥ ×1‼"),
3815
            (8, 39, (O_M9, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN 03 2000 23:30:59 -09:00 warning ⇥ ×1‼"),
3816
            (8, 40, (O_M9, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON. JAN 03 2000 23:30:59 -09:00 warning ⇥ ×1‼"),
3817
            (8, 40, (O_M9, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN. 03 2000 23:30:59 -09:00 warning ⇥ ×1‼"),
3818
            (8, 41, (O_M9, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON. JAN. 03 2000 23:30:59 -09:00 warning ⇥ ×1‼"),
3819
            (9, 40, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning                Sat Jan 01 2000 08:45:55 -09:00 warning ⇥ ×2‼"),
3820
            (6, 37, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "WARN: SAT JAN 01 2000 08:45:55 -09:00 warn:‼"),
3821
            (7, 38, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "ERROR: Sat Jan 01 2000 08:45:55 -09:00 error:‼"),
3822
            (5, 36, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "ERR: Sat Jan 01 2000 08:45:55 -09:00 err:‼"),
3823
        ],
3824
        line!(),
3825
    ),
3826
    DTPD!(
3827
        concatcp!("^", RP_LEVELS, "[:]?", RP_BLANKSq, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZz, RP_NODIGIT),
3828
        DTFSS_YbdHMSz, 0, 60, CGN_DAYa, CGN_TZ,
3829
        &[
3830
            (7, 37, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "TRACE:        Sat Jan 01 2000 08:45:55 +0900 TRACE: ⇥ ×1‼"),
3831
            (8, 38, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "trace0:        sat jan 01 2000 08:45:55 +0900        trace0: ⇥ ×1‼"),
3832
            (9, 39, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "TRACE1:                Sat Jan 01 2000 08:45:55 +0900                TRACE1: ⇥ ×2‼"),
3833
            (8, 38, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "TRACE2:        Sat Jan 01 2000 08:45:55 +0900        TRACE2: ⇥ ×1‼"),
3834
            (7, 37, (O_P9, 2000, 1, 2, 21, 0, 0, 0), "DEBUG: Sun Jan 02 2000 21:00:00 +0900 DEBUG:‼"),
3835
            (7, 37, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "debug: sat jan 01 2000 08:45:55 +0900 debug:‼"),
3836
            (7, 37, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "DEBUG0 Sat Jan 01 2000 08:45:55 +0900 debug0‼"),
3837
            (8, 38, (O_P9, 2000, 1, 1, 8, 45, 55, 0), "DEBUG9: Sat Jan 01 2000 08:45:55 +0900 debug9:‼"),
3838
            (6, 36, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "INFO: Sat Jan 01 2000 08:45:55 -0900 info:‼"),
3839
            (7, 37, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "INFO2: Sat Jan 01 2000 08:45:55 -0900 info2:‼"),
3840
            (9, 39, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning: Sat Jan 01 2000 08:45:55 -0900 warning:‼"),
3841
            (9, 40, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning: Sat. Jan 01 2000 08:45:55 -0900 warning:‼"),
3842
            (9, 40, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning: Sat Jan. 01 2000 08:45:55 -0900 warning:‼"),
3843
            (9, 41, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning: Sat. Jan. 01 2000 08:45:55 -0900 warning:‼"),
3844
            (8, 38, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning sat jan 01 2000 08:45:55 -0900 warning‼"),
3845
            (8, 38, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning        Sat Jan 01 2000 08:45:55 -0900        warning ⇥ ×1‼"),
3846
            (8, 38, (O_M9, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN 03 2000 23:30:59 -0900        warning ⇥ ×1‼"),
3847
            (9, 39, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "warning                Sat Jan 01 2000 08:45:55 -0900                warning ⇥ ×2‼"),
3848
            (6, 36, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "WARN: SAT JAN 01 2000 08:45:55 -0900 warn:‼"),
3849
            (7, 37, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "ERROR: Sat Jan 01 2000 08:45:55 -0900 error:‼"),
3850
            (5, 35, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "ERR: Sat Jan 01 2000 08:45:55 -0900 err:‼"),
3851
            (5, 39, (O_M9, 2000, 1, 1, 8, 45, 55, 0), "ERR: Sat January 01 2000 08:45:55 -0900 err:‼"),
3852
        ],
3853
        line!(),
3854
    ),
3855
    DTPD!(
3856
        concatcp!("^", RP_LEVELS, "[:]?", RP_BLANKSq, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzp, RP_NODIGIT),
3857
        DTFSS_YbdHMSzp, 0, 60, CGN_DAYa, CGN_TZ,
3858
        &[
3859
            (7, 35, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "TRACE:        Sat Jan 31 2000 08:45:55 +09 TRACE: ⇥ ×1‼"),
3860
            (8, 36, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "trace0:        sat jan 31 2000 08:45:55 +09        trace0: ⇥ ×1‼"),
3861
            (9, 37, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "TRACE1:                Sat Jan 31 2000 08:45:55 +09                TRACE1: ⇥ ×2‼"),
3862
            (8, 36, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "TRACE2:        Sat Jan 31 2000 08:45:55 +09        TRACE2: ⇥ ×1‼"),
3863
            (7, 35, (O_P9, 2000, 1, 2, 21, 0, 0, 0), "DEBUG: Sun Jan 02 2000 21:00:00 +09 DEBUG:‼"),
3864
            (7, 35, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "debug: sat jan 31 2000 08:45:55 +09 debug:‼"),
3865
            (7, 35, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "DEBUG0 Sat Jan 31 2000 08:45:55 +09 debug0‼"),
3866
            (8, 36, (O_P9, 2000, 1, 31, 8, 45, 55, 0), "DEBUG9: Sat Jan 31 2000 08:45:55 +09 debug9:‼"),
3867
            (6, 34, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "INFO: Sat Jan 31 2000 08:45:55 -09 info:‼"),
3868
            (7, 35, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "INFO2: Sat Jan 31 2000 08:45:55 -09 info2:‼"),
3869
            (9, 37, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning: Sat Jan 31 2000 08:45:55 -09 warning:‼"),
3870
            (8, 36, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning sat jan 31 2000 08:45:55 -09 warning‼"),
3871
            (8, 37, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning sat. jan 31 2000 08:45:55 -09 warning‼"),
3872
            (8, 37, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning sat jan. 31 2000 08:45:55 -09 warning‼"),
3873
            (8, 38, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning sat. jan. 31 2000 08:45:55 -09 warning‼"),
3874
            (8, 36, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning        Sat Jan 31 2000 08:45:55 -09        warning ⇥ ×1‼"),
3875
            (8, 36, (O_M9, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN 03 2000 23:30:59 -09        warning ⇥ ×1‼"),
3876
            (9, 37, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "warning                Sat Jan 31 2000 08:45:55 -09                warning ⇥ ×2‼"),
3877
            (6, 34, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "WARN: SAT JAN 31 2000 08:45:55 -09 warn:‼"),
3878
            (7, 35, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "ERROR: Sat Jan 31 2000 08:45:55 -09 error:‼"),
3879
            (5, 33, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "ERR: sat jan 31 2000 08:45:55 -09 err:‼"),
3880
            (4, 32, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "err SAT JAN 31 2000 08:45:55 -09 err:‼"),
3881
            (4, 36, (O_M9, 2000, 1, 31, 8, 45, 55, 0), "err SAT JANUARY 31 2000 08:45:55 -09 err:‼"),
3882
        ],
3883
        line!(),
3884
    ),
3885
    DTPD!(
3886
        concatcp!("^", RP_LEVELS, "[:]?", RP_BLANKSq, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZZ, RP_NOALPHA),
3887
        DTFSS_YbdHMSZ, 0, 65, CGN_DAYa, CGN_TZ,
3888
        &[
3889
            (7, 35, (O_PST, 2000, 1, 31, 8, 45, 55, 0), "TRACE:        Sat Jan 31 2000 08:45:55 PST"),
3890
            (7, 35, (O_PST, 2000, 1, 31, 8, 45, 55, 0), "TRACE:        Sat Jan 31 2000 08:45:55 PST TRACE: ⇥ ×1‼"),
3891
            (8, 36, (O_PET, 2000, 1, 31, 8, 45, 55, 0), "trace0:        sat jan 31 2000 08:45:55 pet        trace0: ⇥ ×1‼"),
3892
            (9, 38, (O_PETT, 2000, 1, 31, 8, 45, 55, 0), "TRACE1:                Sat Jan 31 2000 08:45:55 PETT        TRACE1: ⇥ ×2‼"),
3893
            (8, 37, (O_WITA, 2000, 1, 31, 8, 45, 55, 0), "TRACE2:        Sat Jan 31 2000 08:45:55 WITA        TRACE2: ⇥ ×1‼"),
3894
            (7, 36, (O_WITA, 2000, 1, 2, 21, 45, 55, 0), "DEBUG: Sun Jan 02 2000 21:45:55 WITA DEBUG:‼"),
3895
            (7, 36, (O_WITA, 2000, 1, 31, 8, 45, 55, 0), "debug: sat jan 31 2000 08:45:55 wita debug:‼"),
3896
            (7, 36, (O_WITA, 2000, 1, 31, 8, 45, 55, 0), "DEBUG0 Sat Jan 31 2000 08:45:55 WITA debug0‼"),
3897
            (8, 37, (O_WITA, 2000, 1, 31, 8, 45, 55, 0), "DEBUG9: Sat Jan 31 2000 08:45:55 WITA debug9:‼"),
3898
            (6, 35, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "INFO: Sat Jan 31 2000 08:45:55 PONT info:‼"),
3899
            (7, 36, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "INFO2: Sat Jan 31 2000 08:45:55 PONT info2:‼"),
3900
            (9, 38, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning: Sat Jan 31 2000 08:45:55 pont warning:‼"),
3901
            (9, 39, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning: Sat. Jan 31 2000 08:45:55 pont warning:‼"),
3902
            (9, 39, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning: Sat Jan. 31 2000 08:45:55 pont warning:‼"),
3903
            (9, 40, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning: Sat. Jan. 31 2000 08:45:55 pont warning:‼"),
3904
            (8, 37, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning sat jan 31 2000 08:45:55 pont warning‼"),
3905
            (8, 37, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning        Sat Jan 31 2000 08:45:55 pont        warning ⇥ ×1‼"),
3906
            (8, 37, (O_PONT, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN 03 2000 23:30:59 PONT                warning ⇥ ×1‼"),
3907
            (9, 38, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "warning                Sat Jan 31 2000 08:45:55 pont        warning ⇥ ×2‼"),
3908
            (6, 35, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "WARN: SAT JAN 31 2000 08:45:55 PONT:warn:‼"),
3909
            (7, 36, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "ERROR: SAT jan 31 2000 08:45:55 PONT|error:‼"),
3910
            (5, 34, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "ERR: sat Jan 31 2000 08:45:55 PONT err:‼"),
3911
            (5, 34, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "ERR: Sat jan 31 2000 08:45:55 PONT err:‼"),
3912
            (5, 38, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "ERR: Sat january 31 2000 08:45:55 PONT err:‼"),
3913
            (5, 37, (O_PONT, 2000, 1, 31, 8, 45, 55, 0), "ERR: Sat january 31 2000 08:45:55PONT err:‼"),
3914
        ],
3915
        line!(),
3916
    ),
3917
    DTPD!(
3918
        concatcp!("^", RP_LEVELS, "[:]?", RP_BLANKSq, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NODIGIT),
3919
        DTFSS_YbdHMS, 0, 60, CGN_DAYa, CGN_SECOND,
3920
        &[
3921
            (7, 31, (O_L, 2000, 1, 31, 8, 45, 55, 0), "TRACE:        Sat Jan 31 2000 08:45:55 TRACE: ⇥ ×1‼"),
3922
            (8, 32, (O_L, 2000, 1, 31, 8, 45, 55, 0), "trace0:        sat jan 31 2000 08:45:55        trace0: ⇥ ×1‼"),
3923
            (9, 33, (O_L, 2000, 1, 31, 8, 45, 55, 0), "TRACE1:                Sat Jan 31 2000 08:45:55        TRACE1: ⇥ ×2‼"),
3924
            (8, 32, (O_L, 2000, 1, 31, 8, 45, 55, 0), "TRACE2:        Sat Jan 31 2000 08:45:55        TRACE2: ⇥ ×1‼"),
3925
            (7, 31, (O_L, 2000, 1, 2, 21, 0, 0, 0), "DEBUG: Sun Jan 02 2000 21:00:00 DEBUG:‼"),
3926
            (7, 31, (O_L, 2000, 1, 31, 8, 45, 55, 0), "debug: sat jan 31 2000 08:45:55 debug:‼"),
3927
            (7, 31, (O_L, 2000, 1, 31, 8, 45, 55, 0), "DEBUG0 Sat Jan 31 2000 08:45:55 debug0‼"),
3928
            (8, 32, (O_L, 2000, 1, 31, 8, 45, 55, 0), "DEBUG9: Sat Jan 31 2000 08:45:55 debug9:‼"),
3929
            (6, 30, (O_L, 2000, 1, 31, 8, 45, 55, 0), "INFO: Sat Jan 31 2000 08:45:55 info:‼"),
3930
            (7, 31, (O_L, 2000, 1, 31, 8, 45, 55, 0), "INFO2: Sat Jan 31 2000 08:45:55 info2:‼"),
3931
            (9, 33, (O_L, 2000, 1, 31, 8, 45, 55, 0), "warning: Sat Jan 31 2000 08:45:55 -09:00 warning:‼"),
3932
            (8, 32, (O_L, 2000, 1, 31, 8, 45, 55, 0), "warning sat jan 31 2000 08:45:55 warning‼"),
3933
            (8, 32, (O_L, 2000, 1, 31, 8, 45, 55, 0), "warning        Sat Jan 31 2000 08:45:55        warning ⇥ ×1‼"),
3934
            (8, 32, (O_L, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN 03 2000 23:30:59        warning ⇥ ×1‼"),
3935
            (8, 31, (O_L, 2000, 1, 3, 23, 30, 59, 0), "WARNING        MON JAN 3 2000 23:30:59        warning ⇥ ×1‼"),
3936
            (9, 33, (O_L, 2000, 1, 31, 8, 45, 55, 0), "warning                Sat Jan 31 2000 08:45:55                warning ⇥ ×2‼"),
3937
            (6, 30, (O_L, 2000, 1, 31, 8, 45, 55, 0), "WARN: SAT JAN 31 2000 08:45:55 warn:‼"),
3938
            (7, 31, (O_L, 2000, 1, 31, 8, 45, 55, 0), "ERROR: Sat Jan 31 2000 08:45:55 error:‼"),
3939
            (5, 29, (O_L, 2000, 1, 31, 8, 45, 55, 0), "ERR: Sat Jan 31 2000 08:45:55 err:‼"),
3940
            (5, 29, (O_L, 2000, 1, 31, 8, 45, 55, 0), "ERR: Sat JAN 31 2000 08:45:55 err:‼"),
3941
            (4, 32, (O_L, 2000, 1, 31, 8, 45, 55, 0), "ERR Sat JANUARY 31 2000 08:45:55 err:‼"),
3942
        ],
3943
        line!(),
3944
    ),
3945
    // ---------------------------------------------------------------------------------------------
3946
    //
3947
    // from file `./logs/Debian9/apport.log.1`
3948
    //
3949
    //               1         2         3         4         5         6
3950
    //     0123456789012345678901234567890123456789012345678901234567890
3951
    //     ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020: called for pid 8581, signal 24, core limit 0, dump mode 1
3952
    //     ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 -0700: called for pid 8581, signal 24, core limit 0, dump mode 1
3953
    //     ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 -07:00: called for pid 8581, signal 24, core limit 0, dump mode 1
3954
    //     ERROR: apport (pid 9) Thu Feb 20 00:59:59 2020: executable: /usr/lib/firefox/firefox (command line "/usr/lib/firefox/firefox"
3955
    //
3956
    DTPD!(
3957
        concatcp!("^", RP_LEVELS, "[:]?", RP_ANYp, RP_BLANK, CGP_DAYa, RP_BLANK, CGP_MONTHb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZzc, RP_NODIGIT),
3958
        DTFSS_YbdHMSzc, 0, 120, CGN_DAYa, CGN_TZ,
3959
        &[
3960
            (22, 53, (O_M7, 2020, 2, 27, 0, 33, 59, 0), "ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 -07:00: called for pid 8581, signal 24, core limit 0, dump mode 1"),
3961
            (27, 58, (O_M330, 2022, 8, 13, 8, 48, 3, 0), r#"ERROR: apport (pid 529343) Sat Aug 13 08:48:03 2022 -03:30: executable: [s4] (command line "./target/release/s4 -s -wp /dev")"#),
3962
        ],
3963
        line!(),
3964
    ),
3965
    DTPD!(
3966
        concatcp!("^", RP_LEVELS, "[:]?", RP_ANYp, RP_BLANK, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZz, RP_NODIGIT),
3967
        DTFSS_YbdHMSz, 0, 120, CGN_DAYa, CGN_TZ,
3968
        &[
3969
            (22, 52, (O_M7, 2020, 2, 27, 0, 33, 59, 0), "ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 -0700: called for pid 8581, signal 24, core limit 0, dump mode 1"),
3970
            (27, 57, (O_M330, 2022, 8, 13, 8, 48, 3, 0), r#"ERROR: apport (pid 529343) Sat Aug 13 08:48:03 2022 -0330: executable: [s4] (command line "./target/release/s4 -s -wp /dev")"#),
3971
        ],
3972
        line!(),
3973
    ),
3974
    DTPD!(
3975
        concatcp!("^", RP_LEVELS, "[:]?", RP_ANYp, RP_BLANK, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZzp, RP_NODIGIT),
3976
        DTFSS_YbdHMSzp, 0, 120, CGN_DAYa, CGN_TZ,
3977
        &[
3978
            (22, 50, (O_M7, 2020, 2, 27, 0, 33, 59, 0), "ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 -07: called for pid 8581, signal 24, core limit 0, dump mode 1"),
3979
            (27, 55, (O_M3, 2022, 8, 13, 8, 48, 3, 0), r#"ERROR: apport (pid 529343) Sat Aug 13 08:48:03 2022 -03: executable: [s4] (command line "./target/release/s4 -s -wp /dev")"#),
3980
        ],
3981
        line!(),
3982
    ),
3983
    DTPD!(
3984
        concatcp!("^", RP_LEVELS, "[:]?", RP_ANYp, RP_BLANK, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_BLANKS, CGP_TZZ, RP_NOALPHA),
3985
        DTFSS_YbdHMSZ, 0, 120, CGN_DAYa, CGN_TZ,
3986
        &[
3987
            (22, 51, (O_ALMT, 2020, 2, 27, 0, 33, 59, 0), "ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 ALMT called for pid 8581, signal 24, core limit 0, dump mode 1"),
3988
            (27, 56, (O_ALMT, 2022, 8, 13, 8, 48, 3, 0), r#"ERROR: apport (pid 529343) Sat Aug 13 08:48:03 2022 ALMT: executable: [s4] (command line "./target/release/s4 -s -wp /dev")"#),
3989
        ],
3990
        line!(),
3991
    ),
3992
    DTPD!(
3993
        concatcp!("^", RP_LEVELS, "[:]?", RP_ANYp, RP_BLANK, CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKS, CGP_YEAR, RP_NOALNUM),
3994
        DTFSS_YbdHMS, 0, 120, CGN_DAYa, CGN_YEAR,
3995
        &[
3996
            (22, 46, (O_L, 2020, 2, 27, 0, 33, 59, 0), "ERROR: apport (pid 9) Thu Feb 27 00:33:59 2020 called for pid 8581, signal 24, core limit 0, dump mode 1"),
3997
            (27, 51, (O_L, 2022, 8, 13, 8, 48, 3, 0), r#"ERROR: apport (pid 529343) Sat Aug 13 08:48:03 2022: executable: [s4] (command line "./target/release/s4 -s -wp /dev")"#),
3998
            (25, 49, (O_L, 2020, 2, 20, 0, 59, 59, 0), r#"ERROR: apport (pid 9359) Thu Feb 20 00:59:59 2020: executable: /usr/lib/firefox/firefox (command line "/usr/lib/firefox/firefox"#),
3999
            (27, 51, (O_L, 2023, 1, 8, 12, 53, 11, 0), "ERROR: apport (pid 150689) Sun Jan  8 12:53:11 2023: called for pid 150672, signal 6, core limit 0, dump mode 1\n"),
4000
        ],
4001
        line!(),
4002
    ),
4003
    // TODO: add `RP_LEVELS` regex for lines like:
4004
    //       <Notice>: 2024-03-24 19:46:10.665578 (pid/5566 [diskutil]) Service stub created for com.apple.audio.SandboxHelper
4005
    //       [INFO]        2024-03-24 19:46:10.665578        (pid/5566 [diskutil])        Service stub created for com.apple.audio.SandboxHelper
4006
    // ---------------------------------------------------------------------------------------------
4007
    //
4008
    // example with offset:
4009
    //
4010
    //               1         2         3         4
4011
    //     01234567890123456789012345678901234567890
4012
    //     2020-01-02 12:33:59.001 xyz
4013
    //
4014
    // ---------------------------------------------------------------------------------------------
4015
    //
4016
    // general matches from beginning of line
4017
    //
4018
    DTPD!(
4019
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, RP_NODIGIT),
4020
        DTFSS_YmdHMSfz, 0, 50, CGN_YEAR, CGN_TZ,
4021
        &[
4022
            (0, 29, (O_M11, 2000, 1, 2, 0, 0, 2, 123000000), "2000/01/02 00:00:02.123 -1100 a"),
4023

4024
        ],
4025
        line!(),
4026
    ),
4027
    DTPD!(
4028
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, RP_NODIGIT),
4029
        DTFSS_YmdHMSfzc, 0, 50, CGN_YEAR, CGN_TZ,
4030
        &[(0, 33, (O_M1130, 2000, 1, 3, 0, 0, 3, 123456000), "2000/01/03 00:00:03.123456 -11:30 ab")],
4031
        line!(),
4032
    ),
4033
    DTPD!(
4034
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, RP_NODIGIT),
4035
        DTFSS_YmdHMSfzp, 0, 50, CGN_YEAR, CGN_TZ,
4036
        &[(0, 33, (O_M11, 2000, 1, 4, 0, 0, 4, 123456789), "2000/01/04 00:00:04,123456789 -11 abc")],
4037
        line!(),
4038
    ),
4039
    DTPD!(
4040
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, RP_NOALPHA),
4041
        DTFSS_YmdHMSfZ, 0, 50, CGN_YEAR, CGN_TZ,
4042
        &[
4043
            (0, 34, (O_PETT, 2000, 1, 5, 0, 0, 5, 123456789), "2000/01/05 00:00:05.123456789 PETT abcd"),
4044
            (0, 33, (O_PETT, 2000, 1, 5, 0, 0, 5, 123456789), "2000/01/05 00:00:05.123456789PETT abcd"),
4045
            (0, 33, (O_PETT, 2000, 1, 5, 0, 0, 5, 123456789), "2000/01/05 00:00:05.123456789PETT:abcd"),
4046
            (0, 33, (O_PETT, 2000, 1, 5, 0, 0, 5, 123456789), "2000/01/05 00:00:05.123456789PETT|abcd"),
4047
        ],
4048
        line!(),
4049
    ),
4050
    DTPD!(
4051
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_NODIGIT),
4052
        DTFSS_YmdHMSf, 0, 50, CGN_YEAR, CGN_FRACTIONAL,
4053
        &[(0, 29, (O_L, 2020, 1, 6, 0, 0, 26, 123456789), "2020-01-06 00:00:26.123456789 abcdefg")],
4054
        line!(),
4055
    ),
4056
    //
4057
    DTPD!(
4058
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZz, RP_NODIGIT),
4059
        DTFSS_YmdHMSz, 0, 50, CGN_YEAR, CGN_TZ,
4060
        &[(0, 25, (O_M11, 2000, 1, 7, 0, 0, 2, 0), "2000/01/07T00:00:02 -1100 abcdefgh")],
4061
        line!(),
4062
    ),
4063
    DTPD!(
4064
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzc, RP_NODIGIT),
4065
        DTFSS_YmdHMSzc, 0, 50, CGN_YEAR, CGN_TZ,
4066
        &[
4067
            (0, 26, (O_M1130, 2000, 1, 8, 0, 0, 3, 0), "2000-01-08-00:00:03 -11:30 abcdefghi"),
4068
            // ISO 8601, time extended format
4069
            (0, 25, (O_M1, 2020, 1, 2, 3, 4, 5, 0), "2020-01-02T03:04:05-01:00 The standard uses the Gregorian calendar, which 'serves as an international standard for civil use'.[18]"),
4070
            // ISO 8601, time basic format
4071
            (0, 23, (O_M1, 2020, 1, 2, 3, 4, 5, 0), "2020-01-02T030405-01:00 ISO 8601:2004 fixes a reference calendar date to the Gregorian calendar of 20 May 1875 as the date the Convention du Mètre (Metre Convention) was signed in Paris (the explicit reference date was removed in ISO 8601-1:2019)."),
4072
            // ISO 8601, time extended format
4073
            (0, 23, (O_M1, 2020, 1, 2, 3, 4, 5, 0), "20200102T03:04:05-01:00 However, ISO calendar dates before the convention are still compatible with the Gregorian calendar all the way back to the official introduction of the Gregorian calendar on 15 October 1582."),
4074
            // ISO 8601, time extended format
4075
            (0, 23, (O_M1, 2020, 1, 2, 3, 4, 5, 0), "20200102T03:04:05-01:00 Calendar date representations are in the form shown in the adjacent box. [YYYY] indicates a four-digit year, 0000 through 9999. [MM] indicates a two-digit month of the year, 01 through 12. [DD] indicates a two-digit day of that month, 01 through 31."),
4076
            // ISO 8601 / RFC 3339, time basic format
4077
            (0, 21, (O_Z, 2020, 1, 2, 3, 4, 5, 0), "20200102T030405-00:00 IETF RFC 3339[43] defines a profile of ISO 8601 for use in Internet protocols and standards."),
4078
            // ISO 8601 / RFC 3339, time extended format
4079
            (0, 23, (O_Z, 2020, 1, 2, 3, 4, 5, 0), "20200102T03:04:05-00:00 RFC 3339 deviates from ISO 8601 in allowing a zero time zone offset to be specified as '-00:00;', which ISO 8601 forbids."),
4080
            // ISO 8601, time extended format using Unicode "minus sign".
4081
            //
4082
            // Uses non-ASCII pattern in capture data.
4083
            //
4084
            // The data passed to chrono `DateTime::parse_from_str` is modified;
4085
            // the Unicode "minus sign" is replaced with ASCII "hyphen-minus".
4086
            // However, the bytes that would be written to stdout remain
4087
            // unchanged (if this data had followed the full program path and
4088
            // been processed by the `printer::printers::PrinterSysline`).
4089
            // Hence, the offsets for `begin`, `end`, must account for Unicode
4090
            // char "minus sign" (which is larger than typical 1-byte ASCII).
4091
            (0, 27, (O_M1, 2020, 1, 2, 3, 4, 5, 0), "2020-01-02T03:04:05−01:00 To represent a negative offset, ISO 8601 specifies using a minus sign, (−) (U+2212)."),
4092

4093
        ],
4094
        line!(),
4095
    ),
4096
    DTPD!(
4097
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzp, RP_NODIGIT),
4098
        DTFSS_YmdHMSzp, 0, 50, CGN_YEAR, CGN_TZ,
4099
        &[
4100
            (0, 23, (O_M11, 2000, 1, 9, 0, 0, 4, 0), "2000/01/09 00:00:04 -11 abcdefghij"),
4101
            (0, 25, (O_M11, 2000, 1, 9, 0, 0, 4, 0), "2000/01/09 00:00:04 −11 abcdefghij"), // U+2212
4102
        ],
4103
        line!(),
4104
    ),
4105
    DTPD!(
4106
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZZ, RP_NOALPHA),
4107
        DTFSS_YmdHMSZ, 0, 50, CGN_YEAR, CGN_TZ,
4108
        &[
4109
            (0, 24, (O_VLAT, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05 VLAT abcdefghijk"),
4110
            (0, 23, (O_PST, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05 pst abcdefghijk"),
4111
            (0, 24, (O_VLAT, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05 VLAT "),
4112
            (0, 23, (O_PST, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05 pst\tfoo"),
4113
            (0, 24, (O_VLAT, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05 VLAT"),
4114
            (0, 23, (O_PST, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05 pst123"),
4115
            (0, 22, (O_PST, 2000, 1, 10, 0, 0, 5, 0), "2000/01/10T00:00:05pst:foo"),
4116
        ],
4117
        line!(),
4118
    ),
4119
    //
4120
    DTPD!(
4121
        concatcp!("^", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NODIGIT),
4122
        DTFSS_YmdHMS, 0, 50, CGN_YEAR, CGN_SECOND,
4123
        &[
4124
            (0, 19, (O_L, 2020, 1, 11, 0, 0, 26, 0), "2020-01-11 00:00:26 abcdefghijkl"),
4125
            (0, 19, (O_L, 2020, 1, 11, 0, 0, 26, 0), "2020-01-11 00:00:26 pstxxxxxxxxx"),
4126
            (0, 19, (O_L, 2020, 1, 11, 0, 0, 26, 0), "2020-01-11 00:00:26 −pstxxxxxxxxx"), // U+2212
4127
        ],
4128
        line!(),
4129
    ),
4130
    // ---------------------------------------------------------------------------------------------
4131
    // from file `./logs/synology-DS6/upstart/umount-root-fs.log`, Issue #44
4132
    // example with offset:
4133
    //
4134
    //               1         2         3
4135
    //     0123456789012345678901234567890
4136
    //     Mon Dec 5 21:01:12 PST 2016 try umount root [1] times
4137
    //     Wed Feb 28 14:58:07 PST 2018 try umount root [1] times
4138
    //
4139
    // CGP_DAY version
4140
    //
4141
    // timezone then year
4142
    DTPD!(
4143
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZZ, RP_BLANK12, CGP_YEAR, RP_NOALNUM),
4144
        DTFSS_BdHMSYZ, 0, 45, CGN_DAYa, CGN_YEAR,
4145
        &[
4146
            (0, 27, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 PST 2016 try umount root [1] times"),
4147
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 PST 2016 try umount root [1] times"),
4148
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 PST 2016 try umount root [1] times"),
4149
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 pst 2016 try umount root [1] times"),
4150
            (0, 31, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MONDAY dec  5 21:01:12 pst 2016 try umount root [1] times"),
4151
            (0, 31, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MONDAY DEC  5 21:01:12 PST 2016 try umount root [1] times"),
4152
            (0, 27, (O_PDT, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 PDT 2017 try umount root [1] times"),
4153
            (0, 28, (O_PST, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 PST 2018 try umount root [1] times"),
4154
            (0, 34, (O_PST, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 PST 2018 try umount root [1] times"),
4155
            (0, 40, (O_PST, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07\tPST\t\t2018 try umount root [1] times"),
4156
        ],
4157
        line!(),
4158
    ),
4159
    DTPD!(
4160
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZz, RP_BLANK, CGP_YEAR, RP_NOALNUM),
4161
        DTFSS_BdHMSYz, 0, 45, CGN_DAYa, CGN_YEAR,
4162
        &[
4163
            (0, 29, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 -0000 2016 try umount root [1] times"),
4164
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 −0000 2016 try umount root [1] times"), // U+2212
4165
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 +0000 2016 try umount root [1] times"),
4166
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 +0000 2016 try umount root [1] times"),
4167
            (0, 30, (O_M1130, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 -1130 2016 try umount root [1] times"),
4168
            (0, 29, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 +0945 2017 try umount root [1] times"),
4169
            (0, 32, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "monday may 8 08:33:00 +0945 2017 try umount root [1] times"),
4170
            (0, 30, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 -1030 2018 try umount root [1] times"),
4171
            (0, 36, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 -1030 2018 try umount root [1] times"),
4172
            (0, 41, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 -1030 2018 try umount root [1] times"),
4173
        ],
4174
        line!(),
4175
    ),
4176
    DTPD!(
4177
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZzc, RP_BLANK, CGP_YEAR, RP_NOALNUM),
4178
        DTFSS_BdHMSYzc, 0, 45, CGN_DAYa, CGN_YEAR,
4179
        &[
4180
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 -00:00 2016 try umount root [1] times"),
4181
            (0, 32, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 −00:00 2016 try umount root [1] times"), // U+2212
4182
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 +00:00 2016 try umount root [1] times"),
4183
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 +00:00 2016 try umount root [1] times"),
4184
            (0, 31, (O_M1130, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 -11:30 2016 try umount root [1] times"),
4185
            (0, 30, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 +09:45 2017 try umount root [1] times"),
4186
            (0, 33, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "monday may 8 08:33:00 +09:45 2017 try umount root [1] times"),
4187
            (0, 31, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 -10:30 2018 try umount root [1] times"),
4188
            (0, 37, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 -10:30 2018 try umount root [1] times"),
4189
            (0, 42, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 -10:30 2018 try umount root [1] times"),
4190
        ],
4191
        line!(),
4192
    ),
4193
    DTPD!(
4194
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZzp, RP_BLANK, CGP_YEAR, RP_NOALNUM),
4195
        DTFSS_BdHMSYzp, 0, 45, CGN_DAYa, CGN_YEAR,
4196
        &[
4197
            (0, 27, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 -00 2016 try umount root [1] times"),
4198
            (0, 28, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 +00 2016 try umount root [1] times"),
4199
            (0, 28, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 +00 2016 try umount root [1] times"),
4200
            (0, 28, (O_M11, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 -11 2016 try umount root [1] times"),
4201
            (0, 33, (O_M11, 2016, 12, 5, 21, 1, 12, 0), "mon december  5 21:01:12 -11 2016 try umount root [1] times"),
4202
            (0, 27, (O_P9, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 +09 2017 try umount root [1] times"),
4203
            (0, 27, (O_P9, 2017, 5, 8, 8, 33, 0, 0), "mon MAY 8 08:33:00 +09 2017 try umount root [1] times"),
4204
            (0, 28, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 -10 2018 try umount root [1] times"),
4205
            (0, 34, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 -10 2018 try umount root [1] times"),
4206
            (0, 39, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 -10 2018 try umount root [1] times"),
4207
            (0, 39, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY FEBRUARY 28 14:58:07 -10 2018 try umount root [1] times"),
4208
        ],
4209
        line!(),
4210
    ),
4211
    // year then timezone
4212
    DTPD!(
4213
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_YEAR, RP_BLANK12, CGP_TZZ, RP_NOALNUM),
4214
        DTFSS_BdHMSYZ, 0, 45, CGN_DAYa, CGN_TZ,
4215
        &[
4216
            (0, 27, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 PST try umount root [1] times"),
4217
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 2016 PST try umount root [1] times"),
4218
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MON DEC. 5 21:01:12 2016 PST try umount root [1] times"),
4219
            (0, 29, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MON. DEC. 5 21:01:12 2016 PST try umount root [1] times"),
4220
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 2016 PST try umount root [1] times"),
4221
            (0, 28, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 2016 pst try umount root [1] times"),
4222
            (0, 31, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MONDAY dec  5 21:01:12 2016 pst try umount root [1] times"),
4223
            (0, 31, (O_PST, 2016, 12, 5, 21, 1, 12, 0), "MONDAY DEC  5 21:01:12 2016 PST try umount root [1] times"),
4224
            (0, 27, (O_PDT, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 2017 PDT try umount root [1] times"),
4225
            (0, 28, (O_PST, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 2018 PST try umount root [1] times"),
4226
            (0, 34, (O_PST, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 2018 PST try umount root [1] times"),
4227
            (0, 39, (O_PST, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07\t2018\tPST try umount root [1] times"),
4228
        ],
4229
        line!(),
4230
    ),
4231
    DTPD!(
4232
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZz, RP_NOALNUM),
4233
        DTFSS_BdHMSYz, 0, 45, CGN_DAYa, CGN_TZ,
4234
        &[
4235
            (0, 29, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 -0000 try umount root [1] times"),
4236
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 −0000 try umount root [1] times"), // U+2212
4237
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 2016 +0000 try umount root [1] times"),
4238
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC. 5 21:01:12 2016 +0000 try umount root [1] times"),
4239
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 2016 +0000 try umount root [1] times"),
4240
            (0, 30, (O_M1130, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 2016 -1130 try umount root [1] times"),
4241
            (0, 29, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 2017 +0945 try umount root [1] times"),
4242
            (0, 32, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "monday may 8 08:33:00 2017 +0945 try umount root [1] times"),
4243
            (0, 30, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 2018 -1030 try umount root [1] times"),
4244
            (0, 36, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 2018 -1030 try umount root [1] times"),
4245
            (0, 41, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07\t2018\t-1030 try umount root [1] times"),
4246
        ],
4247
        line!(),
4248
    ),
4249
    DTPD!(
4250
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzc, RP_NOALNUM),
4251
        DTFSS_BdHMSYzc, 0, 45, CGN_DAYa, CGN_TZ,
4252
        &[
4253
            (0, 30, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 -00:00 try umount root [1] times"),
4254
            (0, 32, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 −00:00 try umount root [1] times"), // U+2212
4255
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 2016 +00:00 try umount root [1] times"),
4256
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC. 5 21:01:12 2016 +00:00 try umount root [1] times"),
4257
            (0, 31, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 2016 +00:00 try umount root [1] times"),
4258
            (0, 31, (O_M1130, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 2016 -11:30 try umount root [1] times"),
4259
            (0, 30, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 2017 +09:45 try umount root [1] times"),
4260
            (0, 33, (O_P945, 2017, 5, 8, 8, 33, 0, 0), "monday may 8 08:33:00 2017 +09:45 try umount root [1] times"),
4261
            (0, 31, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 2018 -10:30 try umount root [1] times"),
4262
            (0, 37, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 2018 -10:30 try umount root [1] times"),
4263
            (0, 42, (O_M1030, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 2018 -10:30 try umount root [1] times"),
4264
        ],
4265
        line!(),
4266
    ),
4267
    DTPD!(
4268
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzp, RP_NOALNUM),
4269
        DTFSS_BdHMSYzp, 0, 45, CGN_DAYa, CGN_TZ,
4270
        &[
4271
            (0, 27, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 -00 try umount root [1] times"),
4272
            (0, 28, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 2016 +00 try umount root [1] times"),
4273
            (0, 28, (O_Z, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 2016 +00 try umount root [1] times"),
4274
            (0, 28, (O_M11, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 2016 -11 try umount root [1] times"),
4275
            (0, 28, (O_M11, 2016, 12, 5, 21, 1, 12, 0), "mon dec. 5 21:01:12 2016 -11 try umount root [1] times"),
4276
            (0, 33, (O_M11, 2016, 12, 5, 21, 1, 12, 0), "mon december  5 21:01:12 2016 -11 try umount root [1] times"),
4277
            (0, 27, (O_P9, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 2017 +09 try umount root [1] times"),
4278
            (0, 27, (O_P9, 2017, 5, 8, 8, 33, 0, 0), "mon MAY 8 08:33:00 2017 +09 try umount root [1] times"),
4279
            (0, 28, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 2018 -10 try umount root [1] times"),
4280
            (0, 34, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 2018 -10 try umount root [1] times"),
4281
            (0, 39, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 2018 -10 try umount root [1] times"),
4282
            (0, 39, (O_M10, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY FEBRUARY 28 14:58:07 2018 -10 try umount root [1] times"),
4283
        ],
4284
        line!(),
4285
    ),
4286
    // no timezone
4287
    DTPD!(
4288
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_NOALNUM),
4289
        DTFSS_BdHMSY, 0, 45, CGN_DAYa, CGN_YEAR,
4290
        &[
4291
            (0, 23, (O_L, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 try umount root [1] times"),
4292
            (0, 24, (O_L, 2016, 12, 5, 21, 1, 12, 0), "MON DEC  5 21:01:12 2016 try umount root [1] times"),
4293
            (0, 24, (O_L, 2016, 12, 5, 21, 1, 12, 0), "MON DEC 05 21:01:12 2016 try umount root [1] times"),
4294
            (0, 24, (O_L, 2016, 12, 5, 21, 1, 12, 0), "mon dec  5 21:01:12 2016 try umount root [1] times"),
4295
            (0, 29, (O_L, 2016, 12, 5, 21, 1, 12, 0), "mon december  5 21:01:12 2016 try umount root [1] times"),
4296
            (0, 23, (O_L, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 2017 try umount root [1] times"),
4297
            (0, 23, (O_L, 2017, 5, 8, 8, 33, 0, 0), "mon MAY 8 08:33:00 2017 try umount root [1] times"),
4298
            (0, 24, (O_L, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 2018 try umount root [1] times"),
4299
            (0, 30, (O_L, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 2018 try umount root [1] times"),
4300
            (0, 35, (O_L, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 2018 try umount root [1] times"),
4301
            (0, 35, (O_L, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY FEBRUARY 28 14:58:07 2018 try umount root [1] times"),
4302
        ],
4303
        line!(),
4304
    ),
4305
    //
4306
    // from file `./logs/programs/proftpd/xferlog`, Issue #42
4307
    // example with offset:
4308
    //
4309
    //               1         2         3
4310
    //     0123456789012345678901234567890
4311
    //     Sat Oct 03 11:26:12 2020 0 192.168.0.8 0 /var/log/proftpd/xferlog b _ o r root ftp 0 * c
4312
    //     Sat Oct 03 11:26:12 2020 0 192.168.0.8 2323 /var/log/proftpd/proftpd.log b _ o r root ftp 0 * c
4313
    //
4314
    DTPD!(
4315
        concatcp!("^", CGP_DAYa, RP_BLANK, CGP_MONTHBb, RP_BLANKS, CGP_DAYde, RP_BLANK, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_NOALNUMpm),
4316
        DTFSS_BdHMSY, 0, 40, CGN_DAYa, CGN_YEAR,
4317
        &[
4318
            (0, 23, (O_L, 2016, 12, 5, 21, 1, 12, 0), "Mon Dec 5 21:01:12 2016 try umount root [1] times"),
4319
            (0, 24, (O_L, 2016, 12, 5, 21, 1, 13, 0), "MON DEC  5 21:01:13 2016 try umount root [1] times"),
4320
            (0, 24, (O_L, 2016, 12, 5, 21, 1, 14, 0), "mon dec  5 21:01:14 2016 try umount root [1] times"),
4321
            (0, 29, (O_L, 2016, 12, 5, 21, 1, 12, 0), "mon december  5 21:01:12 2016 try umount root [1] times"),
4322
            (0, 23, (O_L, 2017, 5, 8, 8, 33, 0, 0), "mon May 8 08:33:00 2017 try umount root [1] times"),
4323
            (0, 23, (O_L, 2017, 5, 8, 8, 33, 0, 0), "mon MAY 8 08:33:00 2017 try umount root [1] times"),
4324
            (0, 24, (O_L, 2018, 2, 28, 14, 58, 7, 0), "Wed Feb 28 14:58:07 2018 try umount root [1] times"),
4325
            (0, 30, (O_L, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY Feb 28 14:58:07 2018 try umount root [1] times"),
4326
            (0, 35, (O_L, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY February 28 14:58:07 2018 try umount root [1] times"),
4327
            (0, 35, (O_L, 2018, 2, 28, 14, 58, 7, 0), "WEDNESDAY FEBRUARY 28 14:58:07 2018 try umount root [1] times"),
4328
            (0, 24, (O_L, 2020, 10, 3, 11, 26, 12, 0), "Sat Oct  3 11:26:12 2020 0 192.168.0.8 2323 /var/log/proftpd/proftpd.log b _ o r root ftp 0 * c")
4329
        ],
4330
        line!(),
4331
    ),
4332
    // ---------------------------------------------------------------------------------------------
4333
    //
4334
    // file `logs/other/tests/dtf14a.log`
4335
    //
4336
    //                1         2         3         4
4337
    //      01234567890123456789012345678901234567890
4338
    //      2023 Aug 31 20:01:05 UTC [ERROR] dev-disk-a error 0x08320105
4339
    //      2023 Aug 31 20:01:09 UTC [WARNING] dev-disk-a disconnected.
4340
    //
4341
    DTPD!(
4342
        concatcp!("^", CGP_YEAR, RP_BLANK12, CGP_MONTHBb, RP_BLANK12q, CGP_DAYde, RP_BLANK12q, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12q, CGP_TZzc, RP_NOALNUM),
4343
        DTFSS_YbdHMSzc, 0, 30, CGN_YEAR, CGN_TZ,
4344
        &[
4345
            (0, 27, (O_0, 2023, 8, 31, 20, 1, 5, 0), "2023 Aug 31 20:01:05 -00:00 [ERROR] dev-disk-a error 0x08320105"),
4346
            (0, 27, (O_P1, 2023, 8, 31, 20, 1, 9, 0), "2023 Aug 31 20:01:09 +01:00 [WARNING] dev-disk-a disconnected."),
4347
        ],
4348
        line!(),
4349
    ),
4350
    DTPD!(
4351
        concatcp!("^", CGP_YEAR, RP_BLANK12, CGP_MONTHBb, RP_BLANK12q, CGP_DAYde, RP_BLANK12q, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12q, CGP_TZz, RP_NOALNUM),
4352
        DTFSS_YbdHMSz, 0, 30, CGN_YEAR, CGN_TZ,
4353
        &[
4354
            (0, 26, (O_0, 2023, 8, 31, 20, 1, 5, 0), "2023 Aug 31 20:01:05 -0000 [ERROR] dev-disk-a error 0x08320105"),
4355
            (0, 26, (O_P1, 2023, 8, 31, 20, 1, 9, 0), "2023 Aug 31 20:01:09 +0100 [WARNING] dev-disk-a disconnected."),
4356
        ],
4357
        line!(),
4358
    ),
4359
    DTPD!(
4360
        concatcp!("^", CGP_YEAR, RP_BLANK12, CGP_MONTHBb, RP_BLANK12q, CGP_DAYde, RP_BLANK12q, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12q, CGP_TZzp, RP_NOALNUM),
4361
        DTFSS_YbdHMSzp, 0, 30, CGN_YEAR, CGN_TZ,
4362
        &[
4363
            (0, 24, (O_0, 2023, 8, 31, 20, 1, 5, 0), "2023 Aug 31 20:01:05 -00 [ERROR] dev-disk-a error 0x08320105"),
4364
            (0, 24, (O_P1, 2023, 8, 31, 20, 1, 9, 0), "2023 Aug 31 20:01:09 +01 [WARNING] dev-disk-a disconnected."),
4365
        ],
4366
        line!(),
4367
    ),
4368
    DTPD!(
4369
        concatcp!("^", CGP_YEAR, RP_BLANK12, CGP_MONTHBb, RP_BLANK12q, CGP_DAYde, RP_BLANK12q, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12q, CGP_TZZ, RP_NOALNUM),
4370
        DTFSS_YbdHMSZ, 0, 30, CGN_YEAR, CGN_TZ,
4371
        &[
4372
            (0, 24, (O_0, 2023, 8, 31, 20, 1, 5, 0), "2023 Aug 31 20:01:05 UTC [ERROR] dev-disk-a error 0x08320105"),
4373
            (0, 25, (O_P1, 2023, 8, 31, 20, 1, 9, 0), "2023 Aug 31 20:01:09 WEST [WARNING] dev-disk-a disconnected."),
4374
        ],
4375
        line!(),
4376
    ),
4377
    DTPD!(
4378
        concatcp!("^", CGP_YEAR, RP_BLANK12, CGP_MONTHBb, RP_BLANK12q, CGP_DAYde, RP_BLANK12q, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
4379
        DTFSS_YbdHMS, 0, 25, CGN_YEAR, CGN_SECOND,
4380
        &[
4381
            (0, 20, (O_L, 2023, 8, 31, 20, 1, 5, 0), "2023 Aug 31 20:01:05 [ERROR] dev-disk-a error 0x08320105"),
4382
            (0, 20, (O_L, 2023, 8, 31, 20, 1, 9, 0), "2023 Aug 31 20:01:09 [WARNING] dev-disk-a disconnected."),
4383
        ],
4384
        line!(),
4385
    ),
4386
    // ---------------------------------------------------------------------------------------------
4387
    //
4388
    // pacman log format, example with offset:
4389
    //
4390
    //               1         2
4391
    //     012345678901234567890
4392
    //     [2019-03-01 16:56] [PACMAN] synchronizing package lists
4393
    //
4394
    DTPD!(
4395
        // add more "guard" chars
4396
        concatcp!(r"^\[", CGP_YEAR, D_D, CGP_MONTHm, D_D, CGP_DAYde, D_DHq, CGP_HOUR, D_Te, CGP_MINUTE, r"\]"),
4397
        DTFSS_YmdHM, 0, 20, CGN_YEAR, CGN_MINUTE,
4398
        &[
4399
            (1, 17, (O_L, 2019, 3, 1, 16, 56, 0, 0), "[2019-03-01 16:56] [PACMAN] synchronizing package lists"),
4400
            (1, 17, (O_L, 2018, 5, 31, 12, 19, 0, 0), "[2018-05-31 12:19] [PACMAN] Running 'pacman -Syu --root /tmp/newmsys/msys64'"),
4401
        ],
4402
        line!(),
4403
    ),
4404
    // ---------------------------------------------------------------------------------------------
4405
    //
4406
    // Red Hat Audit log format, example with offset:
4407
    //
4408
    //               1         2         3         4         5         6
4409
    //     0123456789012345678901234567890123456789012345678901234567890
4410
    //     type=DAEMON_START msg=audit(1681160194.260:3932): op=start ver=3.0.7 format=enriched kernel=5.14.0-162.6.1.el9_1.x86_64 auid=4294967295 pid=718 uid=0 ses=4294967295 subj=system_u:system_r:auditd_t:s0 res=success�AUID="unset" UID="root"
4411
    //
4412
    DTPD!(
4413
        concatcp!(RP_BLANK, r"msg=audit\(", CGP_EPOCH, ".", CGP_FRACTIONAL3, r":[[:digit:]]{1,5}\):", RP_BLANK),
4414
        DTFSS_sf, 0, 100, CGN_EPOCH, CGN_FRACTIONAL,
4415
        &[
4416
            (28, 42, (O_L, 2023, 4, 10, 20, 56, 34, 260000000), r#"type=DAEMON_START msg=audit(1681160194.260:3932): op=start ver=3.0.7 format=enriched kernel=5.14.0-162.6.1.el9_1.x86_64 auid=4294967295 pid=718 uid=0 ses=4294967295 subj=system_u:system_r:auditd_t:s0 res=success�AUID="unset" UID="root""#),
4417
            (31, 45, (O_L, 2023, 5, 8, 6, 9, 26, 814000000), r#"type=CRYPTO_KEY_USER msg=audit(1683526166.814:492): pid=13862 uid=0 auid=0 ses=6 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=destroy kind=server fp=SHA256:34:76:7b:a4:dc:bb:e0:b6:5e:5d:73:e9:a1:db:89:21:c0:0d:ca:54:f4:7d:46:9c:b2:87:c4:ed:0b:d4:3f:59 direction=? spid=13900 suid=0  exe="/usr/sbin/sshd" hostname=? addr=? terminal=? res=success' UID="root" AUID="root" SUID="root""#),
4418
        ],
4419
        line!(),
4420
    ),
4421
    // ---------------------------------------------------------------------------------------------
4422
    //
4423
    // strace formats
4424
    //
4425
    //                1         2
4426
    //      012345678901234567890
4427
    //      $ strace -ttt ls
4428
    //      1716853121.780157 execve("/usr/bin/ls", ["ls"], 0x7ffe4c501508 /* 41 vars */) = 0
4429
    //
4430
    // strace `--timestamp=unix,ms'
4431
    // TODO: move this clump of patterns down in importance as these can
4432
    //       match too many other things
4433
    DTPD!(
4434
        concatcp!("^", CGP_EPOCH, "[.,]", CGP_FRACTIONAL3, RP_BLANK, RP_BLANK_NO),
4435
        DTFSS_sf, 0, 23, CGN_EPOCH, CGN_FRACTIONAL,
4436
        &[
4437
            (0, 14, (O_L, 2024, 5, 27, 23, 38, 41, 780000000), r#"1716853121.780 execve("/usr/bin/ls", ["ls"], 0x7ffe4c501508 /* 41 vars */) = 0"#),
4438
        ],
4439
        line!(),
4440
    ),
4441
    // strace `--timestamp=unix,ns' or `-ttt`
4442
    DTPD!(
4443
        concatcp!("^", CGP_EPOCH, "[.,]", CGP_FRACTIONAL6, RP_BLANK, RP_BLANK_NO),
4444
        DTFSS_sf, 0, 26, CGN_EPOCH, CGN_FRACTIONAL,
4445
        &[
4446
            (0, 17, (O_L, 2024, 5, 27, 23, 38, 41, 780157000), r#"1716853121.780157 execve("/usr/bin/ls", ["ls"], 0x7ffe4c501508 /* 41 vars */) = 0"#),
4447
        ],
4448
        line!(),
4449
    ),
4450
    // strace `--timestamp=unix,ns'
4451
    DTPD!(
4452
        concatcp!("^", CGP_EPOCH, "[.,]", CGP_FRACTIONAL9, RP_BLANK, RP_BLANK_NO),
4453
        DTFSS_sf, 0, 29, CGN_EPOCH, CGN_FRACTIONAL,
4454
        &[
4455
            (0, 20, (O_L, 2024, 5, 27, 23, 38, 41, 780157012), r#"1716853121.780157012 execve("/usr/bin/ls", ["ls"], 0x7ffe4c501508 /* 41 vars */) = 0"#),
4456
        ],
4457
        line!(),
4458
    ),
4459
    // strace `--timestamp=unix' or `--timestamp=unix,s'
4460
    DTPD!(
4461
        concatcp!("^", CGP_EPOCH, RP_BLANK, RP_BLANK_NO),
4462
        DTFSS_s, 0, 19, CGN_EPOCH, CGN_EPOCH,
4463
        &[
4464
            (0, 10, (O_L, 2024, 5, 27, 23, 38, 41, 0), r#"1716853121 execve("/usr/bin/ls", ["ls"], 0x7ffe4c501508 /* 41 vars */) = 0"#),
4465
        ],
4466
        line!(),
4467
    ),
4468
    // ---------------------------------------------------------------------------------------------
4469
    //
4470
    // Windows 10 ReportingEvents.log format, example with offset:
4471
    //
4472
    //               1         2         3         4         5         6         7
4473
    //     01234567890123456789012345678901234567890123456789012345678901234567890
4474
    //     {5F45546A-691D-4519-810C-9B159EA7A24F}  2022-10-12 09:26:44:980-0700    1       181 [AGENT_INSTALLING_STARTED]  101     {ADF3720E-8453-44C7-82EF-F9F5DA2D8551}  1       0 Update;ScanForUpdates    Success Content Install Installation Started: Windows has started installing the following update: 9WZDNCRFJ364-MICROSOFT.SKYPEAPP      te2D3dMIjE2PeNSM.86.5.1.0.0.1.0
4475
    //
4476
    // very similar to next DTPD!, but with different second-to-fractional divider ":"
4477
    //
4478
    // XXX: the `ReportingEvents.log` file is UTF-16 encoded
4479
    //      So it's not currently parseable. See Issue #16
4480
    //
4481
    DTPD!(
4482
        concatcp!(RP_NODIGITb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, ":", CGP_FRACTIONAL3, RP_BLANKq, CGP_TZz, RP_NODIGIT),
4483
        DTFSS_YmdHMSfz, 0, 1024, CGN_YEAR, CGN_TZ,
4484
        &[
4485
                (40, 68, (O_M7, 2022, 10, 12, 9, 26, 44, 980000000), r"{5F45546A-691D-4519-810C-9B159EA7A24F}  2022-10-12 09:26:44:980-0700    1       181 [AGENT_INSTALLING_STARTED]  101      {ADF3720E-8453-44C7-82EF-F9F5DA2D8551}  1       0 Update;ScanForUpdates    Success Content Download        Download succeeded.     te2D3dMIjE2PeNSM.86.3.1.0.0.85.0"),
4486
                (40, 68, (O_M7, 2022, 10, 12, 9, 26, 44, 169000000), r"{F4A3F9DB-F870-4022-A079-D5D2B596519D}  2022-10-12 09:26:44:169-0700    1       162 [AGENT_DOWNLOAD_SUCCEEDED]  101     {ADF3720E-8453-44C7-82EF-F9F5DA2D8551}  1       0       Update;ScanForUpdates   SuccessContent Download Download succeeded.     te2D3dMIjE2PeNSM.86.3.1.0.0.85.0"),
4487
        ],
4488
        line!(),
4489
    ),
4490
    //
4491
    // ---------------------------------------------------------------------------------------------
4492
    //
4493
    // matches of datetime field commonly found in JSONL files (single-line JSON entries)
4494
    //
4495
    // example with offset:
4496
    //
4497
    //               1         2         3         4         5         6         7         8         9         0         1         2         3         4         5
4498
    //     0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
4499
    //     {"level":"INFO","message":"Started","timestamp":"2024-04-08T21:55:48.726Z"}
4500
    //
4501
    // "timestamp" with fractional
4502
    DTPD!(
4503
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, "\""),
4504
        DTFSS_YmdHMSfZ, 0, 2056, CGN_YEAR, CGN_TZ,
4505
        &[
4506
            (49, 73, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32.123Z"}"#),
4507
            (16, 43, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"TIMESTAMP" : "2000/01/02 05-01-32.123 PST", "data" : ""}"#),
4508
        ],
4509
        line!(),
4510
    ),
4511
    DTPD!(
4512
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, "\""),
4513
        DTFSS_YmdHMSfzc, 0, 2056, CGN_YEAR, CGN_TZ,
4514
        &[
4515
            (49, 78, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32.123+00:00"}"#),
4516
            (16, 46, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"TIMESTAMP" : "2000/01/02 05-01-32.123 -08:00", "data" : ""}"#),
4517
            (16, 48, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"TIMESTAMP" : "2000/01/02 05-01-32.123 −08:00", "data" : ""}"#), // U+2212
4518
        ],
4519
        line!(),
4520
    ),
4521
    DTPD!(
4522
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, "\""),
4523
        DTFSS_YmdHMSfz, 0, 2056, CGN_YEAR, CGN_TZ,
4524
        &[
4525
            (49, 77, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32.123+0000"}"#),
4526
            (16, 45, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"TIMESTAMP" : "2000/01/02 05-01-32.123 -0800", "data" : ""}"#),
4527
        ],
4528
        line!(),
4529
    ),
4530
    DTPD!(
4531
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, "\""),
4532
        DTFSS_YmdHMSfzp, 0, 2056, CGN_YEAR, CGN_TZ,
4533
        &[
4534
            (49, 76, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32.123 +00"}"#),
4535
            (16, 42, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"TIMESTAMP" : "2000/01/02 05-01-32.123-08", "data" : ""}"#),
4536
        ],
4537
        line!(),
4538
    ),
4539
    DTPD!(
4540
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, "\""),
4541
        DTFSS_YmdHMSf, 0, 2056, CGN_YEAR, CGN_FRACTIONAL,
4542
        &[
4543
            (49, 72, (O_L, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32.123"}"#),
4544
            (16, 39, (O_L, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"TIMESTAMP" : "2000/01/02 05-01-32.123", "data" : ""}"#),
4545
        ],
4546
        line!(),
4547
    ),
4548
    // "timestamp" without fractional
4549
    DTPD!(
4550
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZZ, "\""),
4551
        DTFSS_YmdHMSZ, 0, 2056, CGN_YEAR, CGN_TZ,
4552
        &[
4553
            (49, 69, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32Z"}"#),
4554
            (16, 39, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"TIMESTAMP" : "2000/01/02 05-01-32 PST", "data" : ""}"#),
4555
        ],
4556
        line!(),
4557
    ),
4558
    DTPD!(
4559
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZzc, "\""),
4560
        DTFSS_YmdHMSzc, 0, 2056, CGN_YEAR, CGN_TZ,
4561
        &[
4562
            (49, 75, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32 +00:00"}"#),
4563
            (16, 41, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"TIMESTAMP" : "2000/01/02 05-01-32-08:00", "data" : ""}"#),
4564
        ],
4565
        line!(),
4566
    ),
4567
    DTPD!(
4568
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZz, "\""),
4569
        DTFSS_YmdHMSz, 0, 2056, CGN_YEAR, CGN_TZ,
4570
        &[
4571
            (49, 73, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32+0000"}"#),
4572
            (16, 41, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"TIMESTAMP" : "2000/01/02 05-01-32 -0800", "data" : ""}"#),
4573
        ],
4574
        line!(),
4575
    ),
4576
    DTPD!(
4577
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZzp, "\""),
4578
        DTFSS_YmdHMSzp, 0, 2056, CGN_YEAR, CGN_TZ,
4579
        &[
4580
            (49, 72, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32 +00"}"#),
4581
            (16, 39, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"TIMESTAMP" : "2000/01/02 05-01-32 -08", "data" : ""}"#),
4582
        ],
4583
        line!(),
4584
    ),
4585
    DTPD!(
4586
        concatcp!(r#""(TIMESTAMP|Timestamp|timestamp)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, "\""),
4587
        DTFSS_YmdHMS, 0, 2056, CGN_YEAR, CGN_SECOND,
4588
        &[
4589
            (49, 68, (O_L, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","timestamp":"2000-01-02T05:01:32"}"#),
4590
            (16, 35, (O_L, 2000, 1, 2, 5, 1, 32, 0), r#"{"TIMESTAMP" : "2000/01/02 05:01:32", "data" : ""}"#),
4591
        ],
4592
        line!(),
4593
    ),
4594
    // "datetime" with fractional
4595
    DTPD!(
4596
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, "\""),
4597
        DTFSS_YmdHMSfZ, 0, 2056, CGN_YEAR, CGN_TZ,
4598
        &[
4599
            (48, 72, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32.123Z"}"#),
4600
            (15, 42, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"DATETIME" : "2000/01/02 05-01-32.123 PST", "data" : ""}"#),
4601
        ],
4602
        line!(),
4603
    ),
4604
    DTPD!(
4605
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, "\""),
4606
        DTFSS_YmdHMSfzc, 0, 2056, CGN_YEAR, CGN_TZ,
4607
        &[
4608
            (48, 77, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32.123+00:00"}"#),
4609
            (15, 45, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"DATETIME" : "2000/01/02 05-01-32.123 -08:00", "data" : ""}"#),
4610
        ],
4611
        line!(),
4612
    ),
4613
    DTPD!(
4614
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, "\""),
4615
        DTFSS_YmdHMSfz, 0, 2056, CGN_YEAR, CGN_TZ,
4616
        &[
4617
            (48, 76, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32.123+0000"}"#),
4618
            (15, 44, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"DATETIME" : "2000/01/02 05-01-32.123 -0800", "data" : ""}"#),
4619
        ],
4620
        line!(),
4621
    ),
4622
    DTPD!(
4623
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, "\""),
4624
        DTFSS_YmdHMSfzp, 0, 2056, CGN_YEAR, CGN_TZ,
4625
        &[
4626
            (48, 75, (O_Z, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32.123 +00"}"#),
4627
            (15, 41, (O_M8, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"DATETIME" : "2000/01/02 05-01-32.123-08", "data" : ""}"#),
4628
        ],
4629
        line!(),
4630
    ),
4631
    DTPD!(
4632
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, D_SF, CGP_FRACTIONAL, "\""),
4633
        DTFSS_YmdHMSf, 0, 2056, CGN_YEAR, CGN_FRACTIONAL,
4634
        &[
4635
            (48, 71, (O_L, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32.123"}"#),
4636
            (15, 38, (O_L, 2000, 1, 2, 5, 1, 32, 123000000), r#"{"DATETIME" : "2000/01/02 05-01-32.123", "data" : ""}"#),
4637
        ],
4638
        line!(),
4639
    ),
4640
    // "datetime" without fractional
4641
    DTPD!(
4642
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZZ, "\""),
4643
        DTFSS_YmdHMSZ, 0, 2056, CGN_YEAR, CGN_TZ,
4644
        &[
4645
            (48, 68, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32Z"}"#),
4646
            (15, 38, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"DATETIME" : "2000/01/02 05-01-32 PST", "data" : ""}"#),
4647
        ],
4648
        line!(),
4649
    ),
4650
    DTPD!(
4651
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZzc, "\""),
4652
        DTFSS_YmdHMSzc, 0, 2056, CGN_YEAR, CGN_TZ,
4653
        &[
4654
            (48, 74, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32 +00:00"}"#),
4655
            (15, 40, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"DATETIME" : "2000/01/02 05-01-32-08:00", "data" : ""}"#),
4656
        ],
4657
        line!(),
4658
    ),
4659
    DTPD!(
4660
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZz, "\""),
4661
        DTFSS_YmdHMSz, 0, 2056, CGN_YEAR, CGN_TZ,
4662
        &[
4663
            (48, 72, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32+0000"}"#),
4664
            (15, 40, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"DATETIME" : "2000/01/02 05-01-32 -0800", "data" : ""}"#),
4665
        ],
4666
        line!(),
4667
    ),
4668
    DTPD!(
4669
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, RP_BLANKq, CGP_TZzp, "\""),
4670
        DTFSS_YmdHMSzp, 0, 2056, CGN_YEAR, CGN_TZ,
4671
        &[
4672
            (48, 71, (O_Z, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32 +00"}"#),
4673
            (15, 38, (O_M8, 2000, 1, 2, 5, 1, 32, 0), r#"{"DATETIME" : "2000/01/02 05-01-32 -08", "data" : ""}"#),
4674
        ],
4675
        line!(),
4676
    ),
4677
    DTPD!(
4678
        concatcp!(r#""(DATETIME|Datetime|datetime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, "\""),
4679
        DTFSS_YmdHMS, 0, 2056, CGN_YEAR, CGN_SECOND,
4680
        &[
4681
            (48, 67, (O_L, 2000, 1, 2, 5, 1, 32, 0), r#"{"level":"INFO","message":"Started","datetime":"2000-01-02T05:01:32"}"#),
4682
            (15, 34, (O_L, 2000, 1, 2, 5, 1, 32, 0), r#"{"DATETIME" : "2000/01/02 05:01:32", "data" : ""}"#),
4683
        ],
4684
        line!(),
4685
    ),
4686
    // ---------------------------------------------------------------------------------------------
4687
    //
4688
    // Chrome cv_debug.log format
4689
    //
4690
    // example with offset:
4691
    //
4692
    //               1         2         3         4
4693
    //     01234567890123456789012345678901234567890
4694
    //     {"logTime": "0226/052726", "correlationVector":"C3BF38D097234ED3A46F33A1C497BF65","action":"FETCH_UX_CONFIG", "result":""}
4695
    //
4696
    DTPD!(
4697
        concatcp!(r#""(LOGTIME|LogTime|logTime|logtime)""#, RP_BLANKq, ":", RP_BLANKq, "\"", CGP_MONTHm, D_Deq, CGP_DAYde, D_DHcdqus, CGP_HOUR, D_Te, CGP_MINUTE, D_Te, CGP_SECOND, "\""),
4698
        DTFSS_mdHMS, 0, 512, CGN_MONTH, CGN_SECOND,
4699
        &[
4700
            (13, 24, (O_L, YD, 2, 26, 5, 27, 26, 0), r#"{"logTime": "0226/052726", "correlationVector":"A","action":"FETCH_UX_CONFIG", "result":""}"#),
4701
            (13, 24, (O_L, YD, 2, 26, 5, 27, 26, 0), r#"{"LOGTIME" :"0226/052726", "correlationVector":"A","action":"FETCH_UX_CONFIG", "result":""}"#),
4702
        ],
4703
        line!(),
4704
    ),
4705
    // ---------------------------------------------------------------------------------------------
4706
    //
4707
    // general matches anywhere in the first 1024 bytes of the line
4708
    //
4709
    // these are most likely to match datetimes in the log *message*, i.e. a substring that happens
4710
    // to be a datetime but is not the formal log timestamp. In other words, most likely to cause
4711
    // errant matches. One reason they are declared last and so attempted last.
4712
    //
4713
    DTPD!(
4714
        concatcp!(RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, RP_RB),
4715
        DTFSS_YmdHMSfz, 0, 1024, CGN_YEAR, CGN_TZ,
4716
        &[
4717
            (1, 30, (O_M11, 2000, 1, 2, 5, 1, 32, 123000000), "<2000/01/02 05:01:32.123 -1100> a"),
4718
            (1, 30, (O_M11, 2000, 1, 2, 5, 1, 32, 123000000), "{2000/01/02 05:01:32.123 -1100} a"),
4719
            (1, 32, (O_M11, 2000, 1, 2, 5, 1, 32, 123000000), "{2000/01/02 05:01:32.123 −1100} a"), // U+2212
4720
        ],
4721
        line!(),
4722
    ),
4723
    DTPD!(
4724
        concatcp!(RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, RP_RB),
4725
        DTFSS_YmdHMSfzc, 0, 1024, CGN_YEAR, CGN_TZ,
4726
        &[
4727
            (11, 43, (O_M1130, 2000, 1, 3, 5, 2, 33, 123456000), "[LOGGER]  {2000/01/03 05:02:33.123456-11:30} ab"),
4728
            (1, 34, (O_M1130, 2000, 1, 3, 5, 2, 33, 123456000), "<2000-01-03T05:02:33.123456 -11:30> ab"),
4729
            (1, 36, (O_M1130, 2000, 1, 3, 5, 2, 33, 123456000), "<2000-01-03T05:02:33.123456 −11:30> ab"), // U+2212
4730
        ],
4731
        line!(),
4732
    ),
4733
    DTPD!(
4734
        concatcp!(RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, RP_RB),
4735
        DTFSS_YmdHMSfzp, 0, 1024, CGN_YEAR, CGN_TZ,
4736
        &[
4737
            (11, 44, (O_M11, 2000, 1, 4, 0, 3, 34, 123456789), "[LOGGER]  [2000/01/04 00:03:34,123456789 -11]"),
4738
            (11, 44, (O_M11, 2000, 1, 4, 0, 3, 34, 123456789), "[LOGGER]  [2000/01/04 00:03:34.123456789 -11] abc"),
4739
            (11, 46, (O_M11, 2000, 1, 4, 0, 3, 34, 123456789), "[LOGGER]  [2000/01/04 00:03:34.123456789 −11] abc"), // U+2212
4740
            (11, 43, (O_M11, 2000, 1, 4, 0, 3, 34, 123456789), "[LOGGER]  [2000/01/04T00:03:34,123456789-11]abc"),
4741
        ],
4742
        line!(),
4743
    ),
4744
    DTPD!(
4745
        concatcp!(RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, RP_RB),
4746
        DTFSS_YmdHMSfZ, 0, 1024, CGN_YEAR, CGN_TZ,
4747
        &[
4748
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]\t\t<2000/01/05 00:04:35.123456789 VLAT>:"),
4749
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  <2000/01/05 00:04:35.123456789 VLAT>"),
4750
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  <2000/01/05 00:04:35.123456789 VLAT> abcd"),
4751
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  <2000/01/05 00:04:35.123456789 VLAT>abcd"),
4752
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  <2000/01/05 00:04:35.123456789 VLAT>abcd"),
4753
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  [2000/01/05 00:04:35.123456789 VLAT] abcd"),
4754
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  [2000/01/05-00:04:35.123456789 VLAT]"),
4755
            (11, 44, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  [2000/01/05-00:04:35.123456789VLAT]"),
4756
            (11, 45, (O_VLAT, 2000, 1, 5, 0, 4, 35, 123456789), "[LOGGER]  (2000/01/05T00:04:35.123456789 vlat) abcd"),
4757
        ],
4758
        line!(),
4759
    ),
4760
    DTPD!(
4761
        concatcp!(RP_LB, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_RB),
4762
        DTFSS_YmdHMSf, 0, 1024, CGN_YEAR, CGN_FRACTIONAL,
4763
        &[
4764
            (11, 40, (O_L, 2020, 1, 6, 0, 5, 26, 123456789), "[LOGGER]  (2020-01-06 00:05:26.123456789) abcdefg"),
4765
            (21, 50, (O_L, 2020, 1, 6, 0, 5, 26, 123456789), "[FOOBAR] (PID 2005) (2020-01-06 00:05:26.123456789) foobar!"),
4766
        ],
4767
        line!(),
4768
    ),
4769
    DTPD!(
4770
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZz, RP_NODIGIT),
4771
        DTFSS_YmdHMSfz, 0, 1024, CGN_YEAR, CGN_TZ,
4772
        &[
4773
            (0, 29, (O_M11, 2000, 1, 2, 7, 8, 32, 123000000), "2000/01/02 07:08:32.123 -1100 a"),
4774
            (0, 29, (O_M11, 2000, 1, 2, 7, 8, 32, 123000000), "2000-01-02T07:08:32.123 -1100 a"),
4775
            (0, 25, (O_M11, 2000, 1, 2, 7, 8, 32, 123000000), "20000102:070832.123 -1100 a"),
4776
        ],
4777
        line!(),
4778
    ),
4779
    DTPD!(
4780
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzc, RP_NODIGIT),
4781
        DTFSS_YmdHMSfzc, 0, 1024, CGN_YEAR, CGN_TZ,
4782
        &[
4783
            (0, 33, (O_M1130, 2000, 1, 3, 0, 2, 3, 123456000), "2000/01/03 00:02:03.123456 -11:30 ab"),
4784
            (1, 34, (O_M1130, 2000, 1, 3, 0, 2, 3, 123456000), "|2000/01/03:00:02:03.123456 -11:30|ab"),
4785
            (1, 34, (O_M1130, 2000, 1, 3, 0, 2, 3, 123456000), "<2000/01/03T00:02:03.123456 -11:30 abc"),
4786
        ],
4787
        line!(),
4788
    ),
4789
    DTPD!(
4790
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZzp, RP_NODIGIT),
4791
        DTFSS_YmdHMSfzp, 0, 1024, CGN_YEAR, CGN_TZ,
4792
        &[
4793
            (0, 33, (O_M11, 2000, 1, 4, 0, 23, 24, 123456789), "2000/01/04 00:23:24,123456789 -11"),
4794
            (0, 33, (O_M11, 2000, 1, 4, 0, 23, 24, 123456789), "2000/01/04 00:23:24,123456789 -11 abc"),
4795
            (0, 33, (O_M11, 2000, 1, 4, 0, 23, 24, 123456789), "2000/01/04 00:23:24,123456789 -11_abc"),
4796
            (1, 34, (O_M11, 2000, 1, 4, 0, 23, 24, 123456789), "|2000/01/04-00:23:24,123456789 -11|abc"),
4797
            (1, 34, (O_M11, 2000, 1, 4, 0, 23, 24, 123456789), "[2000/01/04T00:23:24,123456789 -11] abc"),
4798
        ],
4799
        line!(),
4800
    ),
4801
    DTPD!(
4802
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_BLANKq, CGP_TZZ, RP_NOALPHA),
4803
        DTFSS_YmdHMSfZ, 0, 1024, CGN_YEAR, CGN_TZ,
4804
        &[
4805
            (0, 34, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "2000/01/05 00:34:35.123456789 VLAT:"),
4806
            (0, 34, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "2000/01/05 00:34:35.123456789 VLAT"),
4807
            (0, 34, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "2000/01/05 00:34:35.123456789 VLAT abcd"),
4808
            (0, 34, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "2000/01/05:00:34:35.123456789 VLAT:abcd"),
4809
            (1, 35, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "|2000/01/05 00:34:35.123456789 VLAT|abcd"),
4810
            (1, 35, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), ":2000/01/05 00:34:35.123456789 VLAT: abcd"),
4811
            (1, 35, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "[2000/01/05T00:34:35.123456789 VLAT]"),
4812
            (1, 34, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "[2000/01/05T00:34:35.123456789VLAT]"),
4813
            (0, 34, (O_VLAT, 2000, 1, 5, 0, 34, 35, 123456789), "2000/01/05-00:34:35.123456789 vlat abcd"),
4814
        ],
4815
        line!(),
4816
    ),
4817
    DTPD!(
4818
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Deq, CGP_MONTHm, D_Deq, CGP_DAYde, D_DHcdq, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, D_SF, CGP_FRACTIONAL, RP_NODIGIT),
4819
        DTFSS_YmdHMSf, 0, 1024, CGN_YEAR, CGN_FRACTIONAL,
4820
        &[
4821
            (0, 29, (O_L, 2020, 1, 6, 0, 5, 26, 123456789), "2020-01-06 00:05:26.123456789 abcdefg"),
4822
            (0, 29, (O_L, 2020, 1, 6, 0, 5, 26, 123456789), r"2020\01\06 00:05:26.123456789 abcdefg"),
4823
            (20, 49, (O_L, 2020, 1, 6, 0, 5, 26, 123456789), "[FOOBAR] (PID 2005) 2020-01-06 00:05:26.123456789 foobar!"),
4824
        ],
4825
        line!(),
4826
    ),
4827
    //
4828
    // Synology OS `fsck/root.log`
4829
    //
4830
    //               1         2         3
4831
    //     0123456789012345678901234567890
4832
    //     20200307_202530 /sbin/e2fsck -pvf
4833
    //
4834
    DTPD!(
4835
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZz, RP_NODIGIT),
4836
        DTFSS_YmdHMSz, 0, 1024, CGN_YEAR, CGN_TZ,
4837
        &[
4838
            (0, 25, (O_M11, 2000, 1, 7, 0, 6, 2, 0), "2000/01/07T00:06:02 -1100 abcdefgh"),
4839
            (1, 26, (O_M11, 2000, 1, 7, 0, 6, 2, 0), "[2000/01/07T00:06:02 -1100]        abcdefgh"),
4840
            (0, 21, (O_M11, 2020, 3, 7, 20, 25, 30, 0), "20200307_202530 -1100 /sbin/e2fsck -pvf"),
4841
        ],
4842
        line!(),
4843
    ),
4844
    DTPD!(
4845
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzc, RP_NODIGIT),
4846
        DTFSS_YmdHMSzc, 0, 1024, CGN_YEAR, CGN_TZ,
4847
        &[
4848
            (0, 26, (O_M1130, 2000, 1, 8, 0, 7, 3, 0), "2000-01-08-00:07:03 -11:30 aabcdefghi"),
4849
            (0, 26, (O_M1130, 2000, 1, 8, 0, 7, 3, 0), "2000-01-08-00:07:03 -11:30        aabcdefghi"),
4850
            (1, 27, (O_M1130, 2000, 1, 8, 0, 7, 3, 0), "[2000-01-08-00:07:03 -11:30] aabcdefghi"),
4851
            (0, 22, (O_M11, 2020, 3, 7, 20, 25, 30, 0), "20200307_202530 -11:00 /sbin/e2fsck -pvf"),
4852
        ],
4853
        line!(),
4854
    ),
4855
    DTPD!(
4856
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZzp, RP_NODIGIT),
4857
        DTFSS_YmdHMSzp, 0, 1024, CGN_YEAR, CGN_TZ,
4858
        &[
4859
            (0, 23, (O_M11, 2000, 1, 9, 0, 8, 4, 0), "2000/01/09 00:08:04 -11 abcdefghij"),
4860
            (1, 24, (O_M11, 2000, 1, 9, 0, 8, 4, 0), "[2000/01/09 00:08:04 -11] abcdefghij"),
4861
            (0, 19, (O_M11, 2020, 3, 7, 20, 25, 30, 0), "20200307_202530 -11 /sbin/e2fsck -pvf"),
4862
        ],
4863
        line!(),
4864
    ),
4865
    DTPD!(
4866
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Dq, CGP_MONTHm, D_Dq, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANKq, CGP_TZZ, RP_NOALPHA),
4867
        DTFSS_YmdHMSZ, 0, 1024, CGN_YEAR, CGN_TZ,
4868
        &[
4869
            (0, 24, (O_VLAT, 2000, 1, 10, 0, 9, 5, 0), "2000/01/10T00:09:05 VLAT abcdefghijk"),
4870
            (0, 24, (O_VLAT, 2000, 1, 10, 0, 9, 5, 0), "2000/01/10T00:09:05 VLAT_abcdefghijk"),
4871
            (1, 25, (O_VLAT, 2000, 1, 10, 0, 9, 5, 0), "[2000/01/10T00:09:05 VLAT] abcdefghijk"),
4872
            (1, 25, (O_VLAT, 2000, 1, 10, 0, 9, 5, 0), "[2000/01/10T00:09:05 VLAT] abcdefghijk"),
4873
            (1, 25, (O_VLAT, 2000, 1, 10, 0, 9, 5, 0), "<2000/01/10T00:09:05 VLAT> abcdefghijk"),
4874
            (1, 24, (O_VLAT, 2000, 1, 10, 0, 9, 5, 0), "<2000/01/10T00:09:05VLAT> abcdefghijk"),
4875
            (0, 20, (O_VLAT, 2020, 3, 7, 20, 25, 30, 0), "20200307_202530 VLAT /sbin/e2fsck -pvf"),
4876
        ],
4877
        line!(),
4878
    ),
4879
    //
4880
    /*
4881
    DTPD!(
4882
        concatcp!(CGP_MONTH, D_D, CGP_MONTHm, D_D, CGP_DAYde, " @", BLANKq, CGP_HOURh, D_T, CGP_MINUTE, RP_BLANKq, ),
4883
        DTFSS_YmdHMSZ, 0, 1024, CGN_YEAR, CGN_TZ,
4884
        &[
4885
            "09/12/2022 @ 7:05am"
4886
        ],
4887
        line!(),
4888
    ),
4889
    */
4890
    //
4891
    DTPD!(
4892
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Deq, CGP_MONTHm, D_Deq, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
4893
        DTFSS_YmdHMS, 0, 512, CGN_YEAR, CGN_SECOND,
4894
        &[
4895
            (0, 19, (O_L, 2020, 1, 11, 0, 10, 26, 0), "2020-01-11 00:10:26 abcdefghijkl"),
4896
            (0, 19, (O_L, 2020, 1, 11, 0, 10, 26, 0), r"2020\01\11 00:10:26 abcdefghijkl"),
4897
            (0, 15, (O_L, 2020, 3, 7, 20, 25, 30, 0), "20200307_202530:/sbin/e2fsck -pvf"),
4898
            // from `C:/Windows/Performance/WinSAT/winsat.log`
4899
            // a datetime format with redundant `AM` and `PM`, see Issue #64
4900
            (50, 69, (O_L, 2023, 2, 22, 16, 4, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- START 2023\02\22 16:04:07 PM ---"),
4901
        ],
4902
        line!(),
4903
    ),
4904
    // variation of prior using single-digit months and hours; Issue #64
4905
    DTPD!(
4906
        concatcp!(RP_NOALNUMb, CGP_YEAR, D_Deq, CGP_MONTHms, D_Deq, CGP_DAYde, D_DHcdqu, CGP_HOURs, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
4907
        DTFSS_YmsdkMS, 0, 512, CGN_YEAR, CGN_SECOND,
4908
        &[
4909
            (0, 17, (O_L, 2020, 1, 11, 0, 10, 26, 0), "2020-1-11 0:10:26 abcdefghijkl 0"),
4910
            (0, 18, (O_L, 2020, 12, 11, 0, 10, 26, 0), "2020-12-11 0:10:26 abcdefghijkl 1"),
4911
            (0, 17, (O_L, 2020, 1, 11, 0, 10, 26, 0), r"2020\1\11 0:10:26 abcdefghijkl 2"),
4912
            (0, 18, (O_L, 2020, 1, 11, 14, 10, 26, 0), r"2020\1\11 14:10:26 abcdefghijkl 3"),
4913
            (0, 13, (O_L, 2020, 3, 7, 4, 25, 30, 0), "2020307_42530:/sbin/e2fsck -pvf"),
4914
            (1, 14, (O_L, 2020, 3, 7, 4, 25, 30, 0), r"[2020307_42530] /sbin/e2fsck -pvf"),
4915
            // from `C:/Windows/Performance/WinSAT/winsat.log`
4916
            // a datetime format with redundant `AM` and `PM`, see Issue #64
4917
            // with single-digit month and hour
4918
            (50, 67, (O_L, 2023, 2, 22, 4, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- START 2023\2\22 4:05:07 AM ---"),
4919
            (50, 67, (O_L, 2023, 2, 22, 1, 5, 7, 0), r"59805625 (9340) - exe\logging.cpp:0841: --- START 2023\2\22 1:05:07 AM ---"),
4920
        ],
4921
        line!(),
4922
    ),
4923
    //
4924
    // another general match variation
4925
    //
4926
    DTPD!(
4927
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZz, RP_NODIGIT),
4928
        DTFSS_BdHMSYz, 0, 1024, CGN_DAYa, CGN_TZ,
4929
        &[
4930
            (8, 42, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tuesday Jun 28 2022 01:51:12 +1230"),
4931
            (8, 38, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12 +1230 FOOBAR"),
4932
            (8, 39, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue, Jun 28 2022 01:51:12 +1230"),
4933
            (8, 39, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue, Jun  2 2022 01:51:12 +1230"),
4934
            (8, 39, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue, Jun 02 2022 01:51:12 +1230"),
4935
            (8, 38, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue, Jun 2 2022 01:51:12 +1230"),
4936
            (8, 38, (O_M11, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue, Jun 2 2022 01:51:12 -1100"),
4937
            (8, 40, (O_M11, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue, Jun 2 2022 01:51:12 −1100"), // U+2212
4938
            (8, 39, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue. Jun 28 2022 01:51:12 +1230 FOOBAR"),
4939
        ],
4940
        line!(),
4941
    ),
4942
    DTPD!(
4943
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZzc, RP_NODIGIT),
4944
        DTFSS_BdHMSYzc, 0, 1024, CGN_DAYa, CGN_TZ,
4945
        &[
4946
            (3, 35, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "<7>Tue, Jun 28 2022 01:51:12 +01:30 FOOBAR"),
4947
            (4, 36, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "<33>Tue, Jun 28 2022 01:51:12 +01:30 FOOBAR"),
4948
            (28, 60, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[SOME OTHER FIELD] BLARG<33>Tue, Jun 28 2022 01:51:12 +01:30 FOOBAR"),
4949
            (1, 33, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "*Tue, Jun 28 2022 01:51:12 +01:30 FOOBAR"),
4950
            (3, 35, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "***Tue, Jun 28 2022 01:51:12 +01:30 FOOBAR"),
4951
            (11, 43, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[RSYSLOG]: Tue, Jun 28 2022 01:51:12 +01:30"),
4952
            (8, 40, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[INFO]: Tue. Jun 28 2022 01:51:12 +01:30:FOOBAR"),
4953
            (7, 38, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[INFO]:Tue Jun 28 2022 01:51:12 +01:30<33>FOOBAR"),
4954
            (6, 37, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[INFO]Tue Jun 28 2022 01:51:12 +01:30FOOBAR"),
4955
            (7, 38, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "{INFO} Tue Jun 28 2022 01:51:12 +01:30 FOOBAR"),
4956
            (7, 38, (O_M1_30, 2022, 6, 28, 1, 51, 12, 0), "{INFO} Tue Jun 28 2022 01:51:12 -01:30 FOOBAR"),
4957
            (7, 40, (O_M1_30, 2022, 6, 28, 1, 51, 12, 0), "{INFO} Tue Jun 28 2022 01:51:12 −01:30 FOOBAR"), // U+2212
4958
        ],
4959
        line!(),
4960
    ),
4961
    DTPD!(
4962
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZzp, RP_NODIGIT),
4963
        DTFSS_BdHMSYzp, 0, 1024, CGN_DAYa, CGN_TZ,
4964
        &[
4965
            (8, 41, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[DEBUG] Tuesday, Jun 28 2022 01:51:12 +01"),
4966
            (9, 38, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE1] Tue. Jun 28 2022 01:51:12 +01 FOOBAR"),
4967
            (9, 38, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "[TRACE1] Tue. Jun  2 2022 01:51:12 +01 FOOBAR"),
4968
            (9, 38, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "[TRACE1] Tue. Jun 02 2022 01:51:12 +01 FOOBAR"),
4969
            (9, 37, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "[TRACE1] Tue. Jun 2 2022 01:51:12 +01 FOOBAR"),
4970
            (9, 38, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE2] Tue, Jun 28 2022 01:51:12 +01"),
4971
            (9, 37, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE1] Tue Jun 28 2022 01:51:12 +01 FOOBAR"),
4972
            (9, 37, (O_M1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE1] Tue Jun 28 2022 01:51:12 -01 FOOBAR"),
4973
            (9, 39, (O_M1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE1] Tue Jun 28 2022 01:51:12 −01 FOOBAR"), // U+2212
4974
        ],
4975
        line!(),
4976
    ),
4977
    DTPD!(
4978
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_TZZ, RP_NOALPHA),
4979
        DTFSS_BdHMSYZ, 0, 1024, CGN_DAYa, CGN_TZ,
4980
        &[
4981
            (6, 39, (O_WIT, 2022, 6, 28, 1, 51, 12, 0), "LOGGR Tuesday, Jun 28 2022 01:51:12 WIT"),
4982
            (6, 36, (O_WITA, 2022, 6, 28, 1, 51, 12, 0), "LOGGR Tue, Jun 28 2022 01:51:12 WITA:FOOBAR"),
4983
            (6, 35, (O_WST, 2022, 6, 28, 1, 51, 12, 0), "LOGGR Tue. Jun 28 2022 01:51:12 WST FOOBAR"),
4984
            (8, 37, (O_YAKT, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12 YAKT"),
4985
            (8, 37, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue Jun  2 2022 01:51:12 YAKT"),
4986
            (8, 37, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue Jun 02 2022 01:51:12 YAKT"),
4987
            (8, 36, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue Jun 2 2022 01:51:12 YAKT"),
4988
            (8, 37, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12 YEKT FOOBAR"),
4989
            (8, 37, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12 yekt foobar"),
4990
            (8, 38, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue June 28 2022 01:51:12 yekt foobar"),
4991
            (8, 38, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue JUNE 28 2022 01:51:12 yekt foobar"),
4992
            (8, 39, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue june 28 2022 01:51:12\t\tyekt\tfoobar"),
4993
        ],
4994
        line!(),
4995
    ),
4996
    DTPD!(
4997
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_YEAR, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
4998
        DTFSS_BdHMSY, 0, 1024, CGN_DAYa, CGN_SECOND,
4999
        &[
5000
            (6, 35, (O_L, 2022, 6, 28, 1, 51, 12, 0), "LOGGR Tuesday, Jun 28 2022 01:51:12 "),
5001
            (6, 35, (O_L, 2022, 6, 28, 1, 51, 12, 0), "LOGGR Tuesday, Jun 28 2022 01:51:12"),
5002
            (6, 31, (O_L, 2022, 6, 28, 1, 51, 12, 0), "LOGGR Tue, Jun 28 2022 01:51:12 FOOBAR"),
5003
            (7, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "blarg: Tue. Jun 28 2022 01:51:12 WST"),
5004
            (8, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12[abc"),
5005
            (8, 32, (O_L, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue Jun  2 2022 01:51:12[abc"),
5006
            (8, 32, (O_L, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue Jun 02 2022 01:51:12;abc"),
5007
            (8, 31, (O_L, 2022, 6, 2, 1, 51, 12, 0), "RSYSLOG Tue Jun 2 2022 01:51:12 YAKT"),
5008
            (8, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12 YEKT FOOBAR"),
5009
            (8, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue Jun 28 2022 01:51:12 foobar"),
5010
            (8, 33, (O_L, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue June 28 2022 01:51:12               foobar"),
5011
            (8, 33, (O_L, 2022, 6, 28, 1, 51, 12, 0), "RSYSLOG Tue JUNE 28 2022 01:51:12[YEKT]"),
5012
            (6, 31, (O_L, 2022, 6, 28, 1, 51, 12, 0), "LOGGR|Tue june 28 2022 01:51:12|YEKT"),
5013
        ],
5014
        line!(),
5015
    ),
5016
    //
5017
    DTPD!(
5018
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_YEAR, RP_BLANK12q, CGP_TZz, RP_NODIGIT),
5019
        DTFSS_BdHMSYz, 0, 1024, CGN_DAYa, CGN_TZ,
5020
        &[
5021
            (27, 57, (O_P1230, 2023, 1, 12, 22, 26, 47, 0), "ERROR: apport (pid 486722) Thu Jan 12 22:26:47 2023 +1230: called for pid 486450, signal 6, core limit 0, dump mode 1"),
5022
            (8, 42, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tuesday Jun 28 01:51:12 2022 +1230"),
5023
            (8, 38, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022 +1230 FOOBAR"),
5024
            (8, 39, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue, Jun 28 01:51:12 2022 +1230"),
5025
            (8, 39, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue, Jun  2 01:51:12 2022 +1230"),
5026
            (8, 39, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue, Jun 02 01:51:12 2022 +1230"),
5027
            (8, 38, (O_P1230, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue, Jun 2 01:51:12 2022 +1230"),
5028
            (8, 39, (O_P1230, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue. Jun 28 01:51:12 2022 +1230 FOOBAR"),
5029
        ],
5030
        line!(),
5031
    ),
5032
    DTPD!(
5033
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_YEAR, RP_BLANK12q, CGP_TZzc, RP_NODIGIT),
5034
        DTFSS_BdHMSYzc, 0, 1024, CGN_DAYa, CGN_TZ,
5035
        &[
5036
            (27, 58, (O_P1230, 2023, 1, 12, 22, 26, 47, 0), "ERROR: apport (pid 486722) Thu Jan 12 22:26:47 2023 +12:30: called for pid 486450, signal 6, core limit 0, dump mode 1"),
5037
            (3, 35, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "<7>Tue, Jun 28 01:51:12 2022 +01:30 FOOBAR"),
5038
            (4, 36, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "<33>Tue, Jun 28 01:51:12 2022 +01:30 FOOBAR"),
5039
            (28, 60, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[SOME OTHER FIELD] BLARG<33>Tue, Jun 28 01:51:12 2022 +01:30 FOOBAR"),
5040
            (1, 33, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "*Tue, Jun 28 01:51:12 2022 +01:30 FOOBAR"),
5041
            (3, 35, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "***Tue, Jun 28 01:51:12 2022 +01:30 FOOBAR"),
5042
            (11, 43, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[MESSAGE]: Tue, Jun 28 01:51:12 2022 +01:30"),
5043
            (8, 40, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[INFO]: Tue. Jun 28 01:51:12 2022 +01:30:FOOBAR"),
5044
            (7, 38, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[INFO]:Tue Jun 28 01:51:12 2022 +01:30<33>FOOBAR"),
5045
            (6, 37, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "[INFO]Tue Jun 28 01:51:12 2022 +01:30FOOBAR"),
5046
            (7, 38, (O_P1_30, 2022, 6, 28, 1, 51, 12, 0), "{INFO} Tue Jun 28 01:51:12 2022 +01:30 FOOBAR"),
5047
        ],
5048
        line!(),
5049
    ),
5050
    DTPD!(
5051
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_YEAR, RP_BLANK12q, CGP_TZzp, RP_NODIGIT),
5052
        DTFSS_BdHMSYzp, 0, 1024, CGN_DAYa, CGN_TZ,
5053
        &[
5054
            (27, 55, (O_P12, 2023, 1, 12, 22, 26, 47, 0), "ERROR: apport (pid 486722) Thu Jan 12 22:26:47 2023 +12: called for pid 486450, signal 6, core limit 0, dump mode 1"),
5055
            (8, 41, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[DEBUG] Tuesday, Jun 28 01:51:12 2022 +01"),
5056
            (9, 38, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE1] Tue. Jun 28 01:51:12 2022 +01 FOOBAR"),
5057
            (9, 38, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "[TRACE1] Tue. Jun  2 01:51:12 2022 +01 FOOBAR"),
5058
            (9, 38, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "[TRACE1] Tue. Jun 02 01:51:12 2022 +01 FOOBAR"),
5059
            (9, 37, (O_P1, 2022, 6, 2, 1, 51, 12, 0), "[TRACE1] Tue. Jun 2 01:51:12 2022 +01 FOOBAR"),
5060
            (9, 38, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE2] Tue, Jun 28 01:51:12 2022 +01"),
5061
            (9, 37, (O_P1, 2022, 6, 28, 1, 51, 12, 0), "[TRACE1] Tue Jun 28 01:51:12 2022 +01 FOOBAR"),
5062
        ],
5063
        line!(),
5064
    ),
5065
    DTPD!(
5066
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_YEAR, RP_BLANK12q, CGP_TZZ, RP_NOALPHA),
5067
        DTFSS_BdHMSYZ, 0, 1024, CGN_DAYa, CGN_TZ,
5068
        &[
5069
            (27, 56, (O_WITA, 2023, 1, 12, 22, 26, 47, 0), "ERROR: apport (pid 486722) Thu Jan 12 22:26:47 2023 WITA: called for pid 486450, signal 6, core limit 0, dump mode 1"),
5070
            (6, 39, (O_WIT, 2022, 6, 28, 1, 51, 12, 0), "MESSG Tuesday, Jun 28 01:51:12 2022 WIT"),
5071
            (6, 36, (O_WITA, 2022, 6, 28, 1, 51, 12, 0), "MESSG Tue, Jun 28 01:51:12 2022 WITA:FOOBAR"),
5072
            (6, 35, (O_WST, 2022, 6, 28, 1, 51, 12, 0), "MESSG Tue. Jun 28 01:51:12 2022 WST FOOBAR"),
5073
            (8, 37, (O_YAKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022 YAKT"),
5074
            (8, 37, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue Jun  2 01:51:12 2022 YAKT"),
5075
            (8, 37, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue Jun 02 01:51:12 2022 YAKT"),
5076
            (8, 36, (O_YAKT, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue Jun 2 01:51:12 2022 YAKT"),
5077
            (8, 37, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022 YEKT FOOBAR"),
5078
            (8, 37, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022 yekt foobar"),
5079
            (8, 38, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue June 28 01:51:12 2022 yekt foobar"),
5080
            (8, 38, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue JUNE 28 01:51:12 2022 yekt foobar"),
5081
            (8, 38, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue june 28 01:51:12 2022 yekt foobar"),
5082
            (8, 39, (O_YEKT, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue june 28 01:51:12 2022  yekt\tfoobar"),
5083
        ],
5084
        line!(),
5085
    ),
5086
    DTPD!(
5087
        concatcp!(RP_NOALPHAb, CGP_DAYa, RP_dcq, RP_BLANK12, CGP_MONTHBb, RP_BLANK, CGP_DAYde, RP_cq, RP_BLANK12, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK12, CGP_YEAR, RP_NOALNUM),
5088
        DTFSS_BdHMSY, 0, 1024, CGN_DAYa, CGN_YEAR,
5089
        &[
5090
            (27, 51, (O_L, 2023, 1, 12, 22, 26, 47, 0), "ERROR: apport (pid 486722) Thu Jan 12 22:26:47 2023: called for pid 486450, signal 6, core limit 0, dump mode 1"),
5091
            (6, 35, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSG Tuesday, Jun 28 01:51:12 2022 "),
5092
            (6, 35, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSG Tuesday, Jun 28 01:51:12 2022"),
5093
            (6, 31, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSG Tue, Jun 28 01:51:12 2022 FOOBAR"),
5094
            (7, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "messg: Tue. Jun 28 01:51:12 2022 WST"),
5095
            (8, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022[abc"),
5096
            (8, 32, (O_L, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue Jun  2 01:51:12 2022[abc"),
5097
            (8, 32, (O_L, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue Jun 02 01:51:12 2022;abc"),
5098
            (8, 31, (O_L, 2022, 6, 2, 1, 51, 12, 0), "MESSAGE Tue Jun 2 01:51:12 2022 YAKT"),
5099
            (8, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022 YEKT FOOBAR"),
5100
            (8, 32, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue Jun 28 01:51:12 2022 foobar"),
5101
            (8, 33, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE Tue June 28 01:51:12 2022               foobar"),
5102
            (8, 33, (O_L, 2022, 6, 28, 1, 51, 12, 0), "FOOBAR! Tue JUNE 28 01:51:12 2022[YEKT]"),
5103
            (8, 33, (O_L, 2022, 6, 28, 1, 51, 12, 0), "MESSAGE|Tue JUNE 28 01:51:12 2022|YEKT|foobar!"),
5104
        ],
5105
        line!(),
5106
    ),
5107
    // ---------------------------------------------------------------------------------------------
5108
    //
5109
    // file `FedoraRemix29/hawkeye.log` (with many variations)
5110
    //
5111
    //                1         2         3         4
5112
    //      01234567890123456789012345678901234567890
5113
    //      INFO Jun-16 14:09:58 === Started libdnf-0.31.0 ===
5114
    //      DEBUG Jun-16 14:09:58 fetching rpmdb
5115
    //
5116
    DTPD!(
5117
        concatcp!("^", RP_LEVELS, RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZZ_U, RP_NOALNUM),
5118
        DTFSS_BdHMSYZ, 0, 64, CGN_MONTH, CGN_TZ,
5119
        &[
5120
            (5, 29, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 2000 PDT === Started libdnf-0.31.0 ==="),
5121
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 PDT fetching rpmdb"),
5122
        ],
5123
        line!(),
5124
    ),
5125
    DTPD!(
5126
        concatcp!("^", RP_LEVELS, RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzc, RP_NOALNUM),
5127
        DTFSS_BdHMSYzc, 0, 64, CGN_MONTH, CGN_TZ,
5128
        &[
5129
            (5, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 2000 -07:00 === Started libdnf-0.31.0 ==="),
5130
            (6, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 -07:00 fetching rpmdb"),
5131
        ],
5132
        line!(),
5133
    ),
5134
    DTPD!(
5135
        concatcp!("^", RP_LEVELS, RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZz, RP_NOALNUM),
5136
        DTFSS_BdHMSYz, 0, 64, CGN_MONTH, CGN_TZ,
5137
        &[
5138
            (5, 31, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 2000 -0700 === Started libdnf-0.31.0 ==="),
5139
            (6, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 -0700 fetching rpmdb"),
5140
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 −0700 fetching rpmdb"), // U+2212
5141
        ],
5142
        line!(),
5143
    ),
5144
    DTPD!(
5145
        concatcp!("^", RP_LEVELS, RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzp, RP_NOALNUM),
5146
        DTFSS_BdHMSYzp, 0, 64, CGN_MONTH, CGN_TZ,
5147
        &[
5148
            (5, 29, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 2000 -07 === Started libdnf-0.31.0 ==="),
5149
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 -07 fetching rpmdb"),
5150
            (6, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 −07 fetching rpmdb"), // U+2212
5151
        ],
5152
        line!(),
5153
    ),
5154
    DTPD!(
5155
        concatcp!("^", RP_LEVELS, RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5156
        DTFSS_BdHMSY, 0, 64, CGN_MONTH, CGN_YEAR,
5157
        &[
5158
            (5, 25, (O_L, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 2000 === Started libdnf-0.31.0 ==="),
5159
            (6, 26, (O_L, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 fetching rpmdb"),
5160
        ],
5161
        line!(),
5162
    ),
5163
    DTPD!(
5164
        concatcp!("^", RP_LEVELS, RP_BLANKSq, "[:]?", RP_BLANKSq, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
5165
        DTFSS_BdHMS, 0, 64, CGN_MONTH, CGN_SECOND,
5166
        &[
5167
            (5, 20, (O_L, YD, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 === Started libdnf-0.31.0 ==="),
5168
            (6, 21, (O_L, YD, 6, 16, 14, 9, 58, 0), "DEBUG Jun-16 14:09:58 fetching rpmdb"),
5169
        ],
5170
        line!(),
5171
    ),
5172
    // ---------------------------------------------------------------------------------------------
5173
    //
5174
    // dmesg "uptime" format, example with offset:
5175
    //
5176
    //               1         2         3         4
5177
    //     01234567890123456789012345678901234567890
5178
    //     [    0.000000] kernel: Linux version 5.15.0-43-generic (build@lcy02-amd64-076) (gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #46-Ubuntu SMP Tue Jul 12 10:30:17 UTC 2022 (Ubuntu 5.15.0-43.46-generic 5.15.39)
5179
    //     [    0.000001] kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-5.15.0-43-generic root=UUID=136735fa-5cc1-470f-9359-ee736e42f844 ro console=tty1 console=ttyS0 net.ifnames=0 biosdevname=0
5180
    //     [    0.000002] kernel: KERNEL supported cpus:
5181
    //     [    0.000002] kernel:   Intel GenuineIntel
5182
    //
5183
    DTPD!(
5184
        concatcp!(r"^\[", RP_BLANKSq, CGP_UPTIME_F, r"\]", RP_BLANK),
5185
        DTFSS_u, 0, 20, CGN_UPTIME, CGN_FRACTIONAL,
5186
        &[
5187
            (5, 13, (O_L, 1970, 1, 1, 0, 0, 1, 3000000), "[    1.003000] kernel: Linux version 5.15.0-48-generic (buildd@lcy02-amd64-080) (gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #54-Ubuntu SMP Fri Aug 26 13:26:29 UTC 2022 (Ubuntu 5.15.0-48.54-generic 5.15.53)"),
5188
            (4, 13, (O_L, 1970, 1, 1, 0, 0, 15, 364159000), "[   15.364159] kernel: ISO 9660 Extensions: RRIP_1991A"),
5189
            (1, 15, (O_L, 1970, 1, 21, 0, 40, 35, 564122000), "[1730435.564122] wireguard: wg1: Handshake for peer 481 ((einval)) did not complete after 20 attempts, giving up"),
5190
        ],
5191
        line!(),
5192
    ),
5193
    //
5194
    // lightdm.log "uptime" format, example with offset:
5195
    //
5196
    //               1         2
5197
    //     012345678901234567890
5198
    //     [+0.00s] DEBUG: Logging to /var/log/lightdm/lightdm.log
5199
    //     [+2.80s] DEBUG: XServer 0: Got signal from X server :0
5200
    //     [+2147.35s] DEBUG: Seat seat0: Display server stopped
5201
    //
5202
    DTPD!(
5203
        concatcp!(r"^\[", RP_BLANKSq, r"\+", CGP_UPTIME_F23, r"s\]", RP_BLANK),
5204
        DTFSS_u, 0, 25, CGN_UPTIME, CGN_FRACTIONAL,
5205
        &[
5206
            (2, 6, (O_L, 1970, 1, 1, 0, 0, 0, 0), "[+0.00s] DEBUG: Logging to /var/log/lightdm/lightdm.log"),
5207
            (5, 12, (O_L, 1970, 1, 1, 0, 35, 47, 350000000), "[   +2147.35s] DEBUG: Seat seat0: Display server stopped"),
5208
            (2, 9, (O_L, 1970, 1, 1, 0, 35, 47, 350000000), "[+2147.35s] DEBUG: Seat seat0: Display server stopped"),
5209
        ],
5210
        line!(),
5211
    ),
5212
    // ---------------------------------------------------------------------------------------------
5213
    //
5214
    // same pattern as prior without specifying leading RP_LEVELS, e.g. `DEBUG`, with leading day of week
5215
    // XXX: These next four patterns capture day of week which is redundant with numeric day of month.
5216
    //      The next next four patterns do the same but without the day of week.
5217
    //      The motivation for capturing day of week is purely for better decoration when color printing is enabled.
5218
    //      Though capturing day of week does give a little more assurance of the matched substring.
5219
    //      But regexs are expensive so these are of questionable value.
5220
    DTPD!(
5221
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZZ_U, RP_NOALNUM),
5222
        DTFSS_BdHMSYZ, 0, 400, CGN_DAYa, CGN_TZ,
5223
        &[
5224
            (5, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 2000 PDT === Started libdnf-0.31.0 ==="),
5225
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 2000 PDT fetching rpmdb"),
5226
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 2000 PDT"),
5227
        ],
5228
        line!(),
5229
    ),
5230
    DTPD!(
5231
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzc, RP_NOALNUM),
5232
        DTFSS_BdHMSYzc, 0, 400, CGN_DAYa, CGN_TZ,
5233
        &[
5234
            (5, 36, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 2000 -07:00 === Started libdnf-0.31.0 ==="),
5235
            (6, 37, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 2000 -07:00 fetching rpmdb"),
5236
        ],
5237
        line!(),
5238
    ),
5239
    DTPD!(
5240
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZz, RP_NOALNUM),
5241
        DTFSS_BdHMSYz, 0, 400, CGN_DAYa, CGN_TZ,
5242
        &[
5243
            (5, 35, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 2000 -0700 === Started libdnf-0.31.0 ==="),
5244
            (6, 36, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 2000 -0700 fetching rpmdb"),
5245
        ],
5246
        line!(),
5247
    ),
5248
    DTPD!(
5249
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzp, RP_NOALNUM),
5250
        DTFSS_BdHMSYzp, 0, 400, CGN_DAYa, CGN_TZ,
5251
        &[
5252
            (5, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 2000 -07 === Started libdnf-0.31.0 ==="),
5253
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 2000 -07 fetching rpmdb"),
5254
        ],
5255
        line!(),
5256
    ),
5257
    // same pattern as prior swapped year and timezone
5258
    DTPD!(
5259
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZZ_U, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5260
        DTFSS_BdHMSYZ, 0, 400, CGN_DAYa, CGN_YEAR,
5261
        &[
5262
            (5, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 PDT 2000 === Started libdnf-0.31.0 ==="),
5263
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 PDT 2000 fetching rpmdb"),
5264
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 PDT 2000"),
5265
        ],
5266
        line!(),
5267
    ),
5268
    DTPD!(
5269
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZzc, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5270
        DTFSS_BdHMSYzc, 0, 400, CGN_DAYa, CGN_YEAR,
5271
        &[
5272
            (5, 36, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 -07:00 2000 === Started libdnf-0.31.0 ==="),
5273
            (6, 37, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 -07:00 2000 fetching rpmdb"),
5274
        ],
5275
        line!(),
5276
    ),
5277
    DTPD!(
5278
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZz, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5279
        DTFSS_BdHMSYz, 0, 400, CGN_DAYa, CGN_YEAR,
5280
        &[
5281
            (5, 35, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 -0700 2000 === Started libdnf-0.31.0 ==="),
5282
            (6, 36, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 -0700 2000 fetching rpmdb"),
5283
        ],
5284
        line!(),
5285
    ),
5286
    DTPD!(
5287
        concatcp!(RP_NOALPHAb, CGP_DAYa3, RP_BLANK, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZzp, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5288
        DTFSS_BdHMSYzp, 0, 400, CGN_DAYa, CGN_YEAR,
5289
        &[
5290
            (5, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Fri Jun-16 14:09:58 -07 2000 === Started libdnf-0.31.0 ==="),
5291
            (6, 34, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Fri Jun 16 14:09:58 -07 2000 fetching rpmdb"),
5292
        ],
5293
        line!(),
5294
    ),
5295
    // same pattern as prior without leading day of week
5296
    DTPD!(
5297
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZZ_U, RP_NOALNUM),
5298
        DTFSS_BdHMSYZ, 0, 400, CGN_MONTH, CGN_TZ,
5299
        &[
5300
            (5, 29, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Jun-16 14:09:58 2000 PDT === Started libdnf-0.31.0 ==="),
5301
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 2000 PDT fetching rpmdb"),
5302
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 2000 PDT"),
5303
        ],
5304
        line!(),
5305
    ),
5306
    DTPD!(
5307
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzc, RP_NOALNUM),
5308
        DTFSS_BdHMSYzc, 0, 400, CGN_MONTH, CGN_TZ,
5309
        &[
5310
            (5, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Jun-16 14:09:58 2000 -07:00 === Started libdnf-0.31.0 ==="),
5311
            (6, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 2000 -07:00 fetching rpmdb"),
5312
        ],
5313
        line!(),
5314
    ),
5315
    DTPD!(
5316
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZz, RP_NOALNUM),
5317
        DTFSS_BdHMSYz, 0, 400, CGN_MONTH, CGN_TZ,
5318
        &[
5319
            (5, 31, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Jun-16 14:09:58 2000 -0700 === Started libdnf-0.31.0 ==="),
5320
            (6, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 2000 -0700 fetching rpmdb"),
5321
        ],
5322
        line!(),
5323
    ),
5324
    DTPD!(
5325
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_BLANK, CGP_TZzp, RP_NOALNUM),
5326
        DTFSS_BdHMSYzp, 0, 400, CGN_MONTH, CGN_TZ,
5327
        &[
5328
            (5, 29, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Jun-16 14:09:58 2000 -07 === Started libdnf-0.31.0 ==="),
5329
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 2000 -07 fetching rpmdb"),
5330
        ],
5331
        line!(),
5332
    ),
5333
    // same pattern as prior but swapped year and timezone
5334
    DTPD!(
5335
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZZ_U, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5336
        DTFSS_BdHMSYZ, 0, 400, CGN_MONTH, CGN_YEAR,
5337
        &[
5338
            (5, 29, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "____ Jun-16 14:09:58 PDT 2000 === Started libdnf-0.31.0 ==="),
5339
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 PDT 2000 fetching rpmdb"),
5340
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "_____ Jun 16 14:09:58 PDT 2000\n"),
5341
        ],
5342
        line!(),
5343
    ),
5344
    DTPD!(
5345
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZzc, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5346
        DTFSS_BdHMSYzc, 0, 400, CGN_MONTH, CGN_YEAR,
5347
        &[
5348
            (5, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 -07:00 2000 === Started libdnf-0.31.0 ==="),
5349
            (6, 33, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 -07:00 2000 fetching rpmdb"),
5350
        ],
5351
        line!(),
5352
    ),
5353
    DTPD!(
5354
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZz, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5355
        DTFSS_BdHMSYz, 0, 400, CGN_MONTH, CGN_YEAR,
5356
        &[
5357
            (5, 31, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 -0700 2000 === Started libdnf-0.31.0 ==="),
5358
            (6, 32, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 -0700 2000 fetching rpmdb"),
5359
        ],
5360
        line!(),
5361
    ),
5362
    DTPD!(
5363
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_TZzp, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5364
        DTFSS_BdHMSYzp, 0, 400, CGN_MONTH, CGN_YEAR,
5365
        &[
5366
            (5, 29, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 -07 2000 === Started libdnf-0.31.0 ==="),
5367
            (6, 30, (O_M7, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 -07 2000 fetching rpmdb"),
5368
        ],
5369
        line!(),
5370
    ),
5371
    // same pattern as prior without timezone
5372
    DTPD!(
5373
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_BLANK, CGP_YEAR, RP_NOALNUM),
5374
        DTFSS_BdHMSY, 0, 400, CGN_MONTH, CGN_YEAR,
5375
        &[
5376
            (5, 25, (O_L, 2000, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 2000 === Started libdnf-0.31.0 ==="),
5377
            (6, 26, (O_L, 2000, 6, 16, 14, 9, 58, 0), "DEBUG Jun 16 14:09:58 2000 fetching rpmdb"),
5378
        ],
5379
        line!(),
5380
    ),
5381
    // same pattern as prior without timezone or year
5382
    DTPD!(
5383
        concatcp!(RP_NOALPHAb, CGP_MONTHBb, D_D, CGP_DAYde, D_DHcdqu, CGP_HOUR, D_T, CGP_MINUTE, D_T, CGP_SECOND, RP_NOALNUM),
5384
        DTFSS_BdHMS, 0, 400, CGN_MONTH, CGN_SECOND,
5385
        &[
5386
            (5, 20, (O_L, YD, 6, 16, 14, 9, 58, 0), "INFO Jun-16 14:09:58 === Started libdnf-0.31.0 ==="),
5387
            (6, 21, (O_L, YD, 6, 16, 14, 9, 58, 0), "DEBUG Jun-16 14:09:58 fetching rpmdb"),
5388
        ],
5389
        line!(),
5390
    ),
5391
];
5392

5393
// TODO: [2023/04/29] the initialisation of `DATETIME_PARSE_DATAS_REGEX_VEC` takes much
5394
//       time during program startup. Like 1/4 to 1/3. I'm concerned that
5395
//       these is duplication of the created `RegEx` instances. I tried to
5396
//       prove this wasn't the case by checking the address of the `RegEx`
5397
//       instances in use. They were the same. e.g. thread 1 address of `RegEx`
5398
//       at `DATETIME_PARSE_DATAS_REGEX_VEC[0]` is `X` and thread 2 address of
5399
//       `RegEx` at `DATETIME_PARSE_DATAS_REGEX_VEC[0]` is also `X`.
5400
//       However, then I found this bug in `rust`:
5401
//            https://github.com/rust-lang/rust/issues/79738
5402
//       > this code actually leads to two allocations containing 42, i.e.,
5403
//       > FOO and BAR point to different things. The linker later merges the two,
5404
//       > so the issue is currently not directly observable.
5405
//       ```rust
5406
//       pub mod a {
5407
//           #[no_mangle]
5408
//           pub static FOO: &i32 = &42;
5409
//       }
5410
//       pub mod b {
5411
//           #[no_mangle]
5412
//           pub static BAR: &i32 = &*crate::a::FOO;
5413
//       }
5414
//       fn main() {
5415
//           assert_eq!(a::FOO as *const _, b::BAR as *const _);
5416
//       }
5417
//       ```
5418
//       I have to spend some time to prove if this is happening with the
5419
//       `RegEx` instances in `DATETIME_PARSE_DATAS_REGEX_VEC`.
5420
//       Obviously, this code uses `lazy_static` and the bug is for `static`.
5421
//       But either way, after find that rust bug I'm less sure of my original conclusion.
5422

5423
lazy_static! {
5424
    /// Count of compiled regular expressions from `DateTimeParseData` instances.
5425
    pub static ref DateTimeParseDatasCompiledCount: RwLock<usize> = {
5426
        defo!("init DateTimeParseDatasCompiledCount");
5427

5428
        RwLock::new(0)
5429
    };
5430

5431
    /// Run-time created mapping of compiled [`Regex`].
5432
    ///
5433
    /// This has the same mapping of index values as
5434
    /// `DATETIME_PARSE_DATAS` array but `DATETIME_PARSE_DATAS_REGEX_VEC`
5435
    /// maps to the compiled `Regex`.
5436
    ///
5437
    /// [`Regex`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Regex.html
5438
    // TODO: cost-savings: each compiled regex requires ≈133,000 Bytes on the heap according
5439
    //       to `./tools/valgrind-massif.sh`. This is a lot of memory.
5440
    //       An easy way to reduce baseline heap-use is drop the unused ones.
5441
    //       That would need to occur after all file threads have passed the stage 1 blockzero analysis.
5442
    //       Another thing to try is "on demand" compilation of the regexes.
5443
    pub(crate) static ref DATETIME_PARSE_DATAS_REGEX_VEC: DateTimeParseInstrsRegexVec = {
5444
        defn!("init DATETIME_PARSE_DATAS_REGEX_VEC");
5445
        let mut datas: DateTimeParseInstrsRegexVec = DateTimeParseInstrsRegexVec::with_capacity(
5446
            DATETIME_PARSE_DATAS_LEN
5447
        );
5448
        let mut count: usize = 0;
5449
        while count < DATETIME_PARSE_DATAS_LEN {
5450
            defo!("init OnceCell {:?}", count);
5451
            datas.push(OnceCell::new());
5452
            count += 1;
5453
        }
5454
        defx!("init DATETIME_PARSE_DATAS_REGEX_VEC {:?}", count);
5455

5456
        datas
5457
    };
5458
}
5459

5460
// TODO: Issue #6 handle all Unicode whitespace.
5461
//       This fn is essentially counteracting an errant call to
5462
//       `std::string:trim` within `Local.datetime_from_str`.
5463
//       `trim` removes "Unicode Derived Core Property White_Space".
5464
//       This implementation handles three whitespace chars. There are
5465
//       twenty-five whitespace chars according to
5466
//       <https://en.wikipedia.org/wiki/Unicode_character_property#Whitespace>.
5467
//
5468
/// Match spaces at beginning and ending of `value`.
5469
/// Return `true` if mismatch of whitespace was found between `value` and
5470
/// `pattern`, e.g. `value` is `"2022-01-01T02:03:04"`
5471
/// but pattern is `"    %Y-%d-%mT%H:%M:%S"`.
5472
/// Else return `false`.
5473
/// Workaround for chrono
5474
/// [Issue #660](https://github.com/chronotope/chrono/issues/660).
5475
#[allow(non_snake_case)]
5476
pub fn datetime_from_str_workaround_Issue660(
58,501✔
5477
    value: &str,
58,501✔
5478
    pattern: &DateTimePattern_str,
58,501✔
5479
) -> bool {
58,501✔
5480
    const SPACES: &str = " ";
5481
    const TABS: &str = "\t";
5482
    const LINE_ENDS: &str = "\n\r";
5483

5484
    // match whitespace forwards from beginning
5485
    let mut v_sc: u32 = 0; // `value` spaces count
58,501✔
5486
    let mut v_tc: u32 = 0; // `value` tabs count
58,501✔
5487
    let mut v_ec: u32 = 0; // `value` line ends count
58,501✔
5488
    let mut v_brk: bool = false;
58,501✔
5489
    for v_ in value.chars() {
58,544✔
5490
        if SPACES.contains(v_) {
58,544✔
5491
            v_sc += 1;
41✔
5492
        } else if TABS.contains(v_) {
58,503✔
5493
            v_tc += 1;
9✔
5494
        } else if LINE_ENDS.contains(v_) {
58,494✔
5495
            v_ec += 1;
9✔
5496
        } else {
9✔
5497
            v_brk = true;
58,485✔
5498
            break;
58,485✔
5499
        }
5500
    }
5501
    let mut p_sc: u32 = 0; // `pattern` space count
58,501✔
5502
    let mut p_tc: u32 = 0; // `pattern` tab count
58,501✔
5503
    let mut p_ec: u32 = 0; // `pattern` line ends count
58,501✔
5504
    let mut p_brk: bool = false;
58,501✔
5505
    for p_ in pattern.chars() {
58,547✔
5506
        if SPACES.contains(p_) {
58,547✔
5507
            p_sc += 1;
43✔
5508
        } else if TABS.contains(p_) {
58,504✔
5509
            p_tc += 1;
9✔
5510
        } else if LINE_ENDS.contains(p_) {
58,495✔
5511
            p_ec += 1;
9✔
5512
        } else {
9✔
5513
            p_brk = true;
58,486✔
5514
            break;
58,486✔
5515
        }
5516
    }
5517
    if v_sc != p_sc || v_tc != p_tc || v_ec != p_ec {
58,501✔
5518
        return false;
12✔
5519
    }
58,489✔
5520

5521
    // match whitespace backwards from ending
5522
    v_sc = 0;
58,489✔
5523
    v_tc = 0;
58,489✔
5524
    v_ec = 0;
58,489✔
5525
    if v_brk {
58,489✔
5526
        for v_ in value.chars().rev() {
58,493✔
5527
            if SPACES.contains(v_) {
58,493✔
5528
                v_sc += 1;
15✔
5529
            } else if TABS.contains(v_) {
58,478✔
5530
                v_tc += 1;
×
5531
            } else if LINE_ENDS.contains(v_) {
58,478✔
5532
                v_ec += 1;
1✔
5533
            } else {
1✔
5534
                break;
58,477✔
5535
            }
5536
        }
5537
    }
12✔
5538
    p_sc = 0;
58,489✔
5539
    p_tc = 0;
58,489✔
5540
    p_ec = 0;
58,489✔
5541
    if p_brk {
58,489✔
5542
        for p_ in pattern.chars().rev() {
58,499✔
5543
            if SPACES.contains(p_) {
58,499✔
5544
                p_sc += 1;
16✔
5545
            } else if TABS.contains(p_) {
58,483✔
5546
                p_tc += 1;
3✔
5547
            } else if LINE_ENDS.contains(p_) {
58,480✔
5548
                p_ec += 1;
2✔
5549
            } else {
2✔
5550
                break;
58,478✔
5551
            }
5552
        }
5553
    }
11✔
5554
    if v_sc != p_sc || v_tc != p_tc || v_ec != p_ec {
58,489✔
5555
        return false;
6✔
5556
    }
58,483✔
5557

5558
    true
58,483✔
5559
}
58,501✔
5560

5561
/// Decoding [\[`u8`\]] bytes to a [`str`] takes a surprisingly long amount of
5562
/// time, according to script `tools/flamegraph.sh`.
5563
///
5564
/// First check `u8` slice with custom simplistic checker that, in case of
5565
/// complications, falls back to using higher-resource and more-precise checker
5566
/// [`encoding_rs::mem::utf8_latin1_up_to`].
5567
///
5568
/// This uses built-in unsafe [`from_utf8_unchecked`].
5569
///
5570
/// See `benches/bench_decode_utf.rs` for comparison of `bytes` → `str`
5571
/// decode strategies.
5572
///
5573
/// [\[`u8`\]]: u8
5574
/// [`str`]: str
5575
/// [`encoding_rs::mem::utf8_latin1_up_to`]: <https://docs.rs/encoding_rs/0.8.31/encoding_rs/mem/fn.utf8_latin1_up_to.html>
5576
/// [`from_utf8_unchecked`]: std::str::from_utf8_unchecked
5577
#[inline(always)]
5578
pub fn u8_to_str(data: &[u8]) -> Option<&str> {
26,530✔
5579
    let dts: &str;
5580
    let mut fallback = false;
26,530✔
5581
    // custom check for UTF8; fast but imperfect
5582
    if !data.is_ascii() {
26,530✔
5583
        fallback = true;
×
5584
    }
26,530✔
5585
    if fallback {
26,530✔
5586
        // found non-ASCII, fallback to checking with `utf8_latin1_up_to`
5587
        // which is a thorough check
5588
        let va = encoding_rs::mem::utf8_latin1_up_to(data);
×
5589
        if va != data.len() {
×
5590
            // TODO: this needs a better resolution
5591
            de_wrn!("u8_to_str return None; va {} != {} data.len()", va, data.len());
×
5592
            return None; // invalid UTF8
×
5593
        }
×
5594
    }
26,530✔
5595
    unsafe {
26,530✔
5596
        dts = std::str::from_utf8_unchecked(data);
26,530✔
5597
    };
26,530✔
5598

5599
    Some(dts)
26,530✔
5600
}
26,530✔
5601

5602
/// Convert `data` to a chrono [`Option<DateTime<FixedOffset>>`] instance.
5603
///
5604
/// Compensate for a missing timezone.
5605
///
5606
/// - `data` to parse that has a datetime string
5607
/// - strftime `pattern` to use for parsing, must complement `data`
5608
/// - `has_tz`, the `pattern` has a timezone (`%Z`, `%z`, etc.)?
5609
/// - `tz_offset` fallback timezone offset when `!has_tz`
5610
///
5611
/// [`Option<DateTime<FixedOffset>>`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#impl-DateTime%3CFixedOffset%3E
5612
pub fn datetime_parse_from_str(
34,601✔
5613
    data: &str,
34,601✔
5614
    pattern: &DateTimePattern_str,
34,601✔
5615
    has_tz: bool,
34,601✔
5616
    tz_offset: &FixedOffset,
34,601✔
5617
) -> DateTimeLOpt {
34,601✔
5618
    defn!("(pattern {:?}, has_tz {}, tz_offset {:?}, data {:?})", pattern, has_tz, tz_offset, str_to_String_noraw(data));
34,601✔
5619

5620
    // saved rust playground for quick testing chrono `DateTime::parse_from_str`
5621
    // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e6f44c79dbb3d2c05c55ffba9bd91c76
5622

5623
    // if `has_tz` then create a `DateTime`.
5624
    // else if `!has_tz` then create a `NaiveDateTime`, then convert that to `DateTime` with aid
5625
    // of crate `chrono_tz`.
5626
    if has_tz {
34,601✔
5627
        match DateTime::parse_from_str(data, pattern) {
5,353✔
5628
            Ok(val) => {
2,284✔
5629
                defo!(
2,284✔
5630
                    "DateTime::parse_from_str({:?}, {:?}) extrapolated DateTime {:?}",
5631
                    str_to_String_noraw(data),
2,284✔
5632
                    pattern,
5633
                    val,
5634
                );
5635
                // HACK: workaround chrono Issue #660 by checking for matching begin, end of `data`
5636
                //       and `dt_pattern`
5637
                //       See Issue #6
5638
                if !datetime_from_str_workaround_Issue660(data, pattern) {
2,284✔
5639
                    defn!("skip match due to chrono Issue #660");
1✔
5640
                    return None;
1✔
5641
                }
2,283✔
5642
                defx!("return Some({:?})", val);
2,283✔
5643

5644
                Some(val)
2,283✔
5645
            }
5646
            Err(_err) => {
3,069✔
5647
                defx!("DateTime::parse_from_str({:?}, {:?}) failed ParseError: {}", data, pattern, _err);
3,069✔
5648

5649
                None
3,069✔
5650
            }
5651
        }
5652
    } else {
5653
        // !has_tz (no timezone in `data`)
5654
        // first convert to a `NaiveDateTime` instance
5655
        let dt_naive = match NaiveDateTime::parse_from_str(data, pattern) {
29,248✔
5656
            Ok(val) => {
28,091✔
5657
                defo!(
28,091✔
5658
                    "NaiveDateTime.parse_from_str({:?}, {:?}) extrapolated NaiveDateTime {:?}",
5659
                    str_to_String_noraw(data),
28,091✔
5660
                    pattern,
5661
                    val,
5662
                );
5663
                // HACK: workaround chrono Issue #660 by checking for matching begin, end of `data`
5664
                //       and `pattern`
5665
                if !datetime_from_str_workaround_Issue660(data, pattern) {
28,091✔
5666
                    defx!("skip match due to chrono Issue #660");
1✔
5667
                    return None;
1✔
5668
                }
28,090✔
5669
                defo!("dt_naive={:?}", val);
28,090✔
5670

5671
                val
28,090✔
5672
            }
5673
            Err(_err) => {
1,157✔
5674
                defx!("NaiveDateTime.parse_from_str({:?}, {:?}) failed ParseError: {}", data, pattern, _err);
1,157✔
5675
                return None;
1,157✔
5676
            }
5677
        };
5678
        // second convert the `NaiveDateTime` instance to a `DateTime<FixedOffset>` instance
5679
        match tz_offset
28,090✔
5680
            .from_local_datetime(&dt_naive)
28,090✔
5681
            .earliest()
28,090✔
5682
        {
5683
            Some(val) => {
28,090✔
5684
                defo!(
28,090✔
5685
                    "tz_offset.from_local_datetime({:?}).earliest() extrapolated NaiveDateTime {:?}",
5686
                    dt_naive,
5687
                    val,
5688
                );
5689
                // HACK: workaround chrono Issue #660 by checking for matching begin, end of `data`
5690
                //       and `pattern`
5691
                if !datetime_from_str_workaround_Issue660(data, pattern) {
28,090✔
5692
                    defx!("skip match due to chrono Issue #660, return None");
×
5693
                    return None;
×
5694
                }
28,090✔
5695
                defx!("return {:?}", Some(val));
28,090✔
5696

5697
                Some(val)
28,090✔
5698
            }
5699
            None => {
5700
                defx!("tz_offset.from_local_datetime({:?}, {:?}) returned None, return None", data, pattern);
×
5701
                None
×
5702
            }
5703
        }
5704
    }
5705
}
34,601✔
5706

5707
/// Call [`datetime_parse_from_str`] with a `pattern` containing a timezone.
5708
pub fn datetime_parse_from_str_w_tz(
27✔
5709
    data: &str,
27✔
5710
    pattern: &DateTimePattern_str,
27✔
5711
) -> DateTimeLOpt {
27✔
5712
    datetime_parse_from_str(
27✔
5713
        data,
27✔
5714
        pattern,
27✔
5715
        true,
5716
        &FixedOffset::east_opt(-9999).unwrap(),
27✔
5717
    )
5718
}
27✔
5719

5720
/// Data of interest from a set of [`regex::Captures`] for a datetime
5721
/// substring found in a [`Line`].
5722
///
5723
/// - datetime substring begin index
5724
/// - datetime substring end index
5725
/// - datetime
5726
///
5727
/// [`Line`]: crate::data::line::Line
5728
/// [`regex::Captures`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Captures.html
5729
// TODO: change to a typed `struct CapturedDtData(...)`
5730
pub type CapturedDtData = (LineIndex, LineIndex, DateTimeL);
5731

5732
/// Macro helper to [`captures_to_buffer_bytes`].
5733
macro_rules! copy_capturegroup_to_buffer {
5734
    (
5735
        $name:ident,
5736
        $captures:ident,
5737
        $buffer:ident,
5738
        $at:ident
5739
    ) => {
5740
        {
5741
            let len_: usize = $captures
5742
                .name($name)
5743
                .as_ref()
5744
                .unwrap()
5745
                .as_bytes()
5746
                .len();
5747
            defo!("copy_capturegroup_to_buffer! buffer[{:?}‥{:?}]", $at, $at + len_);
5748
            $buffer[$at..$at + len_].copy_from_slice(
5749
                $captures
5750
                    .name($name)
5751
                    .as_ref()
5752
                    .unwrap()
5753
                    .as_bytes(),
5754
            );
5755
            $at += len_;
5756
        }
5757
    };
5758
}
5759

5760
/// Macro helper to [`captures_to_buffer_bytes`].
5761
macro_rules! copy_slice_to_buffer {
5762
    (
5763
        $u8_slice:expr,
5764
        $buffer:ident,
5765
        $at:ident
5766
    ) => {
5767
        {
5768
            let len_: usize = $u8_slice.len();
5769
            defo!("copy_slice_to_buffer! buffer[{:?}‥{:?}]", $at, $at + len_);
5770
            $buffer[$at..$at + len_].copy_from_slice($u8_slice);
5771
            $at += len_;
5772
        }
5773
    };
5774
}
5775

5776
/// Macro helper to [`captures_to_buffer_bytes`].
5777
macro_rules! copy_u8_to_buffer {
5778
    (
5779
        $u8_:expr,
5780
        $buffer:ident,
5781
        $at:ident
5782
    ) => {
5783
        {
5784
            defo!("copy_slice_to_buffer! buffer[{:?}] = {:?}", $at, $u8_);
5785
            $buffer[$at] = $u8_;
5786
            $at += 1;
5787
        }
5788
    };
5789
}
5790

5791
// Variables `const MONTH_` are helpers to [`month_bB_to_month_m_bytes`].
5792
//
5793
// MONTH_XY_B_l, month XY as `%B` form, lowercase
5794
// MONTH_XY_b_l, month XY as `%b` form, lowercase
5795
// MONTH_XY_B_u, month XY as `%B` form, uppercase
5796
// MONTH_XY_b_u, month XY as `%b` form, uppercase
5797
// MONTH_XY_b_U, month XY as `%b` form, uppercase all
5798

5799
const MONTH_01_b_l: &[u8] = b"jan";
5800
const MONTH_01_b_u: &[u8] = b"Jan";
5801
const MONTH_01_b_U: &[u8] = b"JAN";
5802
const MONTH_01_b_ld: &[u8] = b"jan.";
5803
const MONTH_01_b_ud: &[u8] = b"Jan.";
5804
const MONTH_01_b_Ud: &[u8] = b"JAN.";
5805
const MONTH_01_B_l: &[u8] = b"january";
5806
const MONTH_01_B_u: &[u8] = b"January";
5807
const MONTH_01_B_U: &[u8] = b"JANUARY";
5808
const MONTH_01_m: &[u8] = b"01";
5809
const MONTH_02_b_l: &[u8] = b"feb";
5810
const MONTH_02_b_u: &[u8] = b"Feb";
5811
const MONTH_02_b_U: &[u8] = b"FEB";
5812
const MONTH_02_b_ld: &[u8] = b"feb.";
5813
const MONTH_02_b_ud: &[u8] = b"Feb.";
5814
const MONTH_02_b_Ud: &[u8] = b"FEB.";
5815
const MONTH_02_B_l: &[u8] = b"february";
5816
const MONTH_02_B_u: &[u8] = b"February";
5817
const MONTH_02_B_U: &[u8] = b"FEBRUARY";
5818
const MONTH_02_m: &[u8] = b"02";
5819
const MONTH_03_b_l: &[u8] = b"mar";
5820
const MONTH_03_b_u: &[u8] = b"Mar";
5821
const MONTH_03_b_U: &[u8] = b"MAR";
5822
const MONTH_03_b_ld: &[u8] = b"mar.";
5823
const MONTH_03_b_ud: &[u8] = b"Mar.";
5824
const MONTH_03_b_Ud: &[u8] = b"MAR.";
5825
const MONTH_03_B_l: &[u8] = b"march";
5826
const MONTH_03_B_u: &[u8] = b"March";
5827
const MONTH_03_B_U: &[u8] = b"MARCH";
5828
const MONTH_03_m: &[u8] = b"03";
5829
const MONTH_04_b_l: &[u8] = b"apr";
5830
const MONTH_04_b_u: &[u8] = b"Apr";
5831
const MONTH_04_b_U: &[u8] = b"APR";
5832
const MONTH_04_b_ld: &[u8] = b"apr.";
5833
const MONTH_04_b_ud: &[u8] = b"Apr.";
5834
const MONTH_04_b_Ud: &[u8] = b"APR.";
5835
const MONTH_04_B_l: &[u8] = b"april";
5836
const MONTH_04_B_u: &[u8] = b"April";
5837
const MONTH_04_B_U: &[u8] = b"APRIL";
5838
const MONTH_04_m: &[u8] = b"04";
5839
const MONTH_05_b_l: &[u8] = b"may";
5840
const MONTH_05_b_u: &[u8] = b"May";
5841
const MONTH_05_b_U: &[u8] = b"MAY";
5842
#[allow(dead_code)]
5843
const MONTH_05_B_l: &[u8] = b"may"; // not used, defined for completeness
5844
#[allow(dead_code)]
5845
const MONTH_05_B_u: &[u8] = b"May"; // not used, defined for completeness
5846
#[allow(dead_code)]
5847
const MONTH_05_B_U: &[u8] = b"MAY"; // not used, defined for completeness
5848
const MONTH_05_m: &[u8] = b"05";
5849
const MONTH_06_b_l: &[u8] = b"jun";
5850
const MONTH_06_b_u: &[u8] = b"Jun";
5851
const MONTH_06_b_U: &[u8] = b"JUN";
5852
const MONTH_06_b_ld: &[u8] = b"jun.";
5853
const MONTH_06_b_ud: &[u8] = b"Jun.";
5854
const MONTH_06_b_Ud: &[u8] = b"JUN.";
5855
const MONTH_06_B_l: &[u8] = b"june";
5856
const MONTH_06_B_u: &[u8] = b"June";
5857
const MONTH_06_B_U: &[u8] = b"JUNE";
5858
const MONTH_06_m: &[u8] = b"06";
5859
const MONTH_07_b_l: &[u8] = b"jul";
5860
const MONTH_07_b_u: &[u8] = b"Jul";
5861
const MONTH_07_b_U: &[u8] = b"JUL";
5862
const MONTH_07_b_ld: &[u8] = b"jul.";
5863
const MONTH_07_b_ud: &[u8] = b"Jul.";
5864
const MONTH_07_b_Ud: &[u8] = b"JUL.";
5865
const MONTH_07_B_l: &[u8] = b"july";
5866
const MONTH_07_B_u: &[u8] = b"July";
5867
const MONTH_07_B_U: &[u8] = b"JULY";
5868
const MONTH_07_m: &[u8] = b"07";
5869
const MONTH_08_b_l: &[u8] = b"aug";
5870
const MONTH_08_b_u: &[u8] = b"Aug";
5871
const MONTH_08_b_U: &[u8] = b"AUG";
5872
const MONTH_08_b_ld: &[u8] = b"aug.";
5873
const MONTH_08_b_ud: &[u8] = b"Aug.";
5874
const MONTH_08_b_Ud: &[u8] = b"AUG.";
5875
const MONTH_08_B_l: &[u8] = b"august";
5876
const MONTH_08_B_u: &[u8] = b"August";
5877
const MONTH_08_B_U: &[u8] = b"AUGUST";
5878
const MONTH_08_m: &[u8] = b"08";
5879
const MONTH_09_b_l: &[u8] = b"sep";
5880
const MONTH_09_b_u: &[u8] = b"Sep";
5881
const MONTH_09_b_U: &[u8] = b"SEP";
5882
const MONTH_09_b_ld: &[u8] = b"sep.";
5883
const MONTH_09_b_ud: &[u8] = b"Sep.";
5884
const MONTH_09_b_Ud: &[u8] = b"SEP.";
5885
const MONTH_09_B_l: &[u8] = b"september";
5886
const MONTH_09_B_u: &[u8] = b"September";
5887
const MONTH_09_B_U: &[u8] = b"SEPTEMBER";
5888
const MONTH_09_m: &[u8] = b"09";
5889
const MONTH_10_b_l: &[u8] = b"oct";
5890
const MONTH_10_b_u: &[u8] = b"Oct";
5891
const MONTH_10_b_U: &[u8] = b"OCT";
5892
const MONTH_10_b_ld: &[u8] = b"oct.";
5893
const MONTH_10_b_ud: &[u8] = b"Oct.";
5894
const MONTH_10_b_Ud: &[u8] = b"OCT.";
5895
const MONTH_10_B_l: &[u8] = b"october";
5896
const MONTH_10_B_u: &[u8] = b"October";
5897
const MONTH_10_B_U: &[u8] = b"OCTOBER";
5898
const MONTH_10_m: &[u8] = b"10";
5899
const MONTH_11_b_l: &[u8] = b"nov";
5900
const MONTH_11_b_u: &[u8] = b"Nov";
5901
const MONTH_11_b_U: &[u8] = b"NOV";
5902
const MONTH_11_b_ld: &[u8] = b"nov.";
5903
const MONTH_11_b_ud: &[u8] = b"Nov.";
5904
const MONTH_11_b_Ud: &[u8] = b"NOV.";
5905
const MONTH_11_B_l: &[u8] = b"november";
5906
const MONTH_11_B_u: &[u8] = b"November";
5907
const MONTH_11_B_U: &[u8] = b"NOVEMBER";
5908
const MONTH_11_m: &[u8] = b"11";
5909
const MONTH_12_b_l: &[u8] = b"dec";
5910
const MONTH_12_b_u: &[u8] = b"Dec";
5911
const MONTH_12_b_U: &[u8] = b"DEC";
5912
const MONTH_12_b_ld: &[u8] = b"dec.";
5913
const MONTH_12_b_ud: &[u8] = b"Dec.";
5914
const MONTH_12_b_Ud: &[u8] = b"DEC.";
5915
const MONTH_12_B_l: &[u8] = b"december";
5916
const MONTH_12_B_u: &[u8] = b"December";
5917
const MONTH_12_B_U: &[u8] = b"DECEMBER";
5918
const MONTH_12_m: &[u8] = b"12";
5919

5920
/// Transform strftime `%B`, `%b` (i.e. `"January"`, `"Jan"`) to
5921
/// strftime `%m` (i.e. `"01"`).
5922
///
5923
/// Helper to [`captures_to_buffer_bytes`].
5924
#[allow(non_snake_case)]
5925
fn month_bB_to_month_m_bytes(
1,926✔
5926
    data: &[u8],
1,926✔
5927
    buffer: &mut [u8],
1,926✔
5928
) {
1,926✔
5929
    match data {
1,926✔
5930
        // try *b* matches first; it is more common
5931
        MONTH_01_b_l | MONTH_01_b_u | MONTH_01_b_U => buffer.copy_from_slice(MONTH_01_m),
1,926✔
5932
        MONTH_02_b_l | MONTH_02_b_u | MONTH_02_b_U => buffer.copy_from_slice(MONTH_02_m),
123✔
5933
        MONTH_03_b_l | MONTH_03_b_u | MONTH_03_b_U => buffer.copy_from_slice(MONTH_03_m),
50✔
5934
        MONTH_04_b_l | MONTH_04_b_u | MONTH_04_b_U => buffer.copy_from_slice(MONTH_04_m),
7✔
5935
        MONTH_05_b_l | MONTH_05_b_u | MONTH_05_b_U => buffer.copy_from_slice(MONTH_05_m),
65✔
5936
        MONTH_06_b_l | MONTH_06_b_u | MONTH_06_b_U => buffer.copy_from_slice(MONTH_06_m),
566✔
5937
        MONTH_07_b_l | MONTH_07_b_u | MONTH_07_b_U => buffer.copy_from_slice(MONTH_07_m),
23✔
5938
        MONTH_08_b_l | MONTH_08_b_u | MONTH_08_b_U => buffer.copy_from_slice(MONTH_08_m),
47✔
5939
        MONTH_09_b_l | MONTH_09_b_u | MONTH_09_b_U => buffer.copy_from_slice(MONTH_09_m),
17✔
5940
        MONTH_10_b_l | MONTH_10_b_u | MONTH_10_b_U => buffer.copy_from_slice(MONTH_10_m),
199✔
5941
        MONTH_11_b_l | MONTH_11_b_u | MONTH_11_b_U => buffer.copy_from_slice(MONTH_11_m),
8✔
5942
        MONTH_12_b_l | MONTH_12_b_u | MONTH_12_b_U => buffer.copy_from_slice(MONTH_12_m),
141✔
5943
        // then try *b*dot matches
5944
        MONTH_01_b_ld | MONTH_01_b_ud | MONTH_01_b_Ud => buffer.copy_from_slice(MONTH_01_m),
189✔
5945
        MONTH_02_b_ld | MONTH_02_b_ud | MONTH_02_b_Ud => buffer.copy_from_slice(MONTH_02_m),
×
5946
        MONTH_03_b_ld | MONTH_03_b_ud | MONTH_03_b_Ud => buffer.copy_from_slice(MONTH_03_m),
×
5947
        MONTH_04_b_ld | MONTH_04_b_ud | MONTH_04_b_Ud => buffer.copy_from_slice(MONTH_04_m),
×
5948
        // MONTH_05_b_ld not needed
5949
        MONTH_06_b_ld | MONTH_06_b_ud | MONTH_06_b_Ud => buffer.copy_from_slice(MONTH_06_m),
6✔
5950
        MONTH_07_b_ld | MONTH_07_b_ud | MONTH_07_b_Ud => buffer.copy_from_slice(MONTH_07_m),
×
5951
        MONTH_08_b_ld | MONTH_08_b_ud | MONTH_08_b_Ud => buffer.copy_from_slice(MONTH_08_m),
×
5952
        MONTH_09_b_ld | MONTH_09_b_ud | MONTH_09_b_Ud => buffer.copy_from_slice(MONTH_09_m),
12✔
5953
        MONTH_10_b_ld | MONTH_10_b_ud | MONTH_10_b_Ud => buffer.copy_from_slice(MONTH_10_m),
×
5954
        MONTH_11_b_ld | MONTH_11_b_ud | MONTH_11_b_Ud => buffer.copy_from_slice(MONTH_11_m),
×
5955
        MONTH_12_b_ld | MONTH_12_b_ud | MONTH_12_b_Ud => buffer.copy_from_slice(MONTH_12_m),
15✔
5956
        // then try *B* matches
5957
        MONTH_01_B_l | MONTH_01_B_u | MONTH_01_B_U => buffer.copy_from_slice(MONTH_01_m),
93✔
5958
        MONTH_02_B_l | MONTH_02_B_u | MONTH_02_B_U => buffer.copy_from_slice(MONTH_02_m),
75✔
5959
        MONTH_03_B_l | MONTH_03_B_u | MONTH_03_B_U => buffer.copy_from_slice(MONTH_03_m),
21✔
5960
        MONTH_04_B_l | MONTH_04_B_u | MONTH_04_B_U => buffer.copy_from_slice(MONTH_04_m),
×
5961
        //MONTH_05_B_l | MONTH_05_B_u | MONTH_05_B_U => buffer.copy_from_slice(MONTH_05_m),
5962
        MONTH_06_B_l | MONTH_06_B_u | MONTH_06_B_U => buffer.copy_from_slice(MONTH_06_m),
39✔
5963
        MONTH_07_B_l | MONTH_07_B_u | MONTH_07_B_U => buffer.copy_from_slice(MONTH_07_m),
×
5964
        MONTH_08_B_l | MONTH_08_B_u | MONTH_08_B_U => buffer.copy_from_slice(MONTH_08_m),
21✔
5965
        MONTH_09_B_l | MONTH_09_B_u | MONTH_09_B_U => buffer.copy_from_slice(MONTH_09_m),
21✔
5966
        MONTH_10_B_l | MONTH_10_B_u | MONTH_10_B_U => buffer.copy_from_slice(MONTH_10_m),
×
5967
        MONTH_11_B_l | MONTH_11_B_u | MONTH_11_B_U => buffer.copy_from_slice(MONTH_11_m),
×
5968
        MONTH_12_B_l | MONTH_12_B_u | MONTH_12_B_U => buffer.copy_from_slice(MONTH_12_m),
12✔
5969
        data_ => {
×
5970
            panic!("month_bB_to_month_m_bytes: unexpected month value {:?}", data_);
×
5971
        }
5972
    }
5973
}
1,926✔
5974

5975
/// Put [`Captures`] into `buffer` in a particular order and formatting.
5976
/// This is to prepare the regex matched data for passing to a later call to
5977
/// [`DateTime::parse_from_str`] (called outside of this function).
5978
///
5979
/// This bridges the crate `regex` regular expression pattern strings,
5980
/// [`DateTimeParseInstr::regex_pattern`], to crate `chrono` strftime strings,
5981
/// [`DateTimeParseInstr::dtfs`].
5982
///
5983
/// Directly relates to datetime format `dtfs` values in
5984
/// [`DATETIME_PARSE_DATAS`] which use `DTFSS_YmdHMS`, etc.
5985
///
5986
/// Transforms `%B` acceptable value to `%m` acceptable value.
5987
///
5988
/// Transforms `%e` acceptable value to `%d` acceptable value.
5989
///
5990
/// Transforms an uptime (seconds since system boot) to current seconds offset
5991
/// since UNIX_EPOCH. This allows later conversion via chrono strftime `%s`.
5992
///
5993
/// Transforms timezone offset inidicator MINUS SIGN `−` (U+2212) into
5994
/// HYPHEN-MINUS `-` (U+2D), e.g `−0700` becomes `-0700`.
5995
///
5996
/// [`Captures`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Captures.html
5997
/// [`DateTime::parse_from_str`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#method.parse_from_str
5998
// TODO: allow returning an `Error` instead of `panic!`
5999
#[inline(always)]
6000
pub(crate) fn captures_to_buffer_bytes(
25,884✔
6001
    buffer: &mut [u8],
25,884✔
6002
    captures: &regex::bytes::Captures,
25,884✔
6003
    year_opt: &Option<Year>,
25,884✔
6004
    systemtime_at_uptime_zero: &Option<SystemTime>,
25,884✔
6005
    tz_offset_string: &String,
25,884✔
6006
    dtfs: &DTFSSet,
25,884✔
6007
) -> usize {
25,884✔
6008
    defn!("(…, …, year_opt {:?}, systemtime_at_uptime_zero {:?}, tz_offset {:?}, …)",
25,884✔
6009
          year_opt, systemtime_at_uptime_zero, tz_offset_string);
6010

6011
    let mut at: usize = 0;
25,884✔
6012

6013
    defo!("process <epoch> {:?}…", dtfs.epoch);
25,884✔
6014
    match dtfs.epoch {
25,884✔
6015
        DTFS_Epoch::s => {
6016
            copy_capturegroup_to_buffer!(CGN_EPOCH, captures, buffer, at);
828✔
6017
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
828✔
6018
        }
6019
        DTFS_Epoch::_none => {}
25,056✔
6020
    }
6021

6022
    defo!("process <uptime> {:?}…", dtfs.uptime);
25,884✔
6023
    match dtfs.uptime {
25,884✔
6024
        DTFS_Uptime::u => {
6025
            // Here is where an important conversion happens:
6026
            // get the log uptime string, e.g. `"1.340"`, and convert it to a
6027
            // Duration. Then add that to the `systemtime_at_uptime_zero`.
6028
            // So value `1.340` is added to the wrapped `SystemTime` value.
6029
            // Then that is written into a buffer as seconds since UNIX_EPOCH.
6030
            // Later, in `datetime_parse_from_str`, this buffer is converted to
6031
            // a `DateTime` value.
6032

6033
            // copy the `uptime` capture group to a temporary local buffer
6034
            let mut at_uptime = 0;
18✔
6035
            const BUFLEN: usize = 30;
6036
            let mut buf_uptime: [u8; BUFLEN] = [0; BUFLEN];
18✔
6037
            copy_capturegroup_to_buffer!(CGN_UPTIME, captures, buf_uptime, at_uptime);
18✔
6038
            defo!("buf_uptime {:?}", buffer_to_String_noraw(&buf_uptime));
18✔
6039
            let buf_uptime_s: &str = match std::str::from_utf8(&buf_uptime[..at_uptime]) {
18✔
6040
                Ok(s) => s,
18✔
6041
                Err(_err) => {
×
6042
                    de_err!("uptime str::from_utf8 error: {}", _err);
×
6043
                    // fallback to zero
6044
                    "0"
×
6045
                }
6046
            };
6047
            defo!("buf_uptime_s {:?}", buf_uptime_s);
18✔
6048
            // extract the uptime string to an `Uptime` value
6049
            let uptime_val: Uptime = match buf_uptime_s.parse::<Uptime>() {
18✔
6050
                Ok(uptime_) => uptime_,
18✔
6051
                Err(_err) => {
×
6052
                    de_err!("uptime parse error: {}", _err);
×
6053
                    // fallback to zero
6054
                    0
×
6055
                }
6056
            };
6057
            defo!("uptime_val {:?}", uptime_val);
18✔
6058

6059
            let uptime_zero: SystemTime = match systemtime_at_uptime_zero {
18✔
6060
                Some(val) => *val,
12✔
6061
                None => UPTIME_DEFAULT_OFFSET,
6✔
6062
            };
6063
            defo!("uptime_zero {:?}", uptime_zero);
18✔
6064

6065
            // convert the uptime value to a `std::time::Duration`
6066
            let uptime_dur: StdDuration = StdDuration::new(uptime_val as u64, 0);
18✔
6067
            defo!("uptime_dur {:?}", uptime_dur);
18✔
6068
            // add the uptime value to the uptime_zero value
6069
            let uptime_zero_plus_uptime: SystemTime = match uptime_zero.checked_add(uptime_dur) {
18✔
6070
                Some(st) => st,
18✔
6071
                None => {
6072
                    debug_panic!("failed checked_add({:?})", uptime_dur);
×
6073
                    // I'm not sure what else to do here in a release build
6074

6075
                    UPTIME_DEFAULT_OFFSET
×
6076
                },
6077
            };
6078
            defo!("uptime_zero_plus_uptime {:?}", uptime_zero_plus_uptime);
18✔
6079
            // convert the `uptime_zero_plus_uptime` value to a string to a [u8]
6080
            buf_uptime.fill(0);
18✔
6081
            let uptime_zero_plus_uptime_dur = match uptime_zero_plus_uptime.duration_since(SystemTime::UNIX_EPOCH) {
18✔
6082
                Ok(dur) => dur,
18✔
6083
                Err(_err) => {
×
6084
                    debug_panic!("uptime_zero_plus_uptime.duration_since(UPTIME_DEFAULT_OFFSET) failed: {}", _err);
×
6085
                    // fallback to zero
6086
                    StdDuration::from_secs(0)
×
6087
                }
6088
            };
6089
            let uptime_zero_plus_uptime_n = uptime_zero_plus_uptime_dur.as_secs();
18✔
6090
            defo!("uptime_zero_plus_uptime_n {:?}", uptime_zero_plus_uptime_n);
18✔
6091
            let buf_uptime_plus = uptime_zero_plus_uptime_n.numtoa(10, &mut buf_uptime);
18✔
6092
            defo!("buf_uptime_plus {:?}", buffer_to_String_noraw(buf_uptime_plus));
18✔
6093
            // copy the local temporary buffer to the main buffer
6094
            copy_slice_to_buffer!(&buf_uptime_plus, buffer, at);
18✔
6095
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
18✔
6096
        }
6097
        DTFS_Uptime::_none => {}
25,866✔
6098
    }
6099

6100
    // year
6101
    defo!("process <year> {:?}…", dtfs.year);
25,884✔
6102
    match dtfs.year {
25,884✔
6103
        DTFS_Year::Y
6104
        | DTFS_Year::y => {
6105
            copy_capturegroup_to_buffer!(CGN_YEAR, captures, buffer, at);
24,873✔
6106
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
24,873✔
6107
        }
6108
        | DTFS_Year::_fill => {
6109
            match captures
165✔
6110
                .name(CGN_YEAR)
165✔
6111
                .as_ref()
165✔
6112
            {
6113
                Some(match_) => {
×
6114
                    copy_slice_to_buffer!(match_.as_bytes(), buffer, at);
×
6115
                    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
×
6116
                }
6117
                None => {
6118
                    match year_opt {
165✔
6119
                        Some(year) => {
93✔
6120
                            // TODO: 2022/07/11 cost-savings: pass in `Option<&[u8]>`, avoid creating `String`
6121
                            let year_s: String = year.to_string();
93✔
6122
                            debug_assert_eq!(year_s.len(), 4, "Bad year string {:?}", year_s);
93✔
6123
                            defo!("using fallback year {:?}", year_s);
93✔
6124
                            copy_slice_to_buffer!(year_s.as_bytes(), buffer, at);
93✔
6125
                            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
93✔
6126
                        }
6127
                        None => {
6128
                            defo!("using hardcoded dummy year {:?}", YEAR_FALLBACKDUMMY);
72✔
6129
                            copy_slice_to_buffer!(YEAR_FALLBACKDUMMY.as_bytes(), buffer, at);
72✔
6130
                            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
72✔
6131
                        }
6132
                    }
6133
                }
6134
            }
6135
        }
6136
        DTFS_Year::_none => {}
846✔
6137
    }
6138
    // month
6139
    defo!("process <month> {:?}…", dtfs.month);
25,884✔
6140
    match dtfs.month {
25,884✔
6141
        DTFS_Month::m => {
6142
            copy_capturegroup_to_buffer!(CGN_MONTH, captures, buffer, at);
23,073✔
6143
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
23,073✔
6144
        }
6145
        DTFS_Month::ms => {
6146
            let month = captures.name(CGN_MONTH).as_ref().unwrap().as_bytes();
39✔
6147
            // chrono strftime expects numeric months to be two-digit
6148
            // so prepend `0` if necessary
6149
            match month.len() {
39✔
6150
                1 => {
6151
                    copy_slice_to_buffer!(b"0", buffer, at);
30✔
6152
                    copy_slice_to_buffer!(month, buffer, at);
30✔
6153
                }
6154
                _val => {
9✔
6155
                    debug_assert_eq!(_val, 2, "unexpected Month length {}", _val);
9✔
6156
                    copy_slice_to_buffer!(month, buffer, at);
9✔
6157
                }
6158
            }
6159
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
39✔
6160
        }
6161
        DTFS_Month::b | DTFS_Month::B => {
6162
            month_bB_to_month_m_bytes(
1,926✔
6163
                captures
1,926✔
6164
                    .name(CGN_MONTH)
1,926✔
6165
                    .as_ref()
1,926✔
6166
                    .unwrap()
1,926✔
6167
                    .as_bytes(),
1,926✔
6168
                &mut buffer[at..at + 2],
1,926✔
6169
            );
6170
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
1,926✔
6171
            at += 2;
1,926✔
6172
        }
6173
        DTFS_Month::_none => {}
846✔
6174
    }
6175
    // day
6176
    defo!("process <day> {:?}…", dtfs.day);
25,884✔
6177
    match dtfs.day {
25,884✔
6178
        DTFS_Day::_e_or_d => {
6179
            let day: &[u8] = captures
25,038✔
6180
                .name(CGN_DAY)
25,038✔
6181
                .as_ref()
25,038✔
6182
                .unwrap()
25,038✔
6183
                .as_bytes();
25,038✔
6184
            debug_assert_ge!(day.len(), 1, "bad named group 'day' data {:?}, expected data ge 1", day);
25,038✔
6185
            debug_assert_le!(day.len(), 2, "bad named group 'day' data {:?}, expected data le 2", day);
25,038✔
6186
            match day.len() {
25,038✔
6187
                1 => {
6188
                    // change day "8" (%e) to "08" (%d)
6189
                    copy_u8_to_buffer!(b'0', buffer, at);
405✔
6190
                    copy_u8_to_buffer!(day[0], buffer, at);
405✔
6191
                    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
405✔
6192
                }
6193
                2 => {
6194
                    match day[0] {
24,633✔
6195
                        b' ' => {
6196
                            // change day " 8" (%e) to "08" (%d)
6197
                            copy_u8_to_buffer!(b'0', buffer, at);
30✔
6198
                            copy_u8_to_buffer!(day[1], buffer, at);
30✔
6199
                        }
6200
                        _ => {
6201
                            copy_slice_to_buffer!(day, buffer, at);
24,603✔
6202
                        }
6203
                    }
6204
                    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
24,633✔
6205
                }
6206
                _ => {
6207
                    panic!("bad day.len() {}", day.len());
×
6208
                }
6209
            }
6210
        }
6211
        DTFS_Day::_none => {}
846✔
6212
    }
6213
    // Day pattern `%a` (`Monday`, 'Tue`, etc.) (capture group `CGN_DAYa`) is captured but not
6214
    // passed along to chrono functions.
6215

6216
    // day-time divider
6217
    defo!("process date-time divider…");
25,884✔
6218
    copy_u8_to_buffer!(b'T', buffer, at);
25,884✔
6219
    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
25,884✔
6220
    // hour
6221
    defo!("process <hour> {:?}…", dtfs.hour);
25,884✔
6222
    match dtfs.hour {
25,884✔
6223
        DTFS_Hour::I
6224
        | DTFS_Hour::l
6225
        | DTFS_Hour::H => {
6226
            copy_capturegroup_to_buffer!(CGN_HOUR, captures, buffer, at);
24,999✔
6227
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
24,999✔
6228
        }
6229
        DTFS_Hour::k => {
6230
            let hour: &[u8] = captures.name(CGN_HOUR).as_ref().unwrap().as_bytes();
39✔
6231
            // chrono strftime expects numeric hour to be two-digit
6232
            // so prepend `0` if necessary
6233
            match hour.len() {
39✔
6234
                1 => {
6235
                    copy_slice_to_buffer!(b"0", buffer, at);
27✔
6236
                    copy_slice_to_buffer!(hour, buffer, at);
27✔
6237
                }
6238
                _val => {
12✔
6239
                    debug_assert_eq!(_val, 2, "unexpected Month length {}", _val);
12✔
6240
                    copy_slice_to_buffer!(hour, buffer, at);
12✔
6241
                }
6242
            }
6243
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
39✔
6244
        }
6245
        DTFS_Hour::_none => {}
846✔
6246
    }
6247
    // minute
6248
    defo!("process <minute> {:?}…", dtfs.minute);
25,884✔
6249
    match dtfs.minute {
25,884✔
6250
        DTFS_Minute::M => {
6251
            copy_capturegroup_to_buffer!(CGN_MINUTE, captures, buffer, at);
25,038✔
6252
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
25,038✔
6253
        },
6254
        DTFS_Minute::_none => {}
846✔
6255
    }
6256
    // second
6257
    defo!("process <second> {:?}…", dtfs.second);
25,884✔
6258
    match dtfs.second {
25,884✔
6259
        DTFS_Second::S => {
6260
            copy_capturegroup_to_buffer!(CGN_SECOND, captures, buffer, at);
23,082✔
6261
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
23,082✔
6262
        }
6263
        DTFS_Second::_fill => {
6264
            copy_slice_to_buffer!(b"00", buffer, at);
×
6265
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
×
6266
        }
6267
        DTFS_Second::_none => {}
2,802✔
6268
    }
6269
    // fractional
6270
    defo!("process <fractional> {:?}…", dtfs.fractional);
25,884✔
6271
    match dtfs.fractional {
25,884✔
6272
        DTFS_Fractional::f => {
6273
            defo!("matched DTFS_Fractional::f");
1,411✔
6274
            copy_u8_to_buffer!(b'.', buffer, at);
1,411✔
6275
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
1,411✔
6276
            let fractional: &[u8] = match captures.name(CGN_FRACTIONAL).as_ref() {
1,411✔
6277
                Some(match_) => {
1,411✔
6278
                    match_.as_bytes()
1,411✔
6279
                }
6280
                None => {
6281
                    panic!("DTFSSet has set {:?} yet no fraction was captured", dtfs.fractional);
×
6282
                }
6283
            };
6284
            let len = fractional.len();
1,411✔
6285
            defo!("match len {:?}", len);
1,411✔
6286
            match len {
1,411✔
6287
                0 => {
6288
                    copy_slice_to_buffer!(fractional, buffer, at);
×
6289
                    copy_slice_to_buffer!(b"000000000", buffer, at);
×
6290
                }
6291
                1 => {
6292
                    copy_slice_to_buffer!(fractional, buffer, at);
15✔
6293
                    copy_slice_to_buffer!(b"00000000", buffer, at);
15✔
6294
                }
6295
                2 => {
6296
                    copy_slice_to_buffer!(fractional, buffer, at);
64✔
6297
                    copy_slice_to_buffer!(b"0000000", buffer, at);
64✔
6298
                }
6299
                3 => {
6300
                    copy_slice_to_buffer!(fractional, buffer, at);
210✔
6301
                    copy_slice_to_buffer!(b"000000", buffer, at);
210✔
6302
                }
6303
                4 => {
6304
                    copy_slice_to_buffer!(fractional, buffer, at);
6✔
6305
                    copy_slice_to_buffer!(b"00000", buffer, at);
6✔
6306
                }
6307
                5 => {
6308
                    copy_slice_to_buffer!(fractional, buffer, at);
×
6309
                    copy_slice_to_buffer!(b"0000", buffer, at);
×
6310
                }
6311
                6 => {
6312
                    copy_slice_to_buffer!(fractional, buffer, at);
936✔
6313
                    copy_slice_to_buffer!(b"000", buffer, at);
936✔
6314
                }
6315
                7 => {
6316
                    copy_slice_to_buffer!(fractional, buffer, at);
×
6317
                    copy_slice_to_buffer!(b"00", buffer, at);
×
6318
                }
6319
                8 => {
6320
                    copy_slice_to_buffer!(fractional, buffer, at);
×
6321
                    copy_slice_to_buffer!(b"0", buffer, at);
×
6322
                }
6323
                9 => {
6324
                    copy_slice_to_buffer!(fractional, buffer, at);
180✔
6325
                }
6326
                10 | 11 | 12 => {
6327
                    // fractional is too large; copy only left-most 9 chars
6328
                    copy_slice_to_buffer!(&fractional[..9], buffer, at);
×
6329
                    de_wrn!("fractional string {:?} is length {} bytes, only copying 9 bytes", fractional, len)
×
6330
                }
6331
                _ => {
6332
                    // something is wrong with this matched string; ignore it
6333
                    de_err!("unexpected fractional string match {:?} length {} bytes", fractional, len)
×
6334
                }
6335
            }
6336
            defo!("buffer {:?}", buffer_to_String_noraw(buffer));
1,411✔
6337
        }
6338
        DTFS_Fractional::_none => {}
24,473✔
6339
    }
6340

6341
    // tz
6342
    defo!("process <tz> {:?}…", dtfs.tz);
25,884✔
6343
    match dtfs.tz {
25,884✔
6344
        DTFS_Tz::_fill => {
6345
            copy_slice_to_buffer!(tz_offset_string.as_bytes(), buffer, at);
22,889✔
6346
        }
6347
        DTFS_Tz::z | DTFS_Tz::zc | DTFS_Tz::zp => {
6348
            // for data passed to chrono `DateTime::parse_from_str`,
6349
            // replace Unicode "minus sign" to ASCII "hyphen-minus"
6350
            // see Issue https://github.com/chronotope/chrono/issues/835
6351
            // XXX: chrono 0.4.27 handles MINUS SIGN (U+2212)
6352
            //      see PR https://github.com/chronotope/chrono/pull/1087
6353
            //      however, keep this code here as it works fine
6354
            let captureb = captures
1,503✔
6355
                .name(CGN_TZ)
1,503✔
6356
                .as_ref()
1,503✔
6357
                .unwrap()
1,503✔
6358
                .as_bytes();
1,503✔
6359
            match captureb.starts_with(MINUS_SIGN) {
1,503✔
6360
                true => {
6361
                    defo!("found Unicode 'minus sign', transform to ASCII 'hyphen-minus'");
45✔
6362
                    // found Unicode "minus sign", replace with ASCII
6363
                    // "hyphen-minus"
6364
                    copy_slice_to_buffer!(HYPHEN_MINUS, buffer, at);
45✔
6365
                    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
45✔
6366
                    // copy data remaining after Unicode "minus sign"
6367
                    match std::str::from_utf8(captureb) {
45✔
6368
                        Ok(val) => {
45✔
6369
                            match val.char_indices().nth(1) {
45✔
6370
                                Some((offset, _)) => {
45✔
6371
                                    copy_slice_to_buffer!(val[offset..].as_bytes(), buffer, at);
45✔
6372
                                    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
45✔
6373
                                }
6374
                                None => {
×
6375
                                    // something is wrong with captured value
×
6376
                                    // ignore it
×
6377
                                }
×
6378
                            }
6379
                        }
6380
                        Err(_err) => {
×
6381
                            // something is wrong with captured value, ignore it
×
6382
                        }
×
6383
                    }
6384
                }
6385
                false => {
6386
                    copy_slice_to_buffer!(captureb, buffer, at);
1,458✔
6387
                    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
1,458✔
6388
                }
6389
            }
6390
        }
6391
        DTFS_Tz::Z => {
6392
            #[allow(non_snake_case)]
6393
            let tzZ: &str = u8_to_str(
646✔
6394
                captures
646✔
6395
                    .name(CGN_TZ)
646✔
6396
                    .as_ref()
646✔
6397
                    .unwrap()
646✔
6398
                    .as_bytes()
646✔
6399
                ).unwrap_or_default();
646✔
6400
            if tzZ.is_empty() {
646✔
6401
                debug_panic!("tzZ.is_empty()");
×
6402
                // `u8_to_str` failed, fallback to using passed TZ offset
6403
                copy_slice_to_buffer!(tz_offset_string.as_bytes(), buffer, at);
×
6404
            } else {
6405
                match MAP_TZZ_TO_TZz.get_entry(tzZ) {
646✔
6406
                    Some((_tz_abbr, tz_offset_val)) => {
646✔
6407
                        match tz_offset_val.is_empty() {
646✔
6408
                            true => {
6409
                                // given an ambiguous timezone name, fallback to
6410
                                // passed TZ offset
6411
                                copy_slice_to_buffer!(tz_offset_string.as_bytes(), buffer, at);
×
6412
                            }
6413
                            false => {
6414
                                // given an unambiguous timezone name, use associated offset
6415
                                copy_slice_to_buffer!(tz_offset_val.as_bytes(), buffer, at);
646✔
6416
                            }
6417
                        }
6418
                    }
6419
                    None => {
6420
                        // cannot find entry in MAP_TZZ_TO_TZz, use passed TZ offset
6421
                        debug_panic!("captured named timezone {:?} not found in MAP_TZZ_TO_TZz", tzZ);
×
6422
                        copy_slice_to_buffer!(tz_offset_string.as_bytes(), buffer, at);
×
6423
                    }
6424
                }
6425
            }
6426
        }
6427
        DTFS_Tz::_none => {}
846✔
6428
    }
6429
    defo!("buffer {:?}", buffer_to_String_noraw(buffer));
25,884✔
6430

6431
    defx!("return {:?}", at);
25,884✔
6432

6433
    at
25,884✔
6434
}
25,884✔
6435

6436
/// Run [`regex::Captures`] on the `data` then convert to a chrono
6437
/// [`Option<DateTime<FixedOffset>>`] instance. Uses matching and pattern
6438
/// information hardcoded in [`DATETIME_PARSE_DATAS`].
6439
///
6440
/// [`regex::Captures`]: https://docs.rs/regex/1.10.5/regex/bytes/struct.Regex.html#method.captures
6441
/// [`Option<DateTime<FixedOffset>>`]: https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html#impl-DateTime%3CFixedOffset%3E
6442
pub fn bytes_to_regex_to_datetime(
206,279✔
6443
    data: &[u8],
206,279✔
6444
    index: &DateTimeParseInstrsIndex,
206,279✔
6445
    year_opt: &Option<Year>,
206,279✔
6446
    systemtime_at_uptime_zero: &Option<SystemTime>,
206,279✔
6447
    tz_offset: &FixedOffset,
206,279✔
6448
    tz_offset_string: &String,
206,279✔
6449
    #[cfg(any(debug_assertions, test))]
206,279✔
6450
    path: &FPath,
206,279✔
6451
) -> Option<CapturedDtData> {
206,279✔
6452
    defn!("(…, {:?}, {:?}, {:?}, {:?})", index, year_opt, tz_offset, tz_offset_string);
206,279✔
6453

6454
    // get the OnceCell
6455
    let entry: &OnceCell<DateTimeRegex> = &DATETIME_PARSE_DATAS_REGEX_VEC[*index];
206,279✔
6456
    // compile the regex if it hasn't been compiled yet
6457
    // TRACKING: use OnceLock::get_or_try_init https://github.com/rust-lang/rust/issues/109737
6458
    //           would also need to replace use of `OnceCell` with `OnceLock`, should be a drop-in
6459
    //           replacement
6460
    let result_init: Result<&Regex, std::io::Error> = entry.get_or_try_init(||
206,279✔
6461
        {
1,925✔
6462
            let regex_pattern = DATETIME_PARSE_DATAS[*index].regex_pattern;
1,925✔
6463
            // update the global counter
6464
            let mut _count: usize = 0;
1,925✔
6465
            match DateTimeParseDatasCompiledCount.write() {
1,925✔
6466
                Ok(mut w) => {
1,925✔
6467
                    *w += 1;
1,925✔
6468
                    _count = *w;
1,925✔
6469
                }
1,925✔
6470
                Err(_err) => {
×
6471
                    de_err!("Failed to write DateTimeParseDatasCompiledCount {:?}", _err);
×
6472
                }
×
6473
            };
6474
            // create the regex
6475
            #[cfg(any(debug_assertions, test))]
6476
            {
6477
                let tcurrent = thread::current();
1,925✔
6478
                let tname = tcurrent.name().unwrap();
1,925✔
6479
                defo!(
1,925✔
6480
                    "init RegEx {} from line {:?} (thread {}) ({:?})",
6481
                    _count, DATETIME_PARSE_DATAS[*index]._line_num, tname, path
1,925✔
6482
                );
6483
            }
6484
            let regex_: DateTimeRegex = DateTimeRegex::new(regex_pattern).unwrap();
1,925✔
6485

6486
            Ok(regex_)
1,925✔
6487
        }
1,925✔
6488
    );
6489
    // get the regex reference
6490
    let regex_: &DateTimeRegex = match result_init {
206,279✔
6491
        Ok(val) => val,
206,279✔
6492
        Err(_err) => {
×
6493
            debug_panic!("Failed get_or_try_init {:?}", _err);
×
6494
            return None;
×
6495
        }
6496
    };
6497

6498
    // The regular expression matching call. According to `tools/flamegraph.sh`
6499
    // this is a very expensive function call.
6500
    let captures: regex::bytes::Captures = match regex_.captures(data) {
206,279✔
6501
        None => {
6502
            defx!(
180,395✔
6503
                "regex: no captures (returned None) for DTPD! at index {}, line {}",
6504
                index, DATETIME_PARSE_DATAS[*index]._line_num,
180,395✔
6505
            );
6506
            return None;
180,395✔
6507
        }
6508
        Some(captures) => {
25,884✔
6509
            deo!(
25,884✔
6510
                "regex: captured using DTPD! at index {}, line {}",
6511
                 index, DATETIME_PARSE_DATAS[*index]._line_num,
25,884✔
6512
            );
6513
            deo!("regex captures: len {}", captures.len());
25,884✔
6514

6515
            captures
25,884✔
6516
        }
6517
    };
6518
    if cfg!(debug_assertions) {
25,884✔
6519
        for (i, name_opt) in regex_
204,652✔
6520
            .capture_names()
25,884✔
6521
            .enumerate()
25,884✔
6522
        {
6523
            let _match: regex::bytes::Match = match captures.get(i) {
204,652✔
6524
                Some(m_) => m_,
204,652✔
6525
                None => {
6526
                    match name_opt {
×
6527
                        Some(_name) => {
×
6528
                            deo!("regex captures: {:2} {:<10} None", i, _name);
×
6529
                        }
×
6530
                        None => {
×
6531
                            deo!("regex captures: {:2} {:<10} None", i, "None");
×
6532
                        }
×
6533
                    }
6534
                    continue;
×
6535
                }
6536
            };
6537
            match name_opt {
204,652✔
6538
                Some(_name) => {
153,849✔
6539
                    deo!(
153,849✔
6540
                        "regex captures: {:2} {:<10} {:?}",
153,849✔
6541
                        i,
153,849✔
6542
                        _name,
153,849✔
6543
                        buffer_to_String_noraw(_match.as_bytes())
153,849✔
6544
                    );
153,849✔
6545
                }
153,849✔
6546
                None => {
50,803✔
6547
                    deo!(
50,803✔
6548
                        "regex captures: {:2} {:<10} {:?}",
50,803✔
6549
                        i,
50,803✔
6550
                        "NO NAME",
50,803✔
6551
                        buffer_to_String_noraw(_match.as_bytes())
50,803✔
6552
                    );
50,803✔
6553
                }
50,803✔
6554
            }
6555
        }
6556
    }
×
6557
    // sanity check
6558
    debug_assert!(
25,884✔
6559
        !captures
25,884✔
6560
            .iter()
25,884✔
6561
            .any(|x| x.is_none()),
204,652✔
6562
        "a match in the regex::Captures was None"
6563
    );
6564

6565
    let dtpd: &DateTimeParseInstr = &DATETIME_PARSE_DATAS[*index];
25,884✔
6566
    // copy regex matches into a buffer with predictable ordering
6567
    // this ordering relates to datetime format strings in `DATETIME_PARSE_DATAS`
6568
    const BUFLEN: usize = 35;
6569
    let mut buffer: [u8; BUFLEN] = [0; BUFLEN];
25,884✔
6570
    let copiedn = captures_to_buffer_bytes(
25,884✔
6571
        &mut buffer,
25,884✔
6572
        &captures,
25,884✔
6573
        year_opt,
25,884✔
6574
        systemtime_at_uptime_zero,
25,884✔
6575
        tz_offset_string,
25,884✔
6576
        &dtpd.dtfs
25,884✔
6577
    );
6578

6579
    // use the `dt_format` to parse the buffer of regex matches
6580
    let buffer_s: &str = u8_to_str(&buffer[0..copiedn]).unwrap();
25,884✔
6581
    let dt = match datetime_parse_from_str(
25,884✔
6582
        buffer_s,
25,884✔
6583
        dtpd.dtfs.pattern,
25,884✔
6584
        dtpd.dtfs.has_tz(),
25,884✔
6585
        tz_offset,
25,884✔
6586
    ) {
25,884✔
6587
        Some(dt_) => dt_,
25,834✔
6588
        None => {
6589
            defx!("return None; datetime_parse_from_str returned None");
50✔
6590
            return None;
50✔
6591
        }
6592
    };
6593

6594
    // derive the `LineIndex` bounds of the datetime substring within `data`
6595
    let dt_beg: LineIndex = match captures.name(dtpd.cgn_first) {
25,834✔
6596
        Some(match_) => match_.start() as LineIndex,
25,834✔
6597
        None => 0,
×
6598
    };
6599
    let dt_end: LineIndex = match captures.name(dtpd.cgn_last) {
25,834✔
6600
        Some(match_) => match_.end() as LineIndex,
25,834✔
6601
        None => 0,
×
6602
    };
6603
    debug_assert_lt!(dt_beg, dt_end, "bad dt_beg {} dt_end {}, index {}", dt_beg, dt_end, index);
25,834✔
6604

6605
    defx!("return Some({:?}, {:?}, {:?})", dt_beg, dt_end, dt);
25,834✔
6606
    Some((dt_beg, dt_end, dt))
25,834✔
6607
}
206,279✔
6608

6609
// --------------------
6610
// DateTime comparisons
6611

6612
/// Describe the result of comparing one [`DateTimeL`] to one DateTime Filter.
6613
#[allow(non_camel_case_types)]
6614
#[derive(Debug, Eq, PartialEq)]
6615
pub enum Result_Filter_DateTime1 {
6616
    /// like Skip
6617
    Pass,
6618
    /// the `DateTimeL` instance occurs at or after the datetime filter
6619
    OccursAtOrAfter,
6620
    /// the `DateTimeL` instance occurs before the datetime filter
6621
    OccursBefore,
6622
}
6623

6624
impl Result_Filter_DateTime1 {
6625
    /// Returns `true` if the result is `OccursAfter`.
6626
    #[inline(always)]
6627
    pub const fn is_after(&self) -> bool {
×
6628
        matches!(*self, Result_Filter_DateTime1::OccursAtOrAfter)
×
6629
    }
×
6630

6631
    /// Returns `true` if the result is `OccursBefore`.
6632
    #[inline(always)]
6633
    pub const fn is_before(&self) -> bool {
×
6634
        matches!(*self, Result_Filter_DateTime1::OccursBefore)
×
6635
    }
×
6636
}
6637

6638
/// Describe the result of comparing one [`DateTimeL`] to two DateTime Filters
6639
/// `(after, before)`.
6640
#[allow(non_camel_case_types)]
6641
#[derive(Debug, Eq, PartialEq)]
6642
pub enum Result_Filter_DateTime2 {
6643
    /// like Pass
6644
    InRange,
6645
    /// like Fail
6646
    BeforeRange,
6647
    /// like Fail
6648
    AfterRange,
6649
}
6650

6651
impl Result_Filter_DateTime2 {
6652
    #[inline(always)]
6653
    pub const fn is_pass(&self) -> bool {
×
6654
        matches!(*self, Result_Filter_DateTime2::InRange)
×
6655
    }
×
6656

6657
    #[inline(always)]
6658
    pub const fn is_fail(&self) -> bool {
×
6659
        matches!(*self, Result_Filter_DateTime2::AfterRange | Result_Filter_DateTime2::BeforeRange)
×
6660
    }
×
6661
}
6662

6663
/// Compare passed [`DateTimeL`] `dt` to the passed filter `dt_filter`.
6664
///
6665
/// If `dt` is at or after `dt_filter` then return [`OccursAtOrAfter`]<br/>
6666
/// If `dt` is before `dt_filter` then return [`OccursBefore`]<br/>
6667
/// Else return [`Pass`] (including if `dt_filter` is `None`)
6668
pub fn dt_after_or_before(
36,277✔
6669
    dt: &DateTimeL,
36,277✔
6670
    dt_filter: &DateTimeLOpt,
36,277✔
6671
) -> Result_Filter_DateTime1 {
36,277✔
6672
    if dt_filter.is_none() {
36,277✔
6673
        defñ!("return Pass; (no dt filters)");
4,988✔
6674
        return Result_Filter_DateTime1::Pass;
4,988✔
6675
    }
31,289✔
6676

6677
    let dt_a = &dt_filter.unwrap();
31,289✔
6678
    if dt < dt_a {
31,289✔
6679
        defñ!("return OccursBefore; (dt {:?} is before dt_filter {:?})", dt, dt_a);
15,234✔
6680
        return Result_Filter_DateTime1::OccursBefore;
15,234✔
6681
    }
16,055✔
6682
    defñ!("return OccursAtOrAfter; (dt {:?} is at or after dt_filter {:?})", dt, dt_a);
16,055✔
6683

6684
    Result_Filter_DateTime1::OccursAtOrAfter
16,055✔
6685
}
36,277✔
6686

6687
/// How does the passed `dt` pass the optional `DateTimeLOpt`
6688
/// filter instances, `dt_filter_after` and `dt_filter_before`?
6689
/// Is `dt` before ([`BeforeRange`]), after ([`AfterRange`]),
6690
/// or in between ([`InRange`])?
6691
///
6692
/// If both filters are `Some` and `dt: DateTimeL` is "between" the filters then
6693
/// return `InRange`.<br/>
6694
/// If before then return `BeforeRange`.<br/>
6695
/// If after then return `AfterRange`.
6696
///
6697
/// If filter `dt_filter_after` is `Some` and `dt: DateTimeL` is after that
6698
/// filter then return `InRange`.<br/>
6699
/// If before then return `BeforeRange`.
6700
///
6701
/// If filter `dt_filter_before` is `Some` and `dt: DateTimeL` is before that
6702
/// filter then return `InRange`.<br/>
6703
/// If after then return `AfterRange`.
6704
///
6705
/// If both filters are `None` then return `InRange`.
6706
///
6707
/// Comparisons are "inclusive" i.e. `dt` == `dt_filter_after` will return
6708
/// `InRange`.
6709
///
6710
/// [`AfterRange`]: crate::data::datetime::Result_Filter_DateTime2::AfterRange
6711
/// [`BeforeRange`]: crate::data::datetime::Result_Filter_DateTime2::BeforeRange
6712
/// [`InRange`]: crate::data::datetime::Result_Filter_DateTime2::InRange
6713
pub fn dt_pass_filters(
4,776✔
6714
    dt: &DateTimeL,
4,776✔
6715
    dt_filter_after: &DateTimeLOpt,
4,776✔
6716
    dt_filter_before: &DateTimeLOpt,
4,776✔
6717
) -> Result_Filter_DateTime2 {
4,776✔
6718
    defn!("({:?}, {:?}, {:?})", dt, dt_filter_after, dt_filter_before);
4,776✔
6719
    match (dt_filter_after, dt_filter_before) {
4,776✔
6720
        (None, None) => {
6721
            defx!("return InRange; (no dt filters)");
4,230✔
6722

6723
            Result_Filter_DateTime2::InRange
4,230✔
6724
        }
6725
        (Some(da), Some(db)) => {
448✔
6726
            debug_assert_le!(da, db, "Bad datetime range values filter_after {:?} {:?} filter_before", da, db);
448✔
6727
            if dt < da {
448✔
6728
                defx!("return BeforeRange");
3✔
6729
                return Result_Filter_DateTime2::BeforeRange;
3✔
6730
            }
445✔
6731
            if db < dt {
445✔
6732
                defx!("return AfterRange");
2✔
6733
                return Result_Filter_DateTime2::AfterRange;
2✔
6734
            }
443✔
6735
            debug_assert_le!(da, dt, "Unexpected range values da dt");
443✔
6736
            debug_assert_le!(dt, db, "Unexpected range values dt db");
443✔
6737
            defx!("return InRange");
443✔
6738

6739
            Result_Filter_DateTime2::InRange
443✔
6740
        }
6741
        (Some(da), None) => {
50✔
6742
            if dt < da {
50✔
6743
                defx!("return BeforeRange");
32✔
6744
                return Result_Filter_DateTime2::BeforeRange;
32✔
6745
            }
18✔
6746
            defx!("return InRange");
18✔
6747

6748
            Result_Filter_DateTime2::InRange
18✔
6749
        }
6750
        (None, Some(db)) => {
48✔
6751
            if db < dt {
48✔
6752
                defx!("return AfterRange");
15✔
6753
                return Result_Filter_DateTime2::AfterRange;
15✔
6754
            }
33✔
6755
            defx!("return InRange");
33✔
6756

6757
            Result_Filter_DateTime2::InRange
33✔
6758
        }
6759
    }
6760
}
4,776✔
6761

6762
// ---------------------------------------------
6763
// other miscellaneous DateTime function helpers
6764

6765
/// Create a new [`DateTimeL`] instance that uses the passed `DateTimeL`
6766
/// month, day, and time, combined with the passed `Year`.
6767
///
6768
/// In case of error, return a copy of the passed `DateTimeL`.
6769
pub fn datetime_with_year(
×
6770
    datetime: &DateTimeL,
×
6771
    year: &Year,
×
6772
) -> DateTimeL {
×
6773
    match datetime.with_year(*year) {
×
6774
        Some(datetime_) => datetime_,
×
6775
        None => *datetime,
×
6776
    }
6777
}
×
6778

6779
/// Convert passed [`SystemTime`] to [`DateTimeL`] with passed [`FixedOffset`].
6780
///
6781
/// [`FixedOffset`]: https://docs.rs/chrono/0.4.38/chrono/offset/struct.FixedOffset.html
6782
/// [`SystemTime`]: std::time::SystemTime
6783
pub fn systemtime_to_datetime(
425✔
6784
    fixedoffset: &FixedOffset,
425✔
6785
    systemtime: &SystemTime,
425✔
6786
) -> DateTimeL {
425✔
6787
    // https://users.rust-lang.org/t/convert-std-time-systemtime-to-chrono-datetime-datetime/7684/6
6788
    let dtu: DateTime<Utc> = (*systemtime).into();
425✔
6789

6790
    dtu.with_timezone(fixedoffset)
425✔
6791
}
425✔
6792

6793
/// Subtract a [`SystemTime`] from a [`DateTimeL`].
6794
pub fn datetime_minus_systemtime(
×
6795
    datetime: &DateTimeL,
×
6796
    systemtime: &SystemTime,
×
6797
) -> Duration {
×
6798
    *datetime - systemtime_to_datetime(
×
6799
        datetime.offset(),
×
6800
        systemtime,
×
6801
    )
×
6802
}
×
6803

6804
/// Convert passed seconds since Unix Epoch to a `SystemTime`.
6805
pub fn seconds_to_systemtime(
74✔
6806
    seconds: &u64,
74✔
6807
) -> SystemTime {
74✔
6808
    let duration = std::time::Duration::from_secs(*seconds);
74✔
6809

6810
    // TODO: [2024/06] handle `None`
6811
    // TODO: if `checked_add` becomes `const` then multiple other functions
6812
    //       could become `const`
6813
    SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()
74✔
6814
}
74✔
6815

6816
/// Return the year of the `systemtime`
6817
pub fn systemtime_year(
5✔
6818
    systemtime: &SystemTime,
5✔
6819
) -> Year {
5✔
6820
    let dtu: DateTime<Utc> = (*systemtime).into();
5✔
6821

6822
    dtu.year()
5✔
6823
}
5✔
6824

6825
// --------------------------------------------
6826
// search a slice quickly (loop unroll version)
6827
//
6828
// loop unrolled implementation of `slice.contains` for a byte slice and a hardcoded array
6829
// benchmark `benches/bench_slice_contains.rs` demonstrates this is faster
6830

6831
#[inline(always)]
6832
#[unroll_for_loops]
6833
const fn slice_contains_2_2(
9✔
6834
    slice_: &[u8; 2],
9✔
6835
    search: &[u8; 2],
9✔
6836
) -> bool {
9✔
6837
    for i in 0..2 {
6838
        if slice_[i] == search[0] || slice_[i] == search[1] {
4✔
6839
            return true;
3✔
6840
        }
1✔
6841
    }
6842
    false
1✔
6843
}
9✔
6844

6845
#[inline(always)]
6846
#[unroll_for_loops]
6847
const fn slice_contains_3_2(
7✔
6848
    slice_: &[u8; 3],
7✔
6849
    search: &[u8; 2],
7✔
6850
) -> bool {
7✔
6851
    for i in 0..3 {
6852
        if slice_[i] == search[0] || slice_[i] == search[1] {
3✔
6853
            return true;
2✔
6854
        }
1✔
6855
    }
6856
    false
1✔
6857
}
7✔
6858

6859
#[inline(always)]
6860
#[unroll_for_loops]
6861
const fn slice_contains_4_2(
10✔
6862
    slice_: &[u8; 4],
10✔
6863
    search: &[u8; 2],
10✔
6864
) -> bool {
10✔
6865
    for i in 0..4 {
6866
        if slice_[i] == search[0] || slice_[i] == search[1] {
4✔
6867
            return true;
3✔
6868
        }
1✔
6869
    }
6870
    false
1✔
6871
}
10✔
6872

6873
#[inline(always)]
6874
#[unroll_for_loops]
6875
const fn slice_contains_5_2(
11✔
6876
    slice_: &[u8; 5],
11✔
6877
    search: &[u8; 2],
11✔
6878
) -> bool {
11✔
6879
    for i in 0..5 {
6880
        if slice_[i] == search[0] || slice_[i] == search[1] {
3✔
6881
            return true;
2✔
6882
        }
1✔
6883
    }
6884
    false
1✔
6885
}
11✔
6886

6887
#[inline(always)]
6888
#[unroll_for_loops]
6889
const fn slice_contains_6_2(
13✔
6890
    slice_: &[u8; 6],
13✔
6891
    search: &[u8; 2],
13✔
6892
) -> bool {
13✔
6893
    for i in 0..6 {
6894
        if slice_[i] == search[0] || slice_[i] == search[1] {
3✔
6895
            return true;
2✔
6896
        }
1✔
6897
    }
6898
    false
1✔
6899
}
13✔
6900

6901
#[inline(always)]
6902
#[unroll_for_loops]
6903
const fn slice_contains_7_2(
×
6904
    slice_: &[u8; 7],
×
6905
    search: &[u8; 2],
×
6906
) -> bool {
×
6907
    for i in 0..7 {
6908
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6909
            return true;
×
6910
        }
×
6911
    }
6912
    false
×
6913
}
×
6914

6915
#[inline(always)]
6916
#[unroll_for_loops]
6917
const fn slice_contains_8_2(
×
6918
    slice_: &[u8; 8],
×
6919
    search: &[u8; 2],
×
6920
) -> bool {
×
6921
    for i in 0..8 {
6922
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6923
            return true;
×
6924
        }
×
6925
    }
6926
    false
×
6927
}
×
6928

6929
#[inline(always)]
6930
#[unroll_for_loops]
6931
const fn slice_contains_9_2(
×
6932
    slice_: &[u8; 9],
×
6933
    search: &[u8; 2],
×
6934
) -> bool {
×
6935
    for i in 0..9 {
6936
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6937
            return true;
×
6938
        }
×
6939
    }
6940
    false
×
6941
}
×
6942

6943
#[inline(always)]
6944
#[unroll_for_loops]
6945
const fn slice_contains_10_2(
×
6946
    slice_: &[u8; 10],
×
6947
    search: &[u8; 2],
×
6948
) -> bool {
×
6949
    for i in 0..10 {
6950
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6951
            return true;
×
6952
        }
×
6953
    }
6954
    false
×
6955
}
×
6956

6957
#[inline(always)]
6958
#[unroll_for_loops]
6959
const fn slice_contains_11_2(
×
6960
    slice_: &[u8; 11],
×
6961
    search: &[u8; 2],
×
6962
) -> bool {
×
6963
    for i in 0..11 {
6964
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6965
            return true;
×
6966
        }
×
6967
    }
6968
    false
×
6969
}
×
6970

6971
#[inline(always)]
6972
#[unroll_for_loops]
6973
const fn slice_contains_12_2(
×
6974
    slice_: &[u8; 12],
×
6975
    search: &[u8; 2],
×
6976
) -> bool {
×
6977
    for i in 0..12 {
6978
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6979
            return true;
×
6980
        }
×
6981
    }
6982
    false
×
6983
}
×
6984

6985
#[inline(always)]
6986
#[unroll_for_loops]
6987
const fn slice_contains_13_2(
×
6988
    slice_: &[u8; 13],
×
6989
    search: &[u8; 2],
×
6990
) -> bool {
×
6991
    for i in 0..13 {
6992
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
6993
            return true;
×
6994
        }
×
6995
    }
6996
    false
×
6997
}
×
6998

6999
#[inline(always)]
7000
#[unroll_for_loops]
7001
const fn slice_contains_14_2(
×
7002
    slice_: &[u8; 14],
×
7003
    search: &[u8; 2],
×
7004
) -> bool {
×
7005
    for i in 0..14 {
7006
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7007
            return true;
×
7008
        }
×
7009
    }
7010
    false
×
7011
}
×
7012

7013
#[inline(always)]
7014
#[unroll_for_loops]
7015
const fn slice_contains_15_2(
×
7016
    slice_: &[u8; 15],
×
7017
    search: &[u8; 2],
×
7018
) -> bool {
×
7019
    for i in 0..15 {
7020
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7021
            return true;
×
7022
        }
×
7023
    }
7024
    false
×
7025
}
×
7026

7027
#[inline(always)]
7028
#[unroll_for_loops]
7029
const fn slice_contains_16_2(
×
7030
    slice_: &[u8; 16],
×
7031
    search: &[u8; 2],
×
7032
) -> bool {
×
7033
    for i in 0..16 {
7034
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7035
            return true;
×
7036
        }
×
7037
    }
7038
    false
×
7039
}
×
7040

7041
#[inline(always)]
7042
#[unroll_for_loops]
7043
const fn slice_contains_17_2(
×
7044
    slice_: &[u8; 17],
×
7045
    search: &[u8; 2],
×
7046
) -> bool {
×
7047
    for i in 0..17 {
7048
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7049
            return true;
×
7050
        }
×
7051
    }
7052
    false
×
7053
}
×
7054

7055
#[inline(always)]
7056
#[unroll_for_loops]
7057
const fn slice_contains_18_2(
×
7058
    slice_: &[u8; 18],
×
7059
    search: &[u8; 2],
×
7060
) -> bool {
×
7061
    for i in 0..18 {
7062
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7063
            return true;
×
7064
        }
×
7065
    }
7066
    false
×
7067
}
×
7068

7069
#[inline(always)]
7070
#[unroll_for_loops]
7071
const fn slice_contains_19_2(
×
7072
    slice_: &[u8; 19],
×
7073
    search: &[u8; 2],
×
7074
) -> bool {
×
7075
    for i in 0..19 {
7076
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7077
            return true;
×
7078
        }
×
7079
    }
7080
    false
×
7081
}
×
7082

7083
#[inline(always)]
7084
#[unroll_for_loops]
7085
const fn slice_contains_20_2(
×
7086
    slice_: &[u8; 20],
×
7087
    search: &[u8; 2],
×
7088
) -> bool {
×
7089
    for i in 0..20 {
7090
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7091
            return true;
×
7092
        }
×
7093
    }
7094
    false
×
7095
}
×
7096

7097
#[inline(always)]
7098
#[unroll_for_loops]
7099
const fn slice_contains_21_2(
×
7100
    slice_: &[u8; 21],
×
7101
    search: &[u8; 2],
×
7102
) -> bool {
×
7103
    for i in 0..21 {
7104
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7105
            return true;
×
7106
        }
×
7107
    }
7108
    false
×
7109
}
×
7110

7111
#[inline(always)]
7112
#[unroll_for_loops]
7113
const fn slice_contains_22_2(
×
7114
    slice_: &[u8; 22],
×
7115
    search: &[u8; 2],
×
7116
) -> bool {
×
7117
    for i in 0..22 {
7118
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7119
            return true;
×
7120
        }
×
7121
    }
7122
    false
×
7123
}
×
7124

7125
#[inline(always)]
7126
#[unroll_for_loops]
7127
const fn slice_contains_23_2(
×
7128
    slice_: &[u8; 23],
×
7129
    search: &[u8; 2],
×
7130
) -> bool {
×
7131
    for i in 0..23 {
7132
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7133
            return true;
×
7134
        }
×
7135
    }
7136
    false
×
7137
}
×
7138

7139
#[inline(always)]
7140
#[unroll_for_loops]
7141
const fn slice_contains_24_2(
×
7142
    slice_: &[u8; 24],
×
7143
    search: &[u8; 2],
×
7144
) -> bool {
×
7145
    for i in 0..24 {
7146
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7147
            return true;
×
7148
        }
×
7149
    }
7150
    false
×
7151
}
×
7152

7153
#[inline(always)]
7154
#[unroll_for_loops]
7155
const fn slice_contains_25_2(
×
7156
    slice_: &[u8; 25],
×
7157
    search: &[u8; 2],
×
7158
) -> bool {
×
7159
    for i in 0..25 {
7160
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7161
            return true;
×
7162
        }
×
7163
    }
7164
    false
×
7165
}
×
7166

7167
#[inline(always)]
7168
#[unroll_for_loops]
7169
const fn slice_contains_26_2(
×
7170
    slice_: &[u8; 26],
×
7171
    search: &[u8; 2],
×
7172
) -> bool {
×
7173
    for i in 0..26 {
7174
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7175
            return true;
×
7176
        }
×
7177
    }
7178
    false
×
7179
}
×
7180

7181
#[inline(always)]
7182
#[unroll_for_loops]
7183
const fn slice_contains_27_2(
×
7184
    slice_: &[u8; 27],
×
7185
    search: &[u8; 2],
×
7186
) -> bool {
×
7187
    for i in 0..27 {
7188
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7189
            return true;
×
7190
        }
×
7191
    }
7192
    false
×
7193
}
×
7194

7195
#[inline(always)]
7196
#[unroll_for_loops]
7197
const fn slice_contains_28_2(
×
7198
    slice_: &[u8; 28],
×
7199
    search: &[u8; 2],
×
7200
) -> bool {
×
7201
    for i in 0..28 {
7202
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7203
            return true;
×
7204
        }
×
7205
    }
7206
    false
×
7207
}
×
7208

7209
#[inline(always)]
7210
#[unroll_for_loops]
7211
const fn slice_contains_29_2(
×
7212
    slice_: &[u8; 29],
×
7213
    search: &[u8; 2],
×
7214
) -> bool {
×
7215
    for i in 0..29 {
7216
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7217
            return true;
×
7218
        }
×
7219
    }
7220
    false
×
7221
}
×
7222

7223
#[inline(always)]
7224
#[unroll_for_loops]
7225
const fn slice_contains_30_2(
×
7226
    slice_: &[u8; 30],
×
7227
    search: &[u8; 2],
×
7228
) -> bool {
×
7229
    for i in 0..30 {
7230
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7231
            return true;
×
7232
        }
×
7233
    }
7234
    false
×
7235
}
×
7236

7237
#[inline(always)]
7238
#[unroll_for_loops]
7239
const fn slice_contains_31_2(
×
7240
    slice_: &[u8; 31],
×
7241
    search: &[u8; 2],
×
7242
) -> bool {
×
7243
    for i in 0..31 {
7244
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7245
            return true;
×
7246
        }
×
7247
    }
7248
    false
×
7249
}
×
7250

7251
#[inline(always)]
7252
#[unroll_for_loops]
7253
const fn slice_contains_32_2(
×
7254
    slice_: &[u8; 32],
×
7255
    search: &[u8; 2],
×
7256
) -> bool {
×
7257
    for i in 0..32 {
7258
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7259
            return true;
×
7260
        }
×
7261
    }
7262
    false
×
7263
}
×
7264

7265
#[inline(always)]
7266
#[unroll_for_loops]
7267
const fn slice_contains_33_2(
×
7268
    slice_: &[u8; 33],
×
7269
    search: &[u8; 2],
×
7270
) -> bool {
×
7271
    for i in 0..33 {
7272
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7273
            return true;
×
7274
        }
×
7275
    }
7276
    false
×
7277
}
×
7278

7279
#[inline(always)]
7280
#[unroll_for_loops]
7281
const fn slice_contains_34_2(
×
7282
    slice_: &[u8; 34],
×
7283
    search: &[u8; 2],
×
7284
) -> bool {
×
7285
    for i in 0..34 {
7286
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7287
            return true;
×
7288
        }
×
7289
    }
7290
    false
×
7291
}
×
7292

7293
#[inline(always)]
7294
#[unroll_for_loops]
7295
const fn slice_contains_35_2(
×
7296
    slice_: &[u8; 35],
×
7297
    search: &[u8; 2],
×
7298
) -> bool {
×
7299
    for i in 0..35 {
7300
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7301
            return true;
×
7302
        }
×
7303
    }
7304
    false
×
7305
}
×
7306

7307
#[inline(always)]
7308
#[unroll_for_loops]
7309
const fn slice_contains_36_2(
×
7310
    slice_: &[u8; 36],
×
7311
    search: &[u8; 2],
×
7312
) -> bool {
×
7313
    for i in 0..36 {
7314
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7315
            return true;
×
7316
        }
×
7317
    }
7318
    false
×
7319
}
×
7320

7321
#[inline(always)]
7322
#[unroll_for_loops]
7323
const fn slice_contains_37_2(
×
7324
    slice_: &[u8; 37],
×
7325
    search: &[u8; 2],
×
7326
) -> bool {
×
7327
    for i in 0..37 {
7328
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7329
            return true;
×
7330
        }
×
7331
    }
7332
    false
×
7333
}
×
7334

7335
#[inline(always)]
7336
#[unroll_for_loops]
7337
const fn slice_contains_38_2(
×
7338
    slice_: &[u8; 38],
×
7339
    search: &[u8; 2],
×
7340
) -> bool {
×
7341
    for i in 0..38 {
7342
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7343
            return true;
×
7344
        }
×
7345
    }
7346
    false
×
7347
}
×
7348

7349
#[inline(always)]
7350
#[unroll_for_loops]
7351
const fn slice_contains_39_2(
×
7352
    slice_: &[u8; 39],
×
7353
    search: &[u8; 2],
×
7354
) -> bool {
×
7355
    for i in 0..39 {
7356
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7357
            return true;
×
7358
        }
×
7359
    }
7360
    false
×
7361
}
×
7362

7363
#[inline(always)]
7364
#[unroll_for_loops]
7365
const fn slice_contains_40_2(
×
7366
    slice_: &[u8; 40],
×
7367
    search: &[u8; 2],
×
7368
) -> bool {
×
7369
    for i in 0..40 {
7370
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7371
            return true;
×
7372
        }
×
7373
    }
7374
    false
×
7375
}
×
7376

7377
#[inline(always)]
7378
#[unroll_for_loops]
7379
const fn slice_contains_41_2(
×
7380
    slice_: &[u8; 41],
×
7381
    search: &[u8; 2],
×
7382
) -> bool {
×
7383
    for i in 0..41 {
7384
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7385
            return true;
×
7386
        }
×
7387
    }
7388
    false
×
7389
}
×
7390

7391
#[inline(always)]
7392
#[unroll_for_loops]
7393
const fn slice_contains_42_2(
×
7394
    slice_: &[u8; 42],
×
7395
    search: &[u8; 2],
×
7396
) -> bool {
×
7397
    for i in 0..42 {
7398
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7399
            return true;
×
7400
        }
×
7401
    }
7402
    false
×
7403
}
×
7404

7405
#[inline(always)]
7406
#[unroll_for_loops]
7407
const fn slice_contains_43_2(
×
7408
    slice_: &[u8; 43],
×
7409
    search: &[u8; 2],
×
7410
) -> bool {
×
7411
    for i in 0..43 {
7412
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7413
            return true;
×
7414
        }
×
7415
    }
7416
    false
×
7417
}
×
7418

7419
#[inline(always)]
7420
#[unroll_for_loops]
7421
const fn slice_contains_44_2(
×
7422
    slice_: &[u8; 44],
×
7423
    search: &[u8; 2],
×
7424
) -> bool {
×
7425
    for i in 0..44 {
7426
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7427
            return true;
×
7428
        }
×
7429
    }
7430
    false
×
7431
}
×
7432

7433
#[inline(always)]
7434
#[unroll_for_loops]
7435
const fn slice_contains_45_2(
×
7436
    slice_: &[u8; 45],
×
7437
    search: &[u8; 2],
×
7438
) -> bool {
×
7439
    for i in 0..45 {
7440
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7441
            return true;
×
7442
        }
×
7443
    }
7444
    false
×
7445
}
×
7446

7447
#[inline(always)]
7448
#[unroll_for_loops]
7449
const fn slice_contains_46_2(
×
7450
    slice_: &[u8; 46],
×
7451
    search: &[u8; 2],
×
7452
) -> bool {
×
7453
    for i in 0..46 {
7454
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7455
            return true;
×
7456
        }
×
7457
    }
7458
    false
×
7459
}
×
7460

7461
#[inline(always)]
7462
#[unroll_for_loops]
7463
const fn slice_contains_47_2(
×
7464
    slice_: &[u8; 47],
×
7465
    search: &[u8; 2],
×
7466
) -> bool {
×
7467
    for i in 0..47 {
7468
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7469
            return true;
×
7470
        }
×
7471
    }
7472
    false
×
7473
}
×
7474

7475
#[inline(always)]
7476
#[unroll_for_loops]
7477
const fn slice_contains_48_2(
×
7478
    slice_: &[u8; 48],
×
7479
    search: &[u8; 2],
×
7480
) -> bool {
×
7481
    for i in 0..48 {
7482
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7483
            return true;
×
7484
        }
×
7485
    }
7486
    false
×
7487
}
×
7488

7489
#[inline(always)]
7490
#[unroll_for_loops]
7491
const fn slice_contains_49_2(
×
7492
    slice_: &[u8; 49],
×
7493
    search: &[u8; 2],
×
7494
) -> bool {
×
7495
    for i in 0..49 {
7496
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7497
            return true;
×
7498
        }
×
7499
    }
7500
    false
×
7501
}
×
7502

7503
#[inline(always)]
7504
#[unroll_for_loops]
7505
const fn slice_contains_50_2(
×
7506
    slice_: &[u8; 50],
×
7507
    search: &[u8; 2],
×
7508
) -> bool {
×
7509
    for i in 0..50 {
7510
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7511
            return true;
×
7512
        }
×
7513
    }
7514
    false
×
7515
}
×
7516

7517
#[inline(always)]
7518
#[unroll_for_loops]
7519
const fn slice_contains_51_2(
×
7520
    slice_: &[u8; 51],
×
7521
    search: &[u8; 2],
×
7522
) -> bool {
×
7523
    for i in 0..51 {
7524
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7525
            return true;
×
7526
        }
×
7527
    }
7528
    false
×
7529
}
×
7530

7531
#[inline(always)]
7532
#[unroll_for_loops]
7533
const fn slice_contains_52_2(
×
7534
    slice_: &[u8; 52],
×
7535
    search: &[u8; 2],
×
7536
) -> bool {
×
7537
    for i in 0..52 {
7538
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7539
            return true;
×
7540
        }
×
7541
    }
7542
    false
×
7543
}
×
7544

7545
#[inline(always)]
7546
#[unroll_for_loops]
7547
const fn slice_contains_53_2(
×
7548
    slice_: &[u8; 53],
×
7549
    search: &[u8; 2],
×
7550
) -> bool {
×
7551
    for i in 0..53 {
7552
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7553
            return true;
×
7554
        }
×
7555
    }
7556
    false
×
7557
}
×
7558

7559
#[inline(always)]
7560
#[unroll_for_loops]
7561
const fn slice_contains_54_2(
×
7562
    slice_: &[u8; 54],
×
7563
    search: &[u8; 2],
×
7564
) -> bool {
×
7565
    for i in 0..54 {
7566
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7567
            return true;
×
7568
        }
×
7569
    }
7570
    false
×
7571
}
×
7572

7573
#[inline(always)]
7574
#[unroll_for_loops]
7575
const fn slice_contains_55_2(
×
7576
    slice_: &[u8; 55],
×
7577
    search: &[u8; 2],
×
7578
) -> bool {
×
7579
    for i in 0..55 {
7580
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7581
            return true;
×
7582
        }
×
7583
    }
7584
    false
×
7585
}
×
7586

7587
#[inline(always)]
7588
#[unroll_for_loops]
7589
const fn slice_contains_56_2(
×
7590
    slice_: &[u8; 56],
×
7591
    search: &[u8; 2],
×
7592
) -> bool {
×
7593
    for i in 0..56 {
7594
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7595
            return true;
×
7596
        }
×
7597
    }
7598
    false
×
7599
}
×
7600

7601
#[inline(always)]
7602
#[unroll_for_loops]
7603
const fn slice_contains_57_2(
×
7604
    slice_: &[u8; 57],
×
7605
    search: &[u8; 2],
×
7606
) -> bool {
×
7607
    for i in 0..57 {
7608
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7609
            return true;
×
7610
        }
×
7611
    }
7612
    false
×
7613
}
×
7614

7615
#[inline(always)]
7616
#[unroll_for_loops]
7617
const fn slice_contains_58_2(
×
7618
    slice_: &[u8; 58],
×
7619
    search: &[u8; 2],
×
7620
) -> bool {
×
7621
    for i in 0..58 {
7622
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7623
            return true;
×
7624
        }
×
7625
    }
7626
    false
×
7627
}
×
7628

7629
#[inline(always)]
7630
#[unroll_for_loops]
7631
const fn slice_contains_59_2(
×
7632
    slice_: &[u8; 59],
×
7633
    search: &[u8; 2],
×
7634
) -> bool {
×
7635
    for i in 0..59 {
7636
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7637
            return true;
×
7638
        }
×
7639
    }
7640
    false
×
7641
}
×
7642

7643
#[inline(always)]
7644
#[unroll_for_loops]
7645
const fn slice_contains_60_2(
×
7646
    slice_: &[u8; 60],
×
7647
    search: &[u8; 2],
×
7648
) -> bool {
×
7649
    for i in 0..60 {
7650
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7651
            return true;
×
7652
        }
×
7653
    }
7654
    false
×
7655
}
×
7656

7657
#[inline(always)]
7658
#[unroll_for_loops]
7659
const fn slice_contains_61_2(
×
7660
    slice_: &[u8; 61],
×
7661
    search: &[u8; 2],
×
7662
) -> bool {
×
7663
    for i in 0..61 {
7664
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7665
            return true;
×
7666
        }
×
7667
    }
7668
    false
×
7669
}
×
7670

7671
#[inline(always)]
7672
#[unroll_for_loops]
7673
const fn slice_contains_62_2(
×
7674
    slice_: &[u8; 62],
×
7675
    search: &[u8; 2],
×
7676
) -> bool {
×
7677
    for i in 0..62 {
7678
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7679
            return true;
×
7680
        }
×
7681
    }
7682
    false
×
7683
}
×
7684

7685
#[inline(always)]
7686
#[unroll_for_loops]
7687
const fn slice_contains_63_2(
×
7688
    slice_: &[u8; 63],
×
7689
    search: &[u8; 2],
×
7690
) -> bool {
×
7691
    for i in 0..63 {
7692
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7693
            return true;
×
7694
        }
×
7695
    }
7696
    false
×
7697
}
×
7698

7699
#[inline(always)]
7700
#[unroll_for_loops]
7701
const fn slice_contains_64_2(
×
7702
    slice_: &[u8; 64],
×
7703
    search: &[u8; 2],
×
7704
) -> bool {
×
7705
    for i in 0..64 {
7706
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7707
            return true;
×
7708
        }
×
7709
    }
7710
    false
×
7711
}
×
7712

7713
#[inline(always)]
7714
#[unroll_for_loops]
7715
const fn slice_contains_65_2(
×
7716
    slice_: &[u8; 65],
×
7717
    search: &[u8; 2],
×
7718
) -> bool {
×
7719
    for i in 0..65 {
7720
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7721
            return true;
×
7722
        }
×
7723
    }
7724
    false
×
7725
}
×
7726

7727
#[inline(always)]
7728
#[unroll_for_loops]
7729
const fn slice_contains_66_2(
×
7730
    slice_: &[u8; 66],
×
7731
    search: &[u8; 2],
×
7732
) -> bool {
×
7733
    for i in 0..66 {
7734
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7735
            return true;
×
7736
        }
×
7737
    }
7738
    false
×
7739
}
×
7740

7741
#[inline(always)]
7742
#[unroll_for_loops]
7743
const fn slice_contains_67_2(
×
7744
    slice_: &[u8; 67],
×
7745
    search: &[u8; 2],
×
7746
) -> bool {
×
7747
    for i in 0..67 {
7748
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7749
            return true;
×
7750
        }
×
7751
    }
7752
    false
×
7753
}
×
7754

7755
#[inline(always)]
7756
#[unroll_for_loops]
7757
const fn slice_contains_68_2(
×
7758
    slice_: &[u8; 68],
×
7759
    search: &[u8; 2],
×
7760
) -> bool {
×
7761
    for i in 0..68 {
7762
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7763
            return true;
×
7764
        }
×
7765
    }
7766
    false
×
7767
}
×
7768

7769
#[inline(always)]
7770
#[unroll_for_loops]
7771
const fn slice_contains_69_2(
×
7772
    slice_: &[u8; 69],
×
7773
    search: &[u8; 2],
×
7774
) -> bool {
×
7775
    for i in 0..69 {
7776
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7777
            return true;
×
7778
        }
×
7779
    }
7780
    false
×
7781
}
×
7782

7783
#[inline(always)]
7784
#[unroll_for_loops]
7785
const fn slice_contains_70_2(
×
7786
    slice_: &[u8; 70],
×
7787
    search: &[u8; 2],
×
7788
) -> bool {
×
7789
    for i in 0..70 {
7790
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7791
            return true;
×
7792
        }
×
7793
    }
7794
    false
×
7795
}
×
7796
#[inline(always)]
7797
#[unroll_for_loops]
7798
const fn slice_contains_71_2(
×
7799
    slice_: &[u8; 71],
×
7800
    search: &[u8; 2],
×
7801
) -> bool {
×
7802
    for i in 0..71 {
7803
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7804
            return true;
×
7805
        }
×
7806
    }
7807
    false
×
7808
}
×
7809

7810
#[inline(always)]
7811
#[unroll_for_loops]
7812
const fn slice_contains_72_2(
×
7813
    slice_: &[u8; 72],
×
7814
    search: &[u8; 2],
×
7815
) -> bool {
×
7816
    for i in 0..72 {
7817
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7818
            return true;
×
7819
        }
×
7820
    }
7821
    false
×
7822
}
×
7823

7824
#[inline(always)]
7825
#[unroll_for_loops]
7826
const fn slice_contains_73_2(
×
7827
    slice_: &[u8; 73],
×
7828
    search: &[u8; 2],
×
7829
) -> bool {
×
7830
    for i in 0..73 {
7831
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7832
            return true;
×
7833
        }
×
7834
    }
7835
    false
×
7836
}
×
7837

7838
#[inline(always)]
7839
#[unroll_for_loops]
7840
const fn slice_contains_74_2(
×
7841
    slice_: &[u8; 74],
×
7842
    search: &[u8; 2],
×
7843
) -> bool {
×
7844
    for i in 0..74 {
7845
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7846
            return true;
×
7847
        }
×
7848
    }
7849
    false
×
7850
}
×
7851

7852
#[inline(always)]
7853
#[unroll_for_loops]
7854
const fn slice_contains_75_2(
×
7855
    slice_: &[u8; 75],
×
7856
    search: &[u8; 2],
×
7857
) -> bool {
×
7858
    for i in 0..75 {
7859
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7860
            return true;
×
7861
        }
×
7862
    }
7863
    false
×
7864
}
×
7865

7866
#[inline(always)]
7867
#[unroll_for_loops]
7868
const fn slice_contains_76_2(
×
7869
    slice_: &[u8; 76],
×
7870
    search: &[u8; 2],
×
7871
) -> bool {
×
7872
    for i in 0..76 {
7873
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7874
            return true;
×
7875
        }
×
7876
    }
7877
    false
×
7878
}
×
7879

7880
#[inline(always)]
7881
#[unroll_for_loops]
7882
const fn slice_contains_77_2(
×
7883
    slice_: &[u8; 77],
×
7884
    search: &[u8; 2],
×
7885
) -> bool {
×
7886
    for i in 0..77 {
7887
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7888
            return true;
×
7889
        }
×
7890
    }
7891
    false
×
7892
}
×
7893

7894
#[inline(always)]
7895
#[unroll_for_loops]
7896
const fn slice_contains_78_2(
×
7897
    slice_: &[u8; 78],
×
7898
    search: &[u8; 2],
×
7899
) -> bool {
×
7900
    for i in 0..78 {
7901
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7902
            return true;
×
7903
        }
×
7904
    }
7905
    false
×
7906
}
×
7907

7908
#[inline(always)]
7909
#[unroll_for_loops]
7910
const fn slice_contains_79_2(
×
7911
    slice_: &[u8; 79],
×
7912
    search: &[u8; 2],
×
7913
) -> bool {
×
7914
    for i in 0..79 {
7915
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7916
            return true;
×
7917
        }
×
7918
    }
7919
    false
×
7920
}
×
7921

7922
#[inline(always)]
7923
#[unroll_for_loops]
7924
const fn slice_contains_80_2(
×
7925
    slice_: &[u8; 80],
×
7926
    search: &[u8; 2],
×
7927
) -> bool {
×
7928
    for i in 0..80 {
7929
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7930
            return true;
×
7931
        }
×
7932
    }
7933
    false
×
7934
}
×
7935

7936
#[inline(always)]
7937
#[unroll_for_loops]
7938
const fn slice_contains_81_2(
×
7939
    slice_: &[u8; 81],
×
7940
    search: &[u8; 2],
×
7941
) -> bool {
×
7942
    for i in 0..81 {
7943
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7944
            return true;
×
7945
        }
×
7946
    }
7947
    false
×
7948
}
×
7949

7950
#[inline(always)]
7951
#[unroll_for_loops]
7952
const fn slice_contains_82_2(
×
7953
    slice_: &[u8; 82],
×
7954
    search: &[u8; 2],
×
7955
) -> bool {
×
7956
    for i in 0..82 {
7957
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7958
            return true;
×
7959
        }
×
7960
    }
7961
    false
×
7962
}
×
7963

7964
#[inline(always)]
7965
#[unroll_for_loops]
7966
const fn slice_contains_83_2(
×
7967
    slice_: &[u8; 83],
×
7968
    search: &[u8; 2],
×
7969
) -> bool {
×
7970
    for i in 0..83 {
7971
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7972
            return true;
×
7973
        }
×
7974
    }
7975
    false
×
7976
}
×
7977

7978
#[inline(always)]
7979
#[unroll_for_loops]
7980
const fn slice_contains_84_2(
×
7981
    slice_: &[u8; 84],
×
7982
    search: &[u8; 2],
×
7983
) -> bool {
×
7984
    for i in 0..84 {
7985
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
7986
            return true;
×
7987
        }
×
7988
    }
7989
    false
×
7990
}
×
7991

7992
#[inline(always)]
7993
#[unroll_for_loops]
7994
const fn slice_contains_85_2(
×
7995
    slice_: &[u8; 85],
×
7996
    search: &[u8; 2],
×
7997
) -> bool {
×
7998
    for i in 0..85 {
7999
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8000
            return true;
×
8001
        }
×
8002
    }
8003
    false
×
8004
}
×
8005

8006
#[inline(always)]
8007
#[unroll_for_loops]
8008
const fn slice_contains_86_2(
×
8009
    slice_: &[u8; 86],
×
8010
    search: &[u8; 2],
×
8011
) -> bool {
×
8012
    for i in 0..86 {
8013
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8014
            return true;
×
8015
        }
×
8016
    }
8017
    false
×
8018
}
×
8019

8020
#[inline(always)]
8021
#[unroll_for_loops]
8022
const fn slice_contains_87_2(
×
8023
    slice_: &[u8; 87],
×
8024
    search: &[u8; 2],
×
8025
) -> bool {
×
8026
    for i in 0..87 {
8027
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8028
            return true;
×
8029
        }
×
8030
    }
8031
    false
×
8032
}
×
8033

8034
#[inline(always)]
8035
#[unroll_for_loops]
8036
const fn slice_contains_88_2(
×
8037
    slice_: &[u8; 88],
×
8038
    search: &[u8; 2],
×
8039
) -> bool {
×
8040
    for i in 0..88 {
8041
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8042
            return true;
×
8043
        }
×
8044
    }
8045
    false
×
8046
}
×
8047

8048
#[inline(always)]
8049
#[unroll_for_loops]
8050
const fn slice_contains_89_2(
×
8051
    slice_: &[u8; 89],
×
8052
    search: &[u8; 2],
×
8053
) -> bool {
×
8054
    for i in 0..89 {
8055
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8056
            return true;
×
8057
        }
×
8058
    }
8059
    false
×
8060
}
×
8061

8062
#[inline(always)]
8063
#[unroll_for_loops]
8064
const fn slice_contains_90_2(
×
8065
    slice_: &[u8; 90],
×
8066
    search: &[u8; 2],
×
8067
) -> bool {
×
8068
    for i in 0..90 {
8069
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8070
            return true;
×
8071
        }
×
8072
    }
8073
    false
×
8074
}
×
8075

8076
#[inline(always)]
8077
#[unroll_for_loops]
8078
const fn slice_contains_91_2(
×
8079
    slice_: &[u8; 91],
×
8080
    search: &[u8; 2],
×
8081
) -> bool {
×
8082
    for i in 0..91 {
8083
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8084
            return true;
×
8085
        }
×
8086
    }
8087
    false
×
8088
}
×
8089

8090
#[inline(always)]
8091
#[unroll_for_loops]
8092
const fn slice_contains_92_2(
×
8093
    slice_: &[u8; 92],
×
8094
    search: &[u8; 2],
×
8095
) -> bool {
×
8096
    for i in 0..92 {
8097
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8098
            return true;
×
8099
        }
×
8100
    }
8101
    false
×
8102
}
×
8103

8104
#[inline(always)]
8105
#[unroll_for_loops]
8106
const fn slice_contains_93_2(
×
8107
    slice_: &[u8; 93],
×
8108
    search: &[u8; 2],
×
8109
) -> bool {
×
8110
    for i in 0..93 {
8111
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8112
            return true;
×
8113
        }
×
8114
    }
8115
    false
×
8116
}
×
8117

8118
#[inline(always)]
8119
#[unroll_for_loops]
8120
const fn slice_contains_94_2(
×
8121
    slice_: &[u8; 94],
×
8122
    search: &[u8; 2],
×
8123
) -> bool {
×
8124
    for i in 0..94 {
8125
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8126
            return true;
×
8127
        }
×
8128
    }
8129
    false
×
8130
}
×
8131

8132
#[inline(always)]
8133
#[unroll_for_loops]
8134
const fn slice_contains_95_2(
×
8135
    slice_: &[u8; 95],
×
8136
    search: &[u8; 2],
×
8137
) -> bool {
×
8138
    for i in 0..95 {
8139
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8140
            return true;
×
8141
        }
×
8142
    }
8143
    false
×
8144
}
×
8145

8146
#[inline(always)]
8147
#[unroll_for_loops]
8148
const fn slice_contains_96_2(
×
8149
    slice_: &[u8; 96],
×
8150
    search: &[u8; 2],
×
8151
) -> bool {
×
8152
    for i in 0..96 {
8153
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8154
            return true;
×
8155
        }
×
8156
    }
8157
    false
×
8158
}
×
8159

8160
#[inline(always)]
8161
#[unroll_for_loops]
8162
const fn slice_contains_97_2(
×
8163
    slice_: &[u8; 97],
×
8164
    search: &[u8; 2],
×
8165
) -> bool {
×
8166
    for i in 0..97 {
8167
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8168
            return true;
×
8169
        }
×
8170
    }
8171
    false
×
8172
}
×
8173

8174
#[inline(always)]
8175
#[unroll_for_loops]
8176
const fn slice_contains_98_2(
×
8177
    slice_: &[u8; 98],
×
8178
    search: &[u8; 2],
×
8179
) -> bool {
×
8180
    for i in 0..98 {
8181
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8182
            return true;
×
8183
        }
×
8184
    }
8185
    false
×
8186
}
×
8187
#[inline(always)]
8188
#[unroll_for_loops]
8189
const fn slice_contains_99_2(
×
8190
    slice_: &[u8; 99],
×
8191
    search: &[u8; 2],
×
8192
) -> bool {
×
8193
    for i in 0..99 {
8194
        if slice_[i] == search[0] || slice_[i] == search[1] {
×
8195
            return true;
×
8196
        }
×
8197
    }
8198
    false
×
8199
}
×
8200

8201
/// Loop unrolled implementation of [`slice.contains`].
8202
/// Returns `true` if any byte in `search` is found in `slice_`.
8203
/// Uses crate [`unroll`].
8204
///
8205
/// Hardcoded implementation for [`u8`] slices up to 99 length. Falls back to
8206
/// using `slice.contains` for slices longer than 99.
8207
///
8208
/// Runs very fast according to benches in `src/benches/slice_contains.rs`.
8209
///
8210
/// [`unroll`]: https://docs.rs/unroll/0.1.5/unroll/index.html
8211
/// [`slice.contains`]: https://doc.rust-lang.org/1.66.0/std/primitive.slice.html#method.contains
8212
//
8213
// slice index values for the `DTPD!` declarations can be reviewed with:
8214
//
8215
//     $ grep -Ee '^[[:space:]]+DTFSS_' -- src/data/datetime.rs  | sort -t ',' -n -k2 -k3 | column -t
8216
//
8217
#[inline(always)]
8218
#[allow(non_snake_case)]
8219
pub fn slice_contains_X_2_unroll(
59✔
8220
    slice_: &[u8],
59✔
8221
    search: &[u8; 2],
59✔
8222
) -> bool {
59✔
8223
    match slice_.len() {
59✔
8224
        2 => slice_contains_2_2(<&[u8; 2]>::try_from(slice_).unwrap(), search),
9✔
8225
        3 => slice_contains_3_2(<&[u8; 3]>::try_from(slice_).unwrap(), search),
7✔
8226
        4 => slice_contains_4_2(<&[u8; 4]>::try_from(slice_).unwrap(), search),
10✔
8227
        5 => slice_contains_5_2(<&[u8; 5]>::try_from(slice_).unwrap(), search),
11✔
8228
        6 => slice_contains_6_2(<&[u8; 6]>::try_from(slice_).unwrap(), search),
13✔
8229
        7 => slice_contains_7_2(<&[u8; 7]>::try_from(slice_).unwrap(), search),
×
8230
        8 => slice_contains_8_2(<&[u8; 8]>::try_from(slice_).unwrap(), search),
×
8231
        9 => slice_contains_9_2(<&[u8; 9]>::try_from(slice_).unwrap(), search),
×
8232
        10 => slice_contains_10_2(<&[u8; 10]>::try_from(slice_).unwrap(), search),
×
8233
        11 => slice_contains_11_2(<&[u8; 11]>::try_from(slice_).unwrap(), search),
×
8234
        12 => slice_contains_12_2(<&[u8; 12]>::try_from(slice_).unwrap(), search),
×
8235
        13 => slice_contains_13_2(<&[u8; 13]>::try_from(slice_).unwrap(), search),
×
8236
        14 => slice_contains_14_2(<&[u8; 14]>::try_from(slice_).unwrap(), search),
×
8237
        15 => slice_contains_15_2(<&[u8; 15]>::try_from(slice_).unwrap(), search),
×
8238
        16 => slice_contains_16_2(<&[u8; 16]>::try_from(slice_).unwrap(), search),
×
8239
        17 => slice_contains_17_2(<&[u8; 17]>::try_from(slice_).unwrap(), search),
×
8240
        18 => slice_contains_18_2(<&[u8; 18]>::try_from(slice_).unwrap(), search),
×
8241
        19 => slice_contains_19_2(<&[u8; 19]>::try_from(slice_).unwrap(), search),
×
8242
        20 => slice_contains_20_2(<&[u8; 20]>::try_from(slice_).unwrap(), search),
×
8243
        21 => slice_contains_21_2(<&[u8; 21]>::try_from(slice_).unwrap(), search),
×
8244
        22 => slice_contains_22_2(<&[u8; 22]>::try_from(slice_).unwrap(), search),
×
8245
        23 => slice_contains_23_2(<&[u8; 23]>::try_from(slice_).unwrap(), search),
×
8246
        24 => slice_contains_24_2(<&[u8; 24]>::try_from(slice_).unwrap(), search),
×
8247
        25 => slice_contains_25_2(<&[u8; 25]>::try_from(slice_).unwrap(), search),
×
8248
        26 => slice_contains_26_2(<&[u8; 26]>::try_from(slice_).unwrap(), search),
×
8249
        27 => slice_contains_27_2(<&[u8; 27]>::try_from(slice_).unwrap(), search),
×
8250
        28 => slice_contains_28_2(<&[u8; 28]>::try_from(slice_).unwrap(), search),
×
8251
        29 => slice_contains_29_2(<&[u8; 29]>::try_from(slice_).unwrap(), search),
×
8252
        30 => slice_contains_30_2(<&[u8; 30]>::try_from(slice_).unwrap(), search),
×
8253
        31 => slice_contains_31_2(<&[u8; 31]>::try_from(slice_).unwrap(), search),
×
8254
        32 => slice_contains_32_2(<&[u8; 32]>::try_from(slice_).unwrap(), search),
×
8255
        33 => slice_contains_33_2(<&[u8; 33]>::try_from(slice_).unwrap(), search),
×
8256
        34 => slice_contains_34_2(<&[u8; 34]>::try_from(slice_).unwrap(), search),
×
8257
        35 => slice_contains_35_2(<&[u8; 35]>::try_from(slice_).unwrap(), search),
×
8258
        36 => slice_contains_36_2(<&[u8; 36]>::try_from(slice_).unwrap(), search),
×
8259
        37 => slice_contains_37_2(<&[u8; 37]>::try_from(slice_).unwrap(), search),
×
8260
        38 => slice_contains_38_2(<&[u8; 38]>::try_from(slice_).unwrap(), search),
×
8261
        39 => slice_contains_39_2(<&[u8; 39]>::try_from(slice_).unwrap(), search),
×
8262
        40 => slice_contains_40_2(<&[u8; 40]>::try_from(slice_).unwrap(), search),
×
8263
        41 => slice_contains_41_2(<&[u8; 41]>::try_from(slice_).unwrap(), search),
×
8264
        42 => slice_contains_42_2(<&[u8; 42]>::try_from(slice_).unwrap(), search),
×
8265
        43 => slice_contains_43_2(<&[u8; 43]>::try_from(slice_).unwrap(), search),
×
8266
        44 => slice_contains_44_2(<&[u8; 44]>::try_from(slice_).unwrap(), search),
×
8267
        45 => slice_contains_45_2(<&[u8; 45]>::try_from(slice_).unwrap(), search),
×
8268
        46 => slice_contains_46_2(<&[u8; 46]>::try_from(slice_).unwrap(), search),
×
8269
        47 => slice_contains_47_2(<&[u8; 47]>::try_from(slice_).unwrap(), search),
×
8270
        48 => slice_contains_48_2(<&[u8; 48]>::try_from(slice_).unwrap(), search),
×
8271
        49 => slice_contains_49_2(<&[u8; 49]>::try_from(slice_).unwrap(), search),
×
8272
        50 => slice_contains_50_2(<&[u8; 50]>::try_from(slice_).unwrap(), search),
×
8273
        51 => slice_contains_51_2(<&[u8; 51]>::try_from(slice_).unwrap(), search),
×
8274
        52 => slice_contains_52_2(<&[u8; 52]>::try_from(slice_).unwrap(), search),
×
8275
        53 => slice_contains_53_2(<&[u8; 53]>::try_from(slice_).unwrap(), search),
×
8276
        54 => slice_contains_54_2(<&[u8; 54]>::try_from(slice_).unwrap(), search),
×
8277
        55 => slice_contains_55_2(<&[u8; 55]>::try_from(slice_).unwrap(), search),
×
8278
        56 => slice_contains_56_2(<&[u8; 56]>::try_from(slice_).unwrap(), search),
×
8279
        57 => slice_contains_57_2(<&[u8; 57]>::try_from(slice_).unwrap(), search),
×
8280
        58 => slice_contains_58_2(<&[u8; 58]>::try_from(slice_).unwrap(), search),
×
8281
        59 => slice_contains_59_2(<&[u8; 59]>::try_from(slice_).unwrap(), search),
×
8282
        60 => slice_contains_60_2(<&[u8; 60]>::try_from(slice_).unwrap(), search),
×
8283
        61 => slice_contains_61_2(<&[u8; 61]>::try_from(slice_).unwrap(), search),
×
8284
        62 => slice_contains_62_2(<&[u8; 62]>::try_from(slice_).unwrap(), search),
×
8285
        63 => slice_contains_63_2(<&[u8; 63]>::try_from(slice_).unwrap(), search),
×
8286
        64 => slice_contains_64_2(<&[u8; 64]>::try_from(slice_).unwrap(), search),
×
8287
        65 => slice_contains_65_2(<&[u8; 65]>::try_from(slice_).unwrap(), search),
×
8288
        66 => slice_contains_66_2(<&[u8; 66]>::try_from(slice_).unwrap(), search),
×
8289
        67 => slice_contains_67_2(<&[u8; 67]>::try_from(slice_).unwrap(), search),
×
8290
        68 => slice_contains_68_2(<&[u8; 68]>::try_from(slice_).unwrap(), search),
×
8291
        69 => slice_contains_69_2(<&[u8; 69]>::try_from(slice_).unwrap(), search),
×
8292
        70 => slice_contains_70_2(<&[u8; 70]>::try_from(slice_).unwrap(), search),
×
8293
        71 => slice_contains_71_2(<&[u8; 71]>::try_from(slice_).unwrap(), search),
×
8294
        72 => slice_contains_72_2(<&[u8; 72]>::try_from(slice_).unwrap(), search),
×
8295
        73 => slice_contains_73_2(<&[u8; 73]>::try_from(slice_).unwrap(), search),
×
8296
        74 => slice_contains_74_2(<&[u8; 74]>::try_from(slice_).unwrap(), search),
×
8297
        75 => slice_contains_75_2(<&[u8; 75]>::try_from(slice_).unwrap(), search),
×
8298
        76 => slice_contains_76_2(<&[u8; 76]>::try_from(slice_).unwrap(), search),
×
8299
        77 => slice_contains_77_2(<&[u8; 77]>::try_from(slice_).unwrap(), search),
×
8300
        78 => slice_contains_78_2(<&[u8; 78]>::try_from(slice_).unwrap(), search),
×
8301
        79 => slice_contains_79_2(<&[u8; 79]>::try_from(slice_).unwrap(), search),
×
8302
        80 => slice_contains_80_2(<&[u8; 80]>::try_from(slice_).unwrap(), search),
×
8303
        81 => slice_contains_81_2(<&[u8; 81]>::try_from(slice_).unwrap(), search),
×
8304
        82 => slice_contains_82_2(<&[u8; 82]>::try_from(slice_).unwrap(), search),
×
8305
        83 => slice_contains_83_2(<&[u8; 83]>::try_from(slice_).unwrap(), search),
×
8306
        84 => slice_contains_84_2(<&[u8; 84]>::try_from(slice_).unwrap(), search),
×
8307
        85 => slice_contains_85_2(<&[u8; 85]>::try_from(slice_).unwrap(), search),
×
8308
        86 => slice_contains_86_2(<&[u8; 86]>::try_from(slice_).unwrap(), search),
×
8309
        87 => slice_contains_87_2(<&[u8; 87]>::try_from(slice_).unwrap(), search),
×
8310
        88 => slice_contains_88_2(<&[u8; 88]>::try_from(slice_).unwrap(), search),
×
8311
        89 => slice_contains_89_2(<&[u8; 89]>::try_from(slice_).unwrap(), search),
×
8312
        90 => slice_contains_90_2(<&[u8; 90]>::try_from(slice_).unwrap(), search),
×
8313
        91 => slice_contains_91_2(<&[u8; 91]>::try_from(slice_).unwrap(), search),
×
8314
        92 => slice_contains_92_2(<&[u8; 92]>::try_from(slice_).unwrap(), search),
×
8315
        93 => slice_contains_93_2(<&[u8; 93]>::try_from(slice_).unwrap(), search),
×
8316
        94 => slice_contains_94_2(<&[u8; 94]>::try_from(slice_).unwrap(), search),
×
8317
        95 => slice_contains_95_2(<&[u8; 95]>::try_from(slice_).unwrap(), search),
×
8318
        96 => slice_contains_96_2(<&[u8; 96]>::try_from(slice_).unwrap(), search),
×
8319
        97 => slice_contains_97_2(<&[u8; 97]>::try_from(slice_).unwrap(), search),
×
8320
        98 => slice_contains_98_2(<&[u8; 98]>::try_from(slice_).unwrap(), search),
×
8321
        99 => slice_contains_99_2(<&[u8; 99]>::try_from(slice_).unwrap(), search),
×
8322
        _ => {
8323
            // fallback to `slice_.contains`
8324
            // surprisingly good performance according to benches in `bench_slice_contains`
8325
            slice_.contains(&search[0]) || slice_.contains(&search[1])
9✔
8326
        }
8327
   }
8328
}
59✔
8329

8330
/// `jetscii` implementation of `slice.contains` for a byte slice and a
8331
/// hardcoded array.
8332
#[inline(always)]
8333
#[allow(non_snake_case)]
8334
#[cfg(feature = "bench_jetscii")]
8335
pub fn slice_contains_X_2_jetscii(
8336
    slice_: &[u8],
8337
    search: &[u8; 2],
8338
) -> bool {
8339
    jetscii::bytes!(search[0], search[1]).find(slice_).is_some()
8340
}
8341

8342
/// `memchr` implementation of `slice.contains` for a byte slice and a
8343
/// hardcoded array.
8344
#[inline(always)]
8345
#[allow(non_snake_case)]
8346
pub fn slice_contains_X_2_memchr(
118✔
8347
    slice_: &[u8],
118✔
8348
    search: &[u8; 2],
118✔
8349
) -> bool {
118✔
8350
    memchr::memchr2(
118✔
8351
        search[0],
118✔
8352
        search[1],
118✔
8353
        slice_,
118✔
8354
    ).is_some()
118✔
8355
}
118✔
8356

8357
/// Stringzilla implementation of `slice.contains` for a byte slice and a
8358
/// hardcoded array.
8359
/// Uses crate [`stringzilla`].
8360
///
8361
/// [`stringzilla`]: https://crates.io/crates/stringzilla
8362
#[inline(always)]
8363
#[allow(non_snake_case)]
8364
#[cfg(feature = "bench_stringzilla")]
8365
pub fn slice_contains_X_2_stringzilla(
8366
    slice_: &[u8],
8367
    search: &[u8; 2],
8368
) -> bool {
8369
    stringzilla::sz::find_char_from(slice_, search).is_some()
8370
}
8371

8372
/// Wrapper to call the preferred `slice_contains_X_2` function.
8373
#[inline(always)]
8374
#[allow(non_snake_case)]
8375
pub fn slice_contains_X_2(
59✔
8376
    slice_: &[u8],
59✔
8377
    search: &[u8; 2],
59✔
8378
) -> bool {
59✔
8379
    slice_contains_X_2_memchr(slice_, search)
59✔
8380
}
59✔
8381

8382
/// Returns `true` if `slice_` contains consecutive "digit" chars (as UTF8 bytes).
8383
/// Custom implementation.
8384
/// Hack efficiency helper.
8385
#[inline(always)]
8386
#[allow(non_snake_case)]
8387
pub fn slice_contains_D2_custom(
12,649✔
8388
    slice_: &[u8],
12,649✔
8389
) -> bool {
12,649✔
8390
    let mut byte_last_d: bool = false;
12,649✔
8391
    for byte_ in slice_.iter() {
30,875✔
8392
        match byte_ {
30,875✔
8393
            b'0'
8394
            | b'1'
8395
            | b'2'
8396
            | b'3'
8397
            | b'4'
8398
            | b'5'
8399
            | b'6'
8400
            | b'7'
8401
            | b'8'
8402
            | b'9' => {
8403
                if byte_last_d {
24,123✔
8404
                    return true;
11,600✔
8405
                }
12,523✔
8406
                byte_last_d = true;
12,523✔
8407
            },
8408
            _ => byte_last_d = false,
6,752✔
8409
        }
8410
    }
8411

8412
    false
1,049✔
8413
}
12,649✔
8414

8415
/// Returns `true` if `slice_` contains consecutive "digit" chars (as UTF8 bytes).
8416
/// jetscii implementation.
8417
/// Hack efficiency helper.
8418
#[inline(always)]
8419
#[allow(non_snake_case)]
8420
#[cfg(feature = "bench_jetscii")]
8421
pub fn slice_contains_D2_jetscii(
8422
    slice_: &[u8],
8423
) -> bool {
8424
    let mut atn: usize = 0;
8425
    let mut lastn: isize = -1;
8426
    let bytes_ = jetscii::bytes!(b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9');
8427
    while let Some(n) = bytes_.find(&slice_[atn..]) {
8428
        if lastn != -1 && atn + n == ((lastn + 1) as usize) {
8429
            return true;
8430
        }
8431
        lastn = (atn + n) as isize;
8432
        atn = atn + n + 1;
8433
    }
8434

8435
    false
8436
}
8437

8438
/// Returns `true` if `slice_` contains consecutive "digit" chars (as UTF8 bytes).
8439
/// Stringzilla implementation.
8440
/// Hack efficiency helper.
8441
#[inline(always)]
8442
#[allow(non_snake_case)]
8443
#[cfg(feature = "bench_stringzilla")]
8444
pub fn slice_contains_D2_stringzilla(
8445
    slice_: &[u8],
8446
) -> bool {
8447
    let mut atn: usize = 0;
8448
    let mut lastn: isize = -1;
8449
    while let Some(n) = stringzilla::sz::find_char_from(&slice_[atn..], b"0123456789") {
8450
        if lastn != -1 && atn + n == ((lastn + 1) as usize) {
8451
            return true;
8452
        }
8453
        lastn = (atn + n) as isize;
8454
        atn = atn + n + 1;
8455
    }
8456

8457
    false
8458
}
8459

8460
/// Wrapper to call the preferred `slice_contains_D2` function.
8461
/// Returns `true` if `slice_` contains consecutive "digit" chars (as UTF8 bytes).
8462
/// Hack efficiency helper.
8463
#[inline(always)]
8464
#[allow(non_snake_case)]
8465
pub fn slice_contains_D2(
12,624✔
8466
    slice_: &[u8],
12,624✔
8467
) -> bool {
12,624✔
8468
    slice_contains_D2_custom(slice_)
12,624✔
8469
}
12,624✔
8470

8471
/// Combination of prior functions `slice_contains_X_2` and
8472
/// `slice_contains_D2`.
8473
///
8474
/// This combined hack check function is more efficient.
8475
///
8476
/// - Returns `true` if `slice_` contains `'1'` or `'2'` (as UTF8 bytes).
8477
/// - Returns `true` if `slice_` contains two consecutive "digit" chars
8478
///   (as UTF8 bytes).
8479
#[inline(always)]
8480
#[allow(non_snake_case)]
8481
pub (crate) fn slice_contains_12_D2(
205,538✔
8482
    slice_: &[u8],
205,538✔
8483
) -> bool {
205,538✔
8484
    let mut byte_last_d: bool = false;
205,538✔
8485
    for byte_ in slice_.iter() {
288,280✔
8486
        match byte_ {
288,280✔
8487
            b'1'
8488
            | b'2' => {
8489
                return true;
186,669✔
8490
            }
8491
            b'0'
8492
            | b'3'
8493
            | b'4'
8494
            | b'5'
8495
            | b'6'
8496
            | b'7'
8497
            | b'8'
8498
            | b'9' => {
8499
                if byte_last_d {
26,993✔
8500
                    return true;
7,241✔
8501
                }
19,752✔
8502
                byte_last_d = true;
19,752✔
8503
            },
8504
            _ => byte_last_d = false,
74,618✔
8505
        }
8506
    }
8507

8508
    false
11,628✔
8509
}
205,538✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc