What Time is Midnight?
Yesterday was time change in the U. S. of A. I pulled out my iPod Touch to update a Numbers spreadsheet, and hit the "today" button to put the current date in the date column. But when I did that I got not the current date but 11 PM the previous day. Today it works as advertised.
Now, I am not privy to the internals here, but this behavior would be explained if "today" were implemented by the Objective C (or Swift, or whatever) equivalent of the following Perl:
my $date = time + $zone_offset; $date -= $date % 86400;
This code is clean, simple, obvious ... and subtly wrong because on time change day, after the change, midnight has a different offset than the current time. This is a bug that manifests only 44 hours in every year. I think that when I want the local day I round-trip through localtime and Time::Local::timelocal. But do I really? Always? And would the above snippet be valid for UTC?
I just did ack '%\s*86000\b'
on my Perl modules, and came up dry. Then I looked for '=>\s*86000;'
to find relevant manifest constants (three -- I'm considering picking a name and converging all modules to it), and find the modulo operations on them. Still nothing. Am I really clean? What about you?
A particularly tricky one because it's a case where the timezone *offset* changes depending what time of day it is. So only a module like DateTime with a complete timezone implementation can make it "just work":
my $date = DateTime->new(year => 2017, month => 11, day => 5, hour => 15, time_zone => 'America/New_York'); # sometime during the day
my $midnight = $date->clone->truncate(to => 'day');
say $midnight->epoch;
Of course if you're working in local time, you can take all of the local time pieces (year, month day, hour, etc), set the hour/minute/second to 0, and then use your localtime library of choice to get the epoch from those parameters. But I find that too tedious for everyday work.
The problem is that not every day has 86400 seconds in it. When calculating days between dates, use noon not midnight, as the time of day. If you must know it to the second, convert the date-times to their epochs, find the difference, and find the hours minutes, and seconds for that.
Well, according to https://en.wikipedia.org/wiki/Unix_time#Leap_seconds:
What I tend to do when I want the current local day with core Perl is something like
(from
eg/almanac
in the Astro::Coord::ECI distribution).This algorithm should not be generalized to years far from the current year, because of the way Time::Local interprets the year. I ran into the "far from the current year" issue when a correspondent wanted to use Astro::Coord::ECI to calculate the sunrise at the full moon nearest the summer solstice in 3000 BC. Which goes to show that when you publish software you never know what it will be used for.
Correct, leap seconds do not affect the unix epoch time, as they're ignored or skipped for that purpose. Time zone offset changes are another matter, since they're applied "on top" of the epoch time. So the simplest method is to do all your date math in UTC, and use the epoch. This doesn't help when you want to know when midnight is in local time though.
Is that a typo in your last paragraph, or did you really search for 23:53:40 (86000 seconds)?