chime icon indicating copy to clipboard operation
chime copied to clipboard

periodic-seq example and DST changes

Open terjesb opened this issue 2 years ago • 1 comments

Thank you for this nice little library.

The README has an example of using periodic-seq for local times:

(chime/periodic-seq (-> (LocalTime/of 20 0 0)
                        (.adjustInto (ZonedDateTime/now (ZoneId/of "America/New_York")))
                        .toInstant)
                    (Period/ofDays 1))

I don't get this to work properly across DST changes. It seems to me that this example converts to instant before adding the period, which means that the period is always from UTC and not local time.

This shows the problem:

(binding [chime.core/*clock* (java.time.Clock/fixed (.toInstant #inst "2022-10-28T12:12:12Z") (java.time.ZoneId/of "UTC"))]
  (take 2
        (->> (chime.core/periodic-seq
              (-> (java.time.LocalTime/of 7 30)
                  (.adjustInto (-> (java.time.ZonedDateTime/now chime.core/*clock*)
                                   (.withZoneSameLocal (java.time.ZoneId/of "Europe/Oslo"))))
                  .toInstant)
              (java.time.Period/ofDays 1))
             (chime.core/without-past-times))))

Here we see that the result is the same UTC hour before before and after the recent DST change, which results in an incorrect local time for the second instant:

(#object[java.time.Instant 0x300c7735 "2022-10-29T05:30:00Z"]
 #object[java.time.Instant 0x54e5895a "2022-10-30T05:30:00Z"])

The problem seems to be that periodic-seq works with instants, but that the conversion to instant should instead be the final step. Not yet sure how to solve this in a nice way. A separate function for periodic-local-date-time-seq, and let the caller map to instants?

Or perhaps add an arity to periodic-seq for [LocalDateTime ZoneId duration-or-period] for calculating using LocalDateTimes, while still returning instants? (If so, maybe also include another arity for a final xform for doing complex schedules on the local date times, before returning the instants?)

In the mean time, here's a possible not-so-nice solution:

(binding [chime.core/*clock* (java.time.Clock/fixed (.toInstant #inst "2022-10-28T12:12:12Z") (java.time.ZoneId/of "UTC"))]
  (take 2
        (->> (map #(.toInstant (.atZone % (java.time.ZoneId/of "Europe/Oslo")))
                  (iterate
                   #(.plusDays % 1)
                   (-> (java.time.LocalTime/of 7 30)
                       (.adjustInto (java.time.LocalDateTime/now chime.core/*clock*)))))
             (chime.core/without-past-times))))

Here we see that the instants change as expected after DST change:

(#object[java.time.Instant 0x221b9091 "2022-10-29T05:30:00Z"]
 #object[java.time.Instant 0x49e3eb99 "2022-10-30T06:30:00Z"])

terjesb avatar Nov 02 '22 10:11 terjesb