Hi,
When trying to compute a difference between two dates, I encountered a problem. Here a small program to show the issue:
echo (parse("1940-03-01", "yyyy-MM-dd") - parse("1940-02-01", "yyyy-MM-dd")).inDays
And the result is 28 instead of 29. For other years such as 1936 or 1944, the result is 29.
If I don’t convert in days, I get `4 weeks and 23 hours` instead of 4 weeks and 1 day. If I create the dates with initDateTime rather than parseTime I encounter the same problem except if I specify that the time zone is UTC.
Thus
let date1 = initDateTime(1, mFeb, year, 0, 0, 0, zone = utc())
let date2 = initDateTime(1, mMar, year, 0, 0, 0, zone = utc())
echo (date2 - date1).inDays
gives 29, but without the zone, it gives 28.
So, it seems that the local() is the reason of this missing hour. I don’t see any reason for this behavior. But maybe someone has an explanation.
Thanks all for your answers. I indeed live in France and I was aware that there are hour changes when France was occupied. But I didn’t know that there was a DST on 1940 February 25th (and in fact many DST before war; this is in fact an old and unpleasant idea).
So this makes sense… in this example.
But if I want to know how many days there are between 1902 January 1st and 1968 December 25th (which was my first goal), I get one day less than I expect. At first, I thought that it was this missing hour which was never recovered and was lost forever. I was wrong.
In France, in 1902, we lived at UTC + 09min 21s as I discovered (Paris meridian time), and without DST. In 1968, we lived at UTC+1 without DST. So there is actually 50min and 39s missing. This is what I get if I display the detailed result rather than the number of days.
If I want to avoid this problem, I have to compute the differences in UTC zone (which I finally did) or to use for the ending date a hour such 12:00:00, or to do introduce some rounding mechanism.
In my short example in 1940 it was indeed a DST problem, but in my full example this is a time zone problem. In trying to locate the problem by limiting the time range, I was wrong.
Yes, when dealing with time we have to be very careful.
And by the way, I didn’t know about https://www.timeanddate.com. This is a wonderful site.
In UTC - there is not DST (day light savings time). I think time zone library should be default UTC to make it easier on the programmer. I think nim's parse involving users local time zone is a really bad idea. In my timestamp/calendar/timezone lib ( https://github.com/treeform/chroma ) I do not do that:
Using my lib you get the correct number of days (or rather 24 hour chunks - days, like months can be any number of hours):
echo (dateFmt.parseCalendar("1940-03-01").ts.float64 - dateFmt.parseCalendar("1940-02-01").ts.float64) / (24 * 60 * 60)
-29.0
But if you where to take a time zone say "Europe/Paris" and try to count the number of 24 hour chunks that pass between them:
var
startCal = dateFmt.parseCalendar("1940-02-01")
endCal = dateFmt.parseCalendar("1940-03-01")
startCal.shiftTimezone("Europe/Paris")
endCal.shiftTimezone("Europe/Paris")
echo (startCal.ts.float64 - endCal.ts.float64) / (24 * 60 * 60)
You get
-28.95833333333333
Because there is a DST switch in the time zones.
Keep in mind its not correct to say that 28.95 days pass - because days are not 24hours... The number of days that passed is still 29 - its just some days , in this case 1 day was shorter.
If you run this monster DST finder:
var
startCal = dateFmt.parseCalendar("1940-02-01")
endCal = dateFmt.parseCalendar("1940-03-01")
startCal.shiftTimezone("Europe/Paris")
endCal.shiftTimezone("Europe/Paris")
var
day = 0
prevTs = startCal.ts
while true:
if startCal.ts >= endCal.ts:
break
startCal.add(Day, 1)
startCal.shiftTimezone(startCal.tzName)
var currTs = startCal.ts
echo day, ": ", (currTs.float64 - prevTs.float64) / 3600, " hours"
prevTs = currTs
inc day
You can see when the DST happens:
0: 24.0 hours 1940-02-02T00:00:00+00:00
1: 24.0 hours 1940-02-03T00:00:00+00:00
2: 24.0 hours 1940-02-04T00:00:00+00:00
3: 24.0 hours 1940-02-05T00:00:00+00:00
4: 24.0 hours 1940-02-06T00:00:00+00:00
5: 24.0 hours 1940-02-07T00:00:00+00:00
6: 24.0 hours 1940-02-08T00:00:00+00:00
7: 24.0 hours 1940-02-09T00:00:00+00:00
8: 24.0 hours 1940-02-10T00:00:00+00:00
9: 24.0 hours 1940-02-11T00:00:00+00:00
10: 24.0 hours 1940-02-12T00:00:00+00:00
11: 24.0 hours 1940-02-13T00:00:00+00:00
12: 24.0 hours 1940-02-14T00:00:00+00:00
13: 24.0 hours 1940-02-15T00:00:00+00:00
14: 24.0 hours 1940-02-16T00:00:00+00:00
15: 24.0 hours 1940-02-17T00:00:00+00:00
16: 24.0 hours 1940-02-18T00:00:00+00:00
17: 24.0 hours 1940-02-19T00:00:00+00:00
18: 24.0 hours 1940-02-20T00:00:00+00:00
19: 24.0 hours 1940-02-21T00:00:00+00:00
20: 24.0 hours 1940-02-22T00:00:00+00:00
21: 24.0 hours 1940-02-23T00:00:00+00:00
22: 24.0 hours 1940-02-24T00:00:00+00:00
23: 24.0 hours 1940-02-25T00:00:00+00:00
24: 23.0 hours 1940-02-26T00:00:00+01:00 <--- here
25: 24.0 hours 1940-02-27T00:00:00+01:00
26: 24.0 hours 1940-02-28T00:00:00+01:00
27: 24.0 hours 1940-02-29T00:00:00+01:00
28: 24.0 hours 1940-03-01T00:00:00+01:00
Does this make sense?
Use UTC were you don't want to care about daylight savings time. Other wise prepare to scratch your head a ton.
Yes I understand, but as I said the problem is not only with DST but also with time zone changes. When a country changes its timezone as it was the case several times in France between 1902 and 1968 (in my second example), this may cause troubles when we want to count the number of days between two dates.
As you said, to avoid these problems we should use dates with UTC time zone which is the solution I indeed chose. This seems logical: local zone should never be used in operations or only when we are sure that there is no time zone change or DST.
As regards the parsing of a date, I agree with you that the default should be UTC and not the local time zone. But if a time is specified (without explicit time zone) should we also use UTC rather than the local time ? This is not so obvious.