DateTime madness

I found it remarkably difficult to construct a TimeDate object from a date and timezone at the beginning of day, which would correctly handle DST. The challenge I ran into was that you don’t know if a calendar day is on DST or not until you construct the TimeDate object, and the TimeDate.new() always constructs a TimeDate with the local (or is it UTC? we run on UST so I forget) timezone, then shifts the time when you call in_time_zone. I could construct a TimeDate from components, but one component is offset, and does not accept a timezone name, so you need to construct an ActiveSupport::Timezone object, and give it a timedate object on the correct date in the correct timezone (as different timezones can have different DST rules) before asking for it’s .formatted_offset… which leads right back to this same code of constructing two timedate objects and checking to see which one maintains the correct date after being shifted to the correct timezone. I eventually ended up with the following code, but it feels like there should be an easier way. Am I missing something?

def date_to_beginning_of_day(date, zone)
  [date.at_beginning_of_day.in_time_zone(zone), date.at_end_of_day.in_time_zone(zone)]
    .detect { |t| t.to_date == date }
    .at_beginning_of_day
end

The problem is that time zone abbreviations can duplicate depending on the location.

Based on this fact, this method only understands the time zone abbreviations described in RFC 822 and the system time zone, in the order named. (i.e. a definition in RFC 822 overrides the system time zone definition.) The system time zone is taken from Time.local(year, 1, 1).zone and Time.local(year, 7, 1).zone . If the extracted time zone abbreviation does not match any of them, it is ignored and the given time is regarded as a local time. - rubydocs 2.6.5

The Time class I find usually has the most flexibility than the other classes.

You can get around this by the Time.now method.

You can string it or add it to a variable.

Time.now.zone
timezone = Time.now.zone

You also have
Time.now.day
Time.now.month
Time.now.year

For sale of easyness
time = Time.now
time.hour
time.min
time.sec
time.wday
time.yday

And a whole lot more.

Fascinating that the .now() method for the Time object is entirely missing from the documentation: Class: Time (Ruby 2.6.5)

But the problem I was describing was instantiating a correct time for a location given a calendar date which may or may not be today or now. I can extract all the same information from a DateTime object as you’re describing from a Time object… but the same issue remains of creating a correct object for the start of the day (midnight) given a date and a timezone. My solution works correctly (as long as a server is running on UTC), but feels very hacky. Is there a more correct solution than my code that the Time object enables? I’m not seeing it yet if there is…

Okay so you need the .dst? Method.

This will help you simply your code.

For an example, we will stick with Time.now (this is a alias for Time.new, both do the same thing)

puts Time.now.dst?
This will output true or false depending I. If your computer is set to a timezone in or out of DST.

So with this and the help with Time.parse()or Time.local(), you can use the .dst? Method to check if it is or not DST via date and timezone.

Simple example:

def check_dst
a = Time.now.dst?
if a == true
puts “hey your in daylight savings time!”
end
if a == false
puts " When told the reason for daylight saving time the old Indian said…
‘Only a white man would believe that you could cut a foot off the top of a blanket and sew it to the bottom of a blanket and have a longer blanket.’ "
end

Time.now.dst? will tell you if it is DST in the currently active timezone (UTC on my servers) today.

Want to find out if someone is in DST in Japan on wednesday next week? How about Australia next month? Your currently suggested approach does not work. And it gets even trickier when you realize that different timezones start and stop DST on different days, and some timezones don’t even use DST.

Handling DST is part of the problem. A full statement of the specific problem I want to solve:

Given a calendar date (could be any valid calendar date)
Given a timezone (like “Eastern (US and Canada)”)
Construct either a Time or a DateTime object at the beginning of the day (midnight) with the correct offset including DST.

The code I wrote handles all those cases correctly… but it’s ugly. I am hoping there is a less ugly approach that handles all those cases.

Use .dst? with the .parse() or .local(). You can do exactly that with those methods.

You’re missing another wrinkle. Time.local- just like Date.at_beginning_of_day (and end_of_day) constructs a Time or DateTIme object in the local timezone. Then, when you apply “in_time_zone(zone_i_want)”, the time gets shifted by the offset between the two timezones. I have not found a way to alter the zone on a Time or Datetime object without it automatically “shifting” the time. And only once you have a TIme or Datetime on the correct calendar date in the correct timezone can you determine if it is in DST or not. That is why I construct both, and use detect to determine which of them maintained the correct date (one of them should, so long as my local timezone is UTC- other timezones could even fail, as the maximum offset between timezones is 27 hours; UTC just happens to sit in the middle), then I set the timestamp back to the beginning of the day now that it’s on the correct date in the correct timezone.

I could construct a string and have ruby parse the string, but that’s even more ridiculous than what I’m doing.

For any future people who come across this topic- I finally came across what I was looking for. The method is Time.use_zone, and it accepts a block within which any Time constructed will receive the zone. So the method I wrote becomes:

def date_to_beginning_of_day(date, zone)
  Time.use_zone(zone) { date.at_beginning_of_day.to_datetime }
end

Happy time construction, anyone.