Perl code that is syntactically correct only on Fridays
github.com
github.com
Unfortunately, the intern only worked the mornings, so it took several days of back and forth before the bug could be finally put to rest.
That's how you get an application with 50,000 unit and integration tests that you can't run locally, and requires massive parallelism in CI to finish in any reasonable timeframe.
Mine is lack of future-proofing. Using dates stuck at one point in time. Or worse, the test suite that fails because of local clock skew.
#if (some C preprocessor expression that's true on Fridays
printf("Hello world!);
#else
printf;
#fi
(although it's been long enough since I've written C that I don't know if (a) there exists a C-preprocessor instruction as per the above, (2) if the compiler will notice errors in the unexecuted branch of an #if and (iii) if printf; would give a syntax error).Another equivalent piece of code could be something along the lines of the JS
if (today_is_friday()) {
eval("2+2")
}
else {
eval("--2abc")
}There's no day-of-week but you could cause mayhem with __DATE__
>(2) if the compiler will notice errors in the unexecuted branch of an #if
Compiler only gets to see preprocessor output, it won't.
>if printf; would give a syntax error
Perhaps surprisingly but no, it's just statement without effect (containing expression returning function pointer to printf).
It takes some patience, but people do try: https://stackoverflow.com/questions/11697820#16472369
Anything involving a slash is asking for trouble.
It couldn't fail at his desk, because he ran both servers locally while developing. And it wouldn't fail in production in the morning, since we had a cron job that synced the clocks at midnight. It took until after 5pm for the clocks to drift enough, meaning I was on call and the intern was not.
But the bug didn't crop up until his code had been in production for a couple of weeks, so it was a real pain in the neck to track down.
Oh man, tell me about it. Timezones are the worst. No wonder so many people just give up and use Moment.js.
But not too far past!
Man they are so hard that you could go on for hours about it.
1. https://en.wikipedia.org/wiki/Time_in_Indiana#tz_database
That's the primary driver for complexity here. Time as a property is hard to deal with because we take something simple, like how many seconds have passed since some other point in time, and then we have to correlate it to the sun and the moon and how close the Earth is to the sun and what side of the Earth we're on, and etc.
It forces computers to wrap a simple understanding of the passage of time in a lot of context that makes no difference to the computer.
Getting rid of calendaring doesn't imply that no one knows when winter is; winter is still a function of time. It's a pretty simple algorithm to figure out whether a given month is winter or not. It's easy to derive that context from an absolute measurement of time. It's an incredible amount more difficult to go from our calendaring system to any kind of absolute time, because our calendaring is kind of arbitrarily made up to keep things in sync. It bears little semblance to the passage of time, because it's purpose is more about maintaining context (i.e. the sun rises early in the morning, it's winter in January, etc) than actually measuring the passage of time.
Personally, I think my definition of "simple" changed over time. Much of that was influenced by the depth of my knowledge of particular paradigms, whether that be software or knowledge of the subjects I was writing code for.
You get what you pay for, eh?
Time synchronization is very arguable THE most important thing on a server (for numerous security related reasons).
How many moons ago was this incident? It's perhaps forgivable ignorance in the 90s... not so much today.
NTP can't properly fix a clock like that either since it's often capped at adjusting the speed by one part per two thousand. At most, with a consistently wrong clock, that can handle about 30 seconds per day. Any worse than that and you won't see much advantage over ntpdate.
1-2 seconds is (probably) well within manageable. But, since you now know for SUER that you have clocks running at different speeds, you need to over-estimate the skew. And hope that the daily skewing is approximately constant over time.
So, yes, clock skew can have an impact on your security, because it makes event correlation (and followup on security incidents) harder.
This could be timestamps in a DB server, or similar.
You can then, if you have too-large skew, end up in the weird position that one of "things that have been commited in the DB is not yet showing up on the frontends" (if the time used as a cut-off for the frontend's query is lagging behind the DB server and the timestamp is set by the DB server) or "things that have been commited are not showing up when you query for SELECT timestamp <= NOW()" (if the DB server is lagging, and the timestamp is set by the client).
If that maters, well, that's really a business and data quality issue.
Some distributed systems will also try to figure out what skew you have across the whole system and then end up taking N times that skew, before it can consider data persisted (see for example Spanner ,and probably CockroachDB). If your distributed system relies on timestamps for consistency, and it doesn't self-discover the skew, you're basically not guaranteed whatever consistency guarantees that the distributed system claims to have.
Again, is this important? It really depends. Is it OK of your distributed data store drops some of your data on the floor and lets you clean up the mess? Sometimes, yes, totally. Is it OK if you get uniqueness guarantees violatedl because two thigs got the same unique ID? Again, sometimes, almost-unique is enough.
Fo most people, log correlation is probably the biggest point, though.
Well, having run a Linux kernel on OpenBSD's vmm: they can drift more than a single day in that time. I did have to resort to using an ntpdate cron job because ntpd just couldn't cope with the time dilation effect. The cron job was configured at * * * * * (i.e. every minute), which roughly translated to once every 180 wall-clock seconds (+/- 30s).
Since developers rarely worked late and would give up when hitting the "random" test suite failure and go home, the bug persisted for months before the true cause was understood.
Our time zone was UTC-5, and the test suite contained a local-vs-UTC bug which only triggered between 7pm and midnight.
There's been a couple projects where if I pushed a commit between 11pm and 1am some tests would break. I'm pretty sure the issue is inconsistent use of UTC vs local time which during that period will differ by one day.
MSSQL not supporting a DATE data type has been the bane of my existence for a long time (of course, Postgresql has supported it since forever, but try telling your application vendor that). And since it took so long to include it, hardly any application uses it even today, because of inertia and compatibility with existing databases.
What is the best way to set or mock system time during testing?
BEGIN { # an execution block that runs when the module is compiled
*f = # assigns to the symbol "f" inside the symbol table ...
(localtime->wdayname eq 'Fri') # is it Friday? ? sub() {} # ... on friday an anonymous subroutine taking no arguments
: sub {}; # ... on every other day a subrouting taking arguments
} # closes the execution block# The following line executes the subroutine "f" and divides the result by 1 on fridays
# but on other days causes the interpreter to trigger a syntax error because it expects
# f to take an argument list "(...)" but receives an operator "/".
f/1;#/+
What is the "#/+" for?
I am sure this is not necessary for the behavior described. It's just a comment.
BTW: I am working for a company still maintaining a large Perl code base and I think this is rather obscure stuff. You rarely need to run something at compile time, hack the symbol table and you almost never need "sub() {}" to define subroutines with an empty "prototype". If I saw this in a code review I would most definitely had a closer look and would decline a PR if that was unnecessary. Meta programming should not be done lightheartedly and better serves a serious purpose.
Having said that, nothing could touch mod_perl performance under Apache in the late 90s. Massive web apps were built on this including eToys dot com where I worked before it imploded in the dot-com bust.
One of my frist dev jobs was slinging perl script, I loved that attitude and it made for a fantastic learning environment. I really dislike the feeling of having to write code in a straightjacket because tHe TeAm CaN't UnDeRsTaNd iT!!!
We minimized the negatives of TMTOWTDI by incorporating a house style, but it was flexible because we were all smart people whose minds didn't crash because we saw a statement we didn't expect.
Similar situation here. Perl is awsome and I'm still bitter that Python won. I mean significant whitespace - what great madness is this???
Anyway, yes, mod_perl ran the Web for about a decade in the early 2000s and I built and maintained huge websites which were all Perl on the back end.
BTW the book "Perl Best Practices" by Damian Conway [0] is the best general programming book I have ever read. Every software engineer should read it no matter what language they program in. So much good advice which applies to pretty much any significant software project. IMHO right up there with the "Mythical Man Month", though obviously much more low level.
[0] https://www.oreilly.com/library/view/perl-best-practices/059...
I've moved on but still use it for simple scripting tasks despite doing my best to learn Python. I just feel so expressive and unconstrained writing it, for better or worse.
Otherwise, "f/1;#/+" is equivalent to something like:
f(/1;#/ +)
where /1;#/ is a regexp matching operator, and stray + triggers syntax error.> # but on other days causes the interpreter to trigger a syntax error
You have this the opposite way round to what's happening (and the description). It only compiles on a Friday, i.e. when the sub takes no arguments.
Removing all the "only on a Friday" stuff boils down to the difference between these two:
# this compiles
sub f() {}
f/1;#/+
# this doesn't compile
sub f {}
f/1;#/+
NB. the latter does compile if you take the `+` off the end, when it parses to (using -MO=Deparse) sub f {}
f /1;#/;
When the sub is declared with zero args, `f` can only compile to mean "call f with no args", thus `/1;#+` means "divide the return value by one, end statement, rest is a comment".However, when the sub does accept args, `f <stuff>` tries to compile `<stuff>` into arguments to send - and it's a regex pattern `/.../` so the # isn't a comment, it's part of the regex, and the + is where modifiers go - but it's invalid, and is the syntax error. A valid pattern modifier would also compile, e.g.:
sub f {}
f/1;#/m
The argument to `f` here is "whether the pattern /1;#/ matched against $_": use feature 'say';
sub f { shift ? "matched" : "unmatched" }
$_ = 'this matches 1;#';
say f /1;#/;
$_ = 'this does not match';
say f /1;#/;
Which prints: matched
unmatched
So, it turns out the + needn't be characterised as an invalid modifier after all - it's just addition with a missing second operand. Add one in and it works as you'd expect: use feature 'say';
sub f { int shift }
$_='1;#';
say f/1;#/+1 # prints 2Isn't that what the parent said, going by your quote? They said "the following line [...] on fridays but on other days causes the interpreter to trigger a syntax error".
If you add a 1 to the end of the "f/1;#/+" line, so that it becomes "f/1;#/+1", the code will become correct.
What's actually happening is that on Fridays, the "f/1;#/+" line means "call f, divide the result by 1, and throw away the result", followed by a comment. On any other day, it means "call f with the regex parameter /1;#/ and add an unspecified parameter to it". Since the right hand side of the + operator isn't specified, it is syntactically incorrect.
: sub {};
Don't all perl subroutines allow any number of arguments (including 0) to be passed?Does the fact that the above is an anonymous subroutine affect the answer to my question?
sub() {}
does not try to parse the /1;#/+ as an argument because the () indicates there are no arguments (although they could be explicitly included if f were called with (), whereas this: sub f {}
does try to parse the /1;#/+ as an argument because that's perl's behavior if () are omitted? if day_of_week is "Friday"
eval(piece_of_incorrect_code)
else
do_sane_operationBut because Perl is parsed as it is executed, incorrect code raises syntax error only when it is reached by execution.
if [ $(date +%u) -ne 5 ]
then
exit
fi
(Add `sleep 1`, and detect pause on server. Then, if pause detected - serve attack payload. If not - somebody is careful enough to download and audit, so serve just the script.
Discussed on HN:
2020: https://news.ycombinator.com/item?id=25356757 (133 comments)
2018: https://news.ycombinator.com/item?id=17636032 (146 comments)
2016: https://news.ycombinator.com/item?id=11532599 (122 comments)
But that's not bulletproof; consider this code (adapted from <https://hal.archives-ouvertes.fr/hal-01513750/document>):
if [ $(date +%u) -eq 5 ]
then
alias maybe=''
else
alias maybe=:
fi
maybe for x in; do :; done
"sh -n" always reports syntax error, even thought the script syntax is correct on Fridays.The real Perl parser disambiguates with heuristics and run-time hints.
There exists an unambiguous subset of Perl syntax that is expressible with a BNF grammar, and such is amenable to all parsers. http://p3rl.org/standard#DESCRIPTION
This goes beyond mere Lisp macros, in that ordinary Lisp macro invocations still look like Lisp lists, while with this you can make arbitrary changes to the syntax, you could even make Common Lisp look like Pascal (if you really wanted to)
The designers of Scheme intentionally left this feature out (which was also found in some of Common Lisp’s ancestors, such as Maclisp), but some Scheme implementations/descendants included it anyway (as an extension), such as Racket, Guile and Chicken Scheme.
the following code produces a syntax error if the time is less than 10 seconds after a full minute.
#define T __TIME__
#if T[6]-48
#define X 1
#else
#define X /
#endif
void main()
{
write("%d, %c, %O\n", X, T[6], T);
}That's not generally true for Perl. The BEGIN block is used to get in that state here. "Some incorrect code raises syntax error only when it is reached" is true.
It's generating this on Fridays:
&f() / 1;
And this on other days: f(/1;#/+);
If you run the same code, but without BEGIN blocking the assignment to *f, it isn't incorrect code. It evaluates as: 'f' / 1;Does the parse that happens on Thursday take precedence or is it reparsed every single time through the loop?
while (1) {
BEGIN {print "hello\n"}
sleep 1;
}
Will only print "hello" one time.You could loop inside the BEGIN block and then drop out of the loop at some point. If you dropped out on friday, after the code assigning *f, it would run correctly. So:
BEGIN {
sleep 86400;
*f = (localtime->wdayname eq 'Fri')
? sub() {}
: sub {};
}
f/1;#/+
Would run correctly if you started it on Thursday. p 'Friday!' if Time.now.wday==5 || hLooking forward to the next time we can have steaks and Scotch.
Sorry, but you're dead wrong. Perl is not parsed as it is executed, which can be verified easily by writing a program with a syntax error at the end, and seeing that it doesn't run code at the top. Try it with the following program.
print "Hello, world\n";
This line raises a syntax error before the previous line tries to execute.
What is going on is that BEGIN blocks are special, they are executed as soon as they are parsed. With them we can interleave parsing and execution.In this case we're assigning to a symbol. And then the parsing of the final line is dependent on whether or not that symbol has a prototype. See https://perldoc.perl.org/perlsub#Prototypes for what Perl prototypes are, and to see why they would affect parsing.
You can't even decide if a particular "*" is multiplication or a pointer dereference until you resolve templates, which is Turing-complete.
In theory I agree with you that this is horribly ugly - I very much prefer simple grammars (the irony being my favourite language to use is Ruby, which has an atrocious grammar). In practice these parses are rarely that hard to resolve.
You cannot even construct the full parse tree before evaluating a mini-language that is Turing complete. Related article: why C++ compilation is slow (https://digitalmars.com/articles/b54.html)
So the right implementation choice, for the compiler, is to bull ahead with the common case, and throw the result away and try the other path if you get to an inconsistency. Such inconsistencies practically never happened in production code anyway, and where they did it most usually was, and now almost always is, a result of a bad edit.
So the practical expense is keeping and then GCing old state as it expires, which reduces to stack-like memory management, a solved problem.
We have not needed to go to JITting compile-time constructs, (though it could still happen to expedite constexpr evaluation). Rust might need to, soon, to accommodate parsing under influence of macros, a chore growing with time, not shrinking. (I.e., produce a new parser for each macro added, and jump into it to finish the parse.)
A handful of curated weird stories like this: https://500mile.email/
I had a "switch(moon_phase)" and forgot one of the cases... And the Android app was in debug mode, so any uncaught exception brought the whole thing down.
I ended up getting the known correct unixtime from the server to try to guess which timezone offset the user actually meant and correcting the result of System.currentTimeMillis() and my formatting for that.
Not sure where you are located, but this can be particularly bad in the UK where winter time happens to coincide with UTC.
There may be some niche astronomical or legal(?) applications which have to keep track of the potential 0.9 seconds skew, but my impression is that any app which claims to support a "GMT" (or Europe/London) option is actually only supporting "GMT-ish".
Are you sure? Looking into things I would have said that GMT exists as a time reference but all the time zones are based on UTC.
To fix this they ran a SQL script that changed the end date on the DST switch over but that had some poor assumptions in it and made it worse.
It also had to be correct in the past and the future.
I just rewrote the whole fucking thing from scratch.
It turns out we never did a release before 10:00, because it ran fine for a few years, but the regex constructing the filename could not deal with hours consisting of 1 number.
Oh well, never write production shell scripts while your platform is burning.
The fix is literally 1 character, but I only get access when production burns. So I was not allowed to change it and the owner does not want to implement it and file all release paperwork it entails. Instead, mgmt forbid this type of release before 10:00.
Is the proof, that perl is capable of this behaviour?! I don't get it - what is the point?
I read all the comments and still don't get it. What am i missing?
they only follow the non-defective side one day a week
because perl doesn't forward analyze, it never sees the error on other days, and doesn't fail
the presentation is false. this code is always wrong. it's just that the bug is only visible under certain conditions.
What's being noted is the degree of flexibility that Perl has with respect to its execution environment.
Though if you include preprocessors, you expand the number of languages for which you can do something similar.
It's not a runtime error but a compile time error. Because yes otherwise I would agree that it's doable easily in every single language.
enum timestamp = __TIMESTAMP__;
void main() { pragma(msg, timestamp[$-6..$-5]); auto a = 4; static if(timestamp[$-6..$-5] == "4") { auto a = 7; } }
What will happen when the command is `perl -c friday.pm`?
Note that the error message is "friday.pm had compilation errors, so yes, there's a compilation phase. It's not compiled to the machine code, though.
f/1;#/+
is parsed differently depending on the day. On Friday it's [ "f", "/1;#/", "+" ]
Other days it's parsed as [ "f", "/", "1", ";" ].Perl is parsing /1;#/ is a regex based on whether f takes arguments. Otherwise it parses the slash as division and then the hash is a comment.
Another example could be:
foo() {
some_func / 1;#/; die "It died"
foo
}
Which depends on some_func to not halt. some_func could come from another file, set by whoever invokes the current file. Whatever, it's determed at runtime.This leads to a proof that perl is a actually impossible to parse.
https://www.perlmonks.org/?node_id=663393
Which means you can't do a lot of static tooling on perl because you just can't analyses the source accurately. (Compared to say python, which parses source before running it)
And good things in Perl don't get noticed because they Just Work.
(Tongue-in-cheek, I’m actually rather partial to Perl.)
I'd wager that all code that "just works", in any language, looks bland and predictable.
But I'd rather have that than cute and clever any day.
First they should try Perl as if it was a tuned up AWK, and then, read the Orelly Perl books as if they were a religion.
perl -e 'print "foo\n" for 1..99999; print "bar\n";' > foobar.txt
constant &s = (now.DateTime.day-of-week == 7) ?? sub () {} !! sub (|) {};
constant c = s(42);
Constants are evaluated at compile time. Here we define a sub that takes no argument or any number of arguments depending on the day of the week (In Raku we start at 1 for monday to avoid ending the universe by deviding by 0). While binding the return value of s(42) to a constant we force execution of that sub at compile time. Thusly, we create a program that can not be executed when compiled on sundays. This is reasonalbe, because by heavenly mandate we are bound to rest on that day.Modules are precompiled, programs are not (yet). So to make this really depend on the day of compilation we have to put it into a module (for now).
Perl Cannot Be Parsed: A Formal Proof (2008) https://news.ycombinator.com/item?id=5770531
If it could not be parsed it could never work!
And this is totally OK in my book, I use Perl for prototyping ideas and an expressive language that allows creativity from me the programmer is a feature as it promotes looking at a problem from multiple aspects.
If you're going to be pedantic, it's important to be correct; there's no such thing as dynamic parsing.
> If it could not be parsed it could never work!
What's the logic here? An interpreter doesn't have to operate on an AST, a language doesn't have to have a nontrivial grammar (consider Brainfuck). Perl is just a language in the same category: it doesn't have a (true) nontrivial AST, it doesn't have a (true) nontrivial grammar, and it can't be (truly, nontrivially) parsed.
As long as we're indulging in pedantry, this is incorrect presuming dynamic means what it usually means. Any regular expression constructed at runtime contradicts you.
Modifying your core parser at runtime though, who would do such a thing? Other than reader macros in Common Lisp I mean. Are we going to argue this means Common Lisp doesn't parse if we add reader macros at runtime? A peculiar argument.
Now. Should we talk about whether doing that is a good idea? A lovely and separate conversation. I agree with GP that since Perl will spit out a parse tree, it does in fact parse, and since it's not static, well, it's dynamic.
Which we can prove by, I dunno, writing some Perl which only parses on Fridays. Right?
Peculiar how? Isn't that just common sense?
Perl? Its numerous language extensions allowed it to stay competitive for such a long time. Every time someone says "language X can do Y but Perl doesn't have that syntax", a new CPAN module is born to amend the shortcoming.
Once we had a client from the Netherlands that complained about a bug that we could not reproduce. We tried to reproduce it by simulating his position, but to no avail. Turns out he was in a very specific, slightly depressed, area that was a few centimetres below mean sea level and that the code didn't like negative altitudes.
Another one only happened in the southern hemisphere. Given we were based in Europe and it was winter, we asked whether we could do some more tests in Australia to make sure there were no more bugs, but sadly it was denied.
You guessed it: There was a sleeper bug detonated by the date change. Pain the butt to track down in Perl code that was generating an Excel spreadsheet cell-by-cell.
What date to change to? Far into the future? Far into the past? To a weekend? To a DST change? To Friday the 13th? To a US holiday? To a Muslim, Jewish, Chinese, Soviet, Albanian holiday? To a particular weekday? To Hitler's birthday? What if it's Lenin's? To that one day my coworker was depressed because the boss denied him a raise and talked shit to him? Too many possibilities. And your timebomb might be in one of your dependencies to boot.
Worked for a few years at a company with a medium-sized codebase whose tests only worked before 6PM.
(Run the tests later than that, and you wind up with some timeframes that span calendar days and therefore break things. Was the underlying code broken, or the tests, or both? Who knows!!)
Was never allowed to fix and investigate. Or to be specific, we were never given the time to fix it. Management simply didn't see it as a problem.
In my younger days I would have gone all HERO HACKER and fixed it anyway on my own time. But now, I know how that turns out. You waste a bunch of evenings fixing it, and usually introduce some other regressions, and you get zero glory and lots of blame. Best case scenario is no regressions and zero glory.
So, screw it. Old-and-jaded me never lifted a finger. If the company doesn't care, why should I?
But, that is an excellent protip. If you are working on a team that actually values things that work. Time/date dependent code is very tricky to get right, especially when multiple time zones are involved. If your test suite doesn't cover multiple possibilities, your code is nearly guaranteed to be wrong.
It's not even like you have to write brand-new, cleverer tests. Just "brute-force" it, assuming your tests are performant:
before:
some_tests
after: [time_zone_1, time_zone_2, time_zone_3].each do |tz|
[time_of_day1, time_of_day2, time_of_day3].each do |tod|
some_tests(tz, tod)
end
end
...that kind of thing. That's what I did when I wrote new code at that company, even though I refused to fix the legacy stuff. My tests were faster, too. Almost like I knew what I was doing...It parsed a string containing the time and sscanf treats "08" and "09" as invalid octal numbers. Argh.
Also once fixed an inherited awk script that malfunctioned at the end of year: a hardcoded month name array with November missing. To this day I always populate any such lookup with a loop and strftime() or appropriate.
$ gcc weird-0.c
$ ./a.out
No output from weird-0 $ gcc weird-1.c
$ ./a.out
foo(42)
Output from weird-1. What is weird-0? $ cat weird-0.c
#include <stdio.h>
void foo(int x)
{
printf("foo(%d)\n", x);
}
int x = 42;
int main(void)
{
#if __LINE__ % 2 == 0
typedef int foo;
#endif
foo(x);
return 0;
}
OK, how does weird-1 differ from weird-0? One blank line! $ diff -u weird-0.c weird-1.c
--- weird-0.c 2022-02-16 12:09:29.565563802 -0800
+++ weird-1.c 2022-02-16 12:09:38.185718722 -0800
@@ -7,6 +7,7 @@
int x = 42;
+
int main(void)
{
#if __LINE__ % 2 == 0
Explanation: {
#if __LINE__ % 2 == 0 // If this line number is even
typedef int foo; // foo is a typedef name for int
#endif
foo(x);
return 0;
}
If foo is a typedef name for int, then "foo(x)" is syntactically declaration. It's declaring a local variable x to be of type foo, alias for int.If the typedef is missing, then foo(x) refers to the file-scope identifiers: the function foo and variable x. It is a call to foo passing the value 42 of x.
The preprocessor conditional could easily introduce a syntax error, but the issue in Perl is that a parsing decision (how the / token is lexically categorized) is made based on the compile-time value assigned to f. This is similar to the compile-time meaning of foo we are setting up with the presence or absence of the typedef.
$ gcc -O2 -Wall -W weird-0.c
weird-0.c: In function ‘main’:
weird-0.c:15:7: warning: unused variable ‘x’ [-Wunused-variable]
foo(x);
^
$ gcc -O2 -Wall -W weird-1.c
weird-1.c: In function ‘main’:
weird-1.c:16:3: error: too few arguments to function ‘foo’
foo(x);
^~~
weird-1.c:3:6: note: declared here
void foo(int x, int y)
^~~
The change is in the foo function: void foo(int x, int y)
{
printf("foo(%d, %d)\n", x, y);
}
The call is now erroneous, and with the increased compiler optimization level and diagnostics, the declaration case diagnoses x being unusehttps://rt-archive.perl.org/perl5/Ticket/Display.html?id=130...
Randal has been doing nothing but this for at least 20 years.
It is merely that the problem branch is only occasionally followed.
The code is never syntactically correct however the syntax error is not identified by the Perl interpreter on Fridays.
</pedantic>
Yes, apparently.
import datetime
if datetime.datetime.now().weekday() == 3:
1/0