use strict;
my $str = '// Time Zone UTC Offsets in actual use for ISO 8601 / RFC 3339 (Museum of Bad Data)
// Accept: UTC indicator
Z
// Accept: Valid +xx:00
+00:00
+01:00
+02:00
+03:00
+04:00
+05:00
+06:00
+07:00
+08:00
+09:00
+10:00
+11:00
+12:00
+13:00
+14:00
// Accept: Valid -xx:00
-01:00
-02:00
-03:00
-04:00
-05:00
-06:00
-07:00
-08:00
-09:00
-10:00
-11:00
-12:00
// Accept: Valid +xx:30
+03:30
+04:30
+05:30
+06:30
+09:30
+10:30
// Accept: Valid +xx:45
+05:45
+08:45
+12:45
// Accept: Valid -xx:30
-03:30
-09:30
// Accept: Valid: 30 offsets
+03:30
+04:30
+05:30
+06:30
+09:30
+10:30
-03:30
-09:30
// Accept: Valid :45 offsets
+05:45
+08:45
+12:45
// Reject: valid RFC 3339, invalid ISO 8601
-00:00
// Reject: no such UTC offset in use
-13:00
-14:00
+00:01
+00:03
+00:99
+20:00
+0:00
// Reject: no such UTC offset in use
+01:30
+07:30
+08:30
+02:30
+11:30
+12:30
+13:30
+14:30
-01:30
-02:30
-04:30
-05:30
-06:30
-07:30
-08:30
-10:30
-11:30
-12:30
-13:30
-14:30
// Reject: Unused :45 offsets
+01:45
+02:45
+03:45
+04:45
+06:45
+07:45
+09:45
+10:45
+11:45
+13:45
+14:45
-01:45
-02:45
-03:45
-04:45
-05:45
-06:45
-07:45
-08:45
-09:45
-10:45
-11:45
-12:45
-13:45
-14:45
+01:15
+02:15
+03:15
+04:15
+05:15
+06:15
+07:15
+08:15
+09:15
+10:15
+11:15
+12:15
+13:15
+14:15
-01:15
-02:15
-03:15
-04:15
-05:15
-06:15
-07:15
-08:15
-09:15
-10:15
-11:15
-12:15
-13:15
-14:15
// Reject: Z stands alone
Z00:00
Z00
Z0
// Reject: hyphen required
0100
+0100
-0100
// Reject: colon required
+0100
// Reject: No extra characters
2001-02-03T04:05:06.007+0800
+08:00
+08:00
```
### Expanded Pattern:
```
^(?!-00:00)(?=^(?:Z|[\\+\\-](?:0[0-9]|1[012]):00|\\+0[34569]:30|\\+10:30|-03:30|-09:30|\\+13:00|\\+14:00|\\+05:45|\\+08:45|\\+12:45))^((Z)|([\\+\\-])(\\d\\d):(\\d\\d))$
```
```
^ # use zero-width assertions to capture all the special cases:
(?!-00:00) # not -00:00
(?=^(?:
Z # Z alone works,
|[\\+\\-](?:0[0-9]|1[012]):00 # and all other +/- xx:00s,
|\\+0[34569]:30|\\+10:30|-03:30|-09:30 # the +/- xx:30s.
|\\+13:00|\\+14:00|\\+05:45|\\+08:45|\\+12:45 # and these special cases
)) # Now that we\'ve forced only positive matches, let\'s capture the pieces:
^( # G1: the whole offset
(Z) | # G2: UTC indicator or nil
([\\+\\-]) # G3: +/- direction
(\\d\\d) # G4: "hours" part of offset
:
(\\d\\d) # G5: "minutes" part of offset
)$
```
';
my $regex = qr/^(?!-00:00)(?=^(?:Z|[\+\-](?:0[0-9]|1[012]):00|\+0[34569]:30|\+10:30|-03:30|-09:30|\+13:00|\+14:00|\+05:45|\+08:45|\+12:45))^((Z)|([\+\-])(\d\d):(\d\d))$/mp;
if ( $str =~ /$regex/g ) {
print "Whole match is ${^MATCH} and its start/end positions can be obtained via \$-[0] and \$+[0]\n";
# print "Capture Group 1 is $1 and its start/end positions can be obtained via \$-[1] and \$+[1]\n";
# print "Capture Group 2 is $2 ... and so on\n";
}
# ${^POSTMATCH} and ${^PREMATCH} are also available with the use of '/p'
# Named capture groups can be called via $+{name}
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Perl, please visit: http://perldoc.perl.org/perlre.html