Ha. I had an intern who wrote code that blew up if the timestamp of our DB server was ever >= 1 second after the timestamp of our app server.
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.
Personally I really don't like code that deals with time - it's too easy to place a "get time stamp" call anywhere in code. If it's down in the bowels of some piece of functionality trying to test it becomes a headache.
This is one of those pieces of software engineering wisdom that only comes from experience. Try explaining to a newish dev why they shouldn't `const now = Date.now()` or whatever anywhere they want, but rather do it in one place for the lifecycle and pass it around, and they will look at you funny and think it's a waste of time. Classic case of simple vs easy. Time has the potential to complect everything it touches.
Oh yes. Any time I see code that tries to do some kind of scheduling, the first thing I ask is if they have considered using a library someone else has written. Way too many things can subtly go wrong, but new folks tend to think it should be easy, because how hard can it be to work with dates?
There are seven time zones in Indiana in the tz database[1] - this doesn't count the two most used ones America/Chicago and America/New_York there's also a Kentucky tz that slightly spills into the state. Whenever anyone makes any comment about dates and times being easy to compute I just mention that yes, there are seven time zones in Indiana. Now the actual issues tend to arise when software makes assumptions like "every second will actually occur", "each second only happens once", "every minute has 60 seconds", "every day has 86400 seconds" amongst a plethora of other bad assumptions... but those are a lot more complicated to explain. I like leaning on Indiana.
One realises that one’s time is relative anyway and any universal time is just one’s own will to join a collective agreement that one doesn’t have to abide by. Then one puts this notion aside and prefers not to worry oneself with such things.
Uh. No. In the northern hemisphere, winter is January for centuries and centuries. Almanacs that farmers use also agree with that, so we have food to eat. Reality is much more than just rich first-world comforts. The world is deadly, and we use calendars and time-keeping as a basic survival skill. So basic, you probably don’t even realize it’s there.
There's a difference here between time as a fundamental property of the universe, and "time", which is how we relate a bunch of physical phenomenon to that fundamental force.
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.
I take it you don’t work remotely with a global team or been homeless? I understand your philosophy and largely agree with it. However reality is much more nuanced.
I've been bit by bugs when coming to moment.js from other languages. It's the only datetime library I knew of, before you'll tell me about all the others, that mutates the datetime object when you do arithmetic.
That's a good starting point, and certainly good advice for marking current time and past times. But keep in mind that future event absolute UTC can change due to politics, e.g. a change in daylight savings rules, which is less rare than you might imagine (https://en.m.wikipedia.org/wiki/Daylight_saving_time_by_coun...). That concert scheduled a year from now at 8pm in UTC+5 might actually end up occurring at 8pm in UTC+6.
I once had briefly traveled into a different time zone (so briefly that I hadn't thought about it) when I received a phone call asking to reschedule a doctor appointment. I entered the new time into my calendar. Then, I was an hour late to the appointment. My phone had adopted the timezone of the cell tower, and the calendar assumed I had meant "local time where I scheduled the appointment".
If DST changes and you live in a democracy, it's your fault. Vote against DST. If you don't get what you want unleash the wrath on your congress member, not the software.
Hmm, I think you should use TAI over UTC because UTC has leap-second corrections, Which means that the current time + X seconds might not actually be X seconds from now
I learned this the hard way. Now I wrap interactions with system time in a container class so that, if need be, I can finely control time inside my app in unit tests and debugging.
It's also one of those things that actually is simple in restricted cases, then when you try to generalise it blows up badly, and it blows up like a time bomb rather than a landmine. When a time bomb explodes you don't quite know when it was triggered, but you have to deal with it then and there, learning a heck of a lot about time libs in general before you can proceed. What's a leap second, which years have a leap day, are time zones connected to places somehow, and so on. Just so you can fix your calendar that's part of a larger app.
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 pass around a time provider module, which you can (and should) mock in tests to ensure that nothing catastrophic happens if time drifts in ways you don't really expect.
A cronjob to sync the time instead of a proper NTP client doesn't sound that good either. I know that each place has its own weird things because historical reasons but NTP is quite ancient.
Our sysadmin was being paranoid about how many memory-resident programs we ran. he figured time syncing wasn't important enough to justify a daemon, so he just ran the ntpdate command on a daily schedule. It wasn't that bad - after all, how far can clocks drift in a single day!? ;-)
For security? Kerberos usually has a 5 minute tolerance. Are you saying that's wrong? Because if your hardware/firmware isn't literally broken you won't drift anywhere near that in a day.
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.
The bigger the clock skew, the harder it is to correlate events from logs. The harder it is to corelate, the more information needs to come from your machine-fueled winnowing stage, to the final inspection by eyeballs.
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.
"It makes logs more annoying, and sometimes security needs logs" is a pretty weak connection, though. And it's a far cry from precise timing being "THE most important thing on a server". Is there anything more direct at all?
It's basically a whole bunch of small "make it harder to correlate" issues. If you have a distributed system (which you probably do, if you care about time to the point of considering if daily ntpdate or continually running ntp-or-equivalent is better), you will probably end up with timestamps somewhere in a protocol.
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.
after all, how far can clocks drift in a single day!?
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).
Delivering real functionality in production (with code review from your mentor, of course) is absolutely standard at a Big Tech or hot startup internship.
This was the early 2000s, at a very small startup funded privately by a non-technical guy, and most of the technical staff was under 25. It's a big world, and best practices are not universal.
I once had to implement date picker that returned the date to be sent to backend to fetch the day's reservations. It seemed to work fine the day I implemented it. Next day it broke. Turned out the widget returned MM/DD/YYYY instead of DD/MM/YYYY.
And I implemented it on 10th of October.
Aliens landed on this planet, found out about the date format MM/DD/YYYY, and left in a hurry. They are currently preparing a gamma ray burst out of pure compassion and pity but also a hint of disgust!
Is that date format used anywhere outside the US? We have always used YYYY-MM-DD. Which is very nice, if for nothing you can use a single string sort if for whatever reason need to sort them.
Yeah, me too. I had severe time constraints (heh) and just wanted to be done with it with minimal conversion. Which I then had to do anyway later. I also learned on that day that Javascript's getUTCMonth() is zero-based (January is month 0). And I had to sync time between timezones as well. I don't want to work with dates ever again.
I've learned the hard way that this is also dependent on the user's date format set on their computer in some libraries(iirc it was jquery ui date picker a few years back). its liberating when you decide to throw it all out and just use integers for all things not facing an end user.
We had situation where if you worked late, the test suite would consistently fail.
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.
It is. I'm in London which means that our time is +/- 1 hour of UTC depending on daylight saving time.
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.
One of the perils of working in the UK is _assuming_ that you've been using UTC timestamps this whole time, and then summer rolls around and suddenly a bunch of tests start failing, and all your production logs are an hour out because you were wrong, and actually used local timestamps everywhere. Oops.
I worked with date range in a service that expected start date to be inclusive, but end date to be exclusive. So when you wanted to get data from dates 12-15, you'd have to send query for 12-16.
That sounds like a naive date->datetime conversion rather than explicit boundary exclusion. If you convert a date to a datetime, you get midnight at the start of day, which is fine if you're only comparing dates, but fails spectacularly in the way you describe for date ranges.
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.
I had a coworker working on a data-pipeline sort of thing once. They for some reason wrote their own date parser (Akamai logs) and used a regular expression to find the month. For whatever reason, the RegExp didn't work for March (I think) and so at midnight 1 March the year of first deploy, we started dropping all the logs as un-parseable (causing the NOC to think the traffic had gone to 0 or our system was buggy). We all got paged and eventually figured out it. It had been running in live for several months without problems at that time.
Fantastic. We had a similar problem with test code that always worked on a remote team's computers during their work day but failed when we got into the office and tried to run it in a different timezone.
Reminds me of an app I worked on a while ago where both the server it was running on and the datetimes in the database had to be in a specific time zone. Change either (including changing both to an identical but different TZ) and the app just failed to run.
I've seen something similar where some automated unit tests always passed and when I tried to run it locally would always fail. After digging around I realized it was making assumptions about the timezone it ran in, which was on the east coast, whereas I was on the west.
I've had to fix any number of test suite bugs as well as regressions that happened because the people who wrote the tests had ensured the test suite didn't (most of the time) test what they thought it would by relying on date arithmetic from current date and time...
Many people think that test suite authoring is easy mode. It really isn't. Making a good test suite that actually does a good job of tracking invariants is a royal pain.
As far as I'm concerned, writing tests is a different skillset and its often underdeveloped. It then snowballs into hating testing, because all you've experienced is a poor test suite that gets worse over time. It's hard to do well as you say, and it also gets glossed over in code review as long as the tests pass. Do the tests actually verify anything useful? Who knows, build is green!
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.
My favourite is the test suite where each test relies on data from every other test, so running a test in isolation fails, and adding a new test is almost impossible without breaking a bunch of other tests.
Kind of sort of. The catch here is that because runtime is compile time with Perl, it's not exactly the same. Looking at the code, it's kind of like having something like
#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")
}
One perhaps apocryphal story that's kicking around out there was that Draper Labs had a hard-real-time navigation system which... took too much time computing something when the moon was full.
Saw some tests that had the same pattern. Passed half the day, and failed the other half of the day. I assume it always worked during India work hours.
Fortunately we rarely use 12h clocks where I live. But that opens up other avenues: if you are not much of an early riser, a build script failing to deal with single digit hours can remain undetected for a very long time.
It could have been much worse. At least you didn't have a leap year or timezone bug. I've exterminated more than one of those in my time as an engineer, and it's never really any fun.
> # 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
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 2
> 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.
Isn'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".
This is generally correct, but your final analysis is slightly wrong and this is a perfect example of why Perl can be difficult to parse.
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.
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?
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.
Worked as a Perl dev for 15 years and enjoyed it. The hardest thing was the number of ways to do OO Perl using Perl5. Everyone has their own flavor. And TMTOWTDI was a motto the community was proud of.
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.
> Worked as a Perl dev for 15 years and enjoyed it
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.
The Damian was a phenomenon of pure inspiration. In fact Perl community in general had a pantheon of acolytes I've never seen in any other programming community. Ruby has a few but nowhere near as many as Perl. Perl in the early 2000s really was on fire.
> Worked as a Perl dev for 15 years and enjoyed it. The hardest thing was the number of ways to do OO Perl using Perl5. Everyone has their own flavor. And TMTOWTDI was a motto the community was proud of.
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.
Perl makes you think differently. True for all languages I guess, but maybe a little truer for Perl than most.
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.
Right, the beauty of this is that it's a syntax error he's produced. Sure any language can write code to produce a runtime error under any condition at all. And a lot of languages have features that allow them to run code at compile time, and similarly produce errors then too. But those languages don't allow you to ruin the syntax of the language. I guess it's something that requires macros. Would it be safe to say that any language with macros can do this? Or is there something even more interesting with perl going on here?
Common Lisp is another language in which this should be possible. Common Lisp allows you to run arbitrary code at compile-time, and that code is allowed to modify the language syntax (*READTABLE*, SET-MACRO-CHARACTER, etc). So code could make itself syntactically invalid on Fridays by changing the language syntax depending on the day of the week.
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.
Bash even reads the file as it goes, so if you run a "long-running" script (a sleep is enough), edit far enough down, and write the file again, the previously started bash will end up running the new content once it gets up to reading where the change happened.
You can exploit it do distinguish whenever script is `curl | bash`'ed.
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.
It can be externally parsed with ambiguities. The article is written by someone who authored such a parsing toolkit about three years later. Despite what you may have learnt, an ambiguous parse tree is still a useful thing to have, we can build tools taking it into account, also most existing tools can be modified in a straightforward fashion to make use of the extra nodes.
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
But because Perl is parsed as it is executed, incorrect code raises syntax error only when it is reached by execution.
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.
>incorrect code raises syntax error only when it is reached by execution.
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:
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.
Perl is not parsed as it is executed. It is compiled to opcodes then run. The BEGIN block, however, explicitly runs code at compile time. It is literally compiling different code on different days, then attempting to run it.
Compilers decreasingly encounter parses that depend on template instantiation, as users move to new constexpr alternatives to template metaprogramming.
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.)
Unless something has changed a lot, C++ templates at least only used to be Turing complete if you assume you're allowed unlimited recursive template applications, and compilers were free to restrict that (and did). Has that changed?
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.
It's not the undecidability itself that is the problem though. The undecidability is just an indicator how horribly complicated C++ is to compile.
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)
I have a half-finished Ruby compiler sitting around, so I'm a bit jaded when it comes to how complicated something is to compile. C++ is complex, but at least it's reasonable well specified.
I have written code that only works in winter because of daylight saving times and some unfortunate date conversions, but having a temporary correct syntax is really a new high.
As a part of a release always got borked, I wrote a small bash script that took currently deployed files, archived them to rollback_dateandtime.tgz, then put the freshly released files in its place.
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.
I have written code (at work) that crashes when the moon is in a specific phase.
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.
Not nearly as cursed as two parent comments, but there was a widespread problem in my very popular Android app. The relative date formatting function worked incorrectly for many users, it was off by one hour. It turns out that when Russia (where most of our users were) had abolished DST, and then changed timezones a couple of times, people got really confused. Since Android's timezone data is part of the system, it was basically set in stone. Most of the users didn't choose the timezone with the correct offset, but instead set their time an hour forward so the incorrect timezone and incorrect unixtime cancel each other out for a "correct" displayed time.
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.
I have once heard of some software that would crash on some Wednesdays. Apparently Wednesday is the longest day name, and it was overflowing some buffer. But only if the day of the month was 10 or above, because the strings 1-9 still fit in the buffer.
Yeah it’s horrible. I had a contract a number of years ago to fix a bug that they had been fighting for 5 years. When winter time kicked in all appointments on their booking system collapsed to zero minutes long.
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.
Let's be pedantic about this, because this is one of the very few areas where pedantry matters: GMT ("British Winter Time") is NOT UTC, and it can differ from UTC up to 0.9 seconds. GMT is a time zone (corresponding to UT1). UTC is a time standard.
Indeed. I get invites from people from the UK who simply refer to the current local time (GMT or BST) as "GMT". It just means current London time to them.
I appreciate your pedantry, and I wonder if you know of a widely used computer system which genuinely implements the GMT time zone as something different from UTC+0.
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".
Ok, fair enough. But it's close enough that it's very easy for anything that isn't working with second-level granularity to seem like they're working only to break as soon as the time switches BST.
But isn’t this possible with any language that has a pre-compilation step? Perl is certainly conflating its passes (as explained by other comments above), but in rust you could add a check in build.rs or use macros in C to accomplish the same, right?
You can write code in any language that fails at runtime depending on the day. What's notable about this is that its _syntactical correctness_ depends on the day, which is not possible to achieve in many languages.
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.
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.
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)
Was going to say the same thing. I thought it would be something wrong with the language. If you're going to write code that's different depending on the day of the week...meh.
Because with its declining popularity, most people here are unfamiliar with the language except for its meme status as "that weird line noise language". Things that confirm pre-existing beliefs are easy to accept and upvote, whereas good Perl code would require (a) some actual thinking, and (b) challenging the socially accepted narrative - both of which are relatively much harder.
And a lot of people just can't get over significant whitespace, and curly braces, and lispy parantheses, and semicolons. No syntax can please everyone, especially so with people unfamiliar with the language they're looking at.
Afaik Perl is not declining in popularity, it's just not growing in popularity as fast as many other languages, so it's market share shrinks while the number of users it has grows.
I loathe perl still (based on having to SSH (or telnet?) into people's systems and debug their perl scripts in the 90s) but, Higher Order Perl, https://hop.perl.plover.com/ is a truly excellent introduction to the power of functions that generate functions, more accessible and more immediately useful than Paul Graham's On Lisp. The lessons are immediately applicable at least to Javascript and Python. Strongly typed things like Go make it a bit more work, but still you'll understand why the work is worth doing.
The other day, I wanted to check if a text file's 100,000th word was being indexed, so I generated a text file with 99,999 lines containing "foo" and one line with "bar" with this line in bash
perl -e 'print "foo\n" for 1..99999; print "bar\n";' > foobar.txt
As a sister language of Perl, Raku can do the same trick.
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).
Well, to be a little pedantic, Perl cannot be statically parsed, since some constructs require runtime context.
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.
> Well, to be a little pedantic, Perl cannot be statically parsed, since some constructs require runtime context.
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?
> Modifying your core parser at runtime though, who would do such a thing?
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.
I have seen some code that would fail based not on time but location.
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.
I worked with Randal L. Schwartz for several years in the Los Angeles area. I was already doing Perl but getting an opportunity to see his code and ask him questions was something that changed my career for the better. Thanks Merlyn!
Just reading the headline gave me traumatic flashback to a customer project for a winter sports brand that broke on April 1st after not being modified for months prior to this.
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.
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...
The thing is, you need to have a pretty corkscrew mind to think about cases like this.
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.
Been there, a TCL web script, IIRC. This led to religious processing of input with "regsub" (this predated TCL 8). Yuck.
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.
{
#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.
With a small change to the weird-0 and weird-1 programs:
$ 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)
^~~
I miss Perl and it’s community from the early-mid 2000s. Sure it was bad for those who have to maintain but trying to create the shorted and most unreadable possible was a fun pastime.
Am I literally the only person who sees the pun against “His Girl Friday?” Maybe it wasn’t intentional but that’s literally the first thing I thought of.
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.