I've found that some dates (at the moment I only found this error with some before-epoch dates) add an hour of difference from UTC.
Local time is GMT-3
First time I see it was in Javascript
> new Date("1969-07-26T03:00:00+00:00")
< Fri Jul 25 1969 23:00:00 GMT-0400 (-03) // why is it -0400?
> new Date("1963-07-26T03:00:00+00:00")
< Fri Jul 26 1963 00:00:00 GMT-0300 (-02)
Then I tried in Ruby and the same happened
irb(main):288:0> Time.parse("1969-07-26T03:00:00+00:00").localtime
=> 1969-07-25 23:00:00 -0400
But (maybe I did it wrong) doesn't happen in Python
In [12]: utc = datetime.fromisoformat("1969-07-26T03:00:00+00:00")
In [13]: utc.replace(tzinfo=tz.tzutc())
Out[13]: datetime.datetime(1969, 7, 26, 3, 0, tzinfo=tzutc())
In [14]: utc.astimezone(tz.tzlocal())
Out[14]: datetime.datetime(1969, 7, 26, 0, 0, tzinfo=tzlocal())
I haven't been able to find information about it. Anything to read about and how to handle those cases? For example 1963-07-26T03:00:00+00:00 works as expected.
Cheers!
Adding +00:00
to the end of your date string is creating a UTC offset. Since you're specifying zero hours, zero minutes, and zero seconds, it's creating a zero-offset from GMT, or in other-words, setting your timezone to exactly GMT.
For example, I'm in a GMT -5 timezone (US Central). When I create a new JS date using today's date at 6am, I get a subtraction of 5 hours (because GMT -5 is my timezone) :
> new Date("2022-06-08T06:00:00+00:00")
// Wed Jun 08 2022 01:00:00 GMT-0500 (Central Daylight Time)
However, if I omit the offset, then it works as expected, and I get the date in my local timezone.
> new Date("2022-06-08T06:00:00")
// Wed Jun 08 2022 06:00:00 GMT-0500 (Central Daylight Time)
I can also run your code examples and get the expected result.
> new Date("1969-07-26T03:00:00")
// Sat Jul 26 1969 03:00:00 GMT-0500 (Central Daylight Time)
See the section Date Time String Format in the ECMAScript specification for more details.
I believe the reason is that from Sun, 6 Apr, 1969 00:00 to Sun, 5 Oct 1969, 00:00 the America/Buenos_Aires
timezone observed a UTC offset of UTC-4h. So I don't think this is an error, rather it is reflecting what the UTC offsets were at this time.
Before 1 Oct, 1963 00:00 (from ~1946), the UTC offset was always 3 hours with no DST.
So the UTC offset in this timezone has varied over time, from UTC-2 to UTC-4, depending on the year and the date. Since Sun, 15 Mar, 2009, the UTC offset has been UTC-3.
I'll add more examples of the varying UTC offsets below, two from your original example and some more to illustrate the varying UTC offsets.
const timeZone = 'America/Buenos_Aires';
const fmt1 = new Intl.DateTimeFormat('en-GB', { timeStyle: "medium",
dateStyle: "medium", timeZone: 'UTC'});
const fmt2 = new Intl.DateTimeFormat('en-GB', { timeStyle: "long",
dateStyle: "medium", timeZone});
const examples = [
new Date("1962-07-26T03:00:00+00:00"),
new Date("1963-07-26T03:00:00+00:00"),
new Date("1964-07-26T03:00:00+00:00"),
new Date("1969-07-26T03:00:00+00:00"),
new Date("2008-10-19T03:00:00+00:00"),
new Date("2010-07-26T03:00:00+00:00"),
new Date("2020-07-26T03:00:00+00:00"),
];
console.log('UTC'.padEnd(25, ' '), 'Local time');
for(let dt of examples) {
console.log(fmt1.format(dt).padEnd(25, ' '), fmt2.format(dt));
}
.as-console-wrapper { max-height: 100% !important; }
Here is a list of the UTC offset changes for the timezone from 1950 onwards, this illustrates the fact that is has changed many times. until 2009 when it has remained at UTC-3.
let { IANAZone, DateTime } = luxon;
const timeZone = new IANAZone('America/Buenos_Aires');
let date = DateTime.fromISO('1950-01-01T03:00:00Z').setZone('UTC');
let endDate = DateTime.now();
let lastOffset = 0;
function formatUTCOffset(offsetMinutes) {
return (offsetMinutes / 60) + ':' + String(offsetMinutes % 60).padStart(2, '0');
}
console.log('Time'.padEnd(24, ' '), '\t', 'UTC Offset')
while (date < endDate) {
if (lastOffset !== timeZone.offset(date)) {
console.log(date.toISO(), '\t', formatUTCOffset(timeZone.offset(date)));
lastOffset = timeZone.offset(date);
}
date = date.plus( { day: 1 } );
}
.as-console-wrapper { max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.3.1/luxon.min.js" integrity="sha512-Nw0Abk+Ywwk5FzYTxtB70/xJRiCI0S2ORbXI3VBlFpKJ44LM6cW2WxIIolyKEOxOuMI90GIfXdlZRJepu7cczA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>