Daylight saving hours in Golang - go

I have been designing a UI for a scheduling interface, where a user can set timers a number of hours into the future. I want to be able to handle daylight saving if possible, I thought it would be quite simple. Whilst checking out time.Time in the golang packages I ran into the following inconsistency, if that is what it is.
package main
import (
"fmt"
"time"
)
func main(){
const timeFormat = "2 Jan, 2006 3:04pm (MST)"
test , err := time.Parse( timeFormat, "25 Oct, 2015 1:59am (BST)" )
fmt.Println( test , test.UTC() , err)
dur , _ := time.ParseDuration( "1m" )
test = test.Add( dur )
fmt.Println( test , test.UTC())
fmt.Println( "--------------------" )
test , err = time.Parse( timeFormat, "25 Oct, 2015 2:01am (BST)" )
fmt.Println( test , test.UTC() , err)
test = test.Add( dur )
fmt.Println( test , test.UTC())
test = test.Sub( dur )
fmt.Println( test , test.UTC())
}
I know that 2am on the 25 Oct 2015 in BST will should cause the clock to go back to 1am GMT (UTC). If I increment 1:59am BST by one minute the time does indeed switch to GMT.
2015-10-25 01:59:00 +0100 BST 2015-10-25 00:59:00 +0000 UTC <nil>
2015-10-25 01:00:00 +0000 GMT 2015-10-25 01:00:00 +0000 UTC
--------------------
2015-10-25 02:01:00 +0000 BST 2015-10-25 02:01:00 +0000 UTC <nil>
2015-10-25 02:02:00 +0000 BST 2015-10-25 02:02:00 +0000 UTC
However if I parse a time after 2am in BST I would expect it to switch to GMT just as incrementing the time over the transition would have. In case the transitional code was called by the Add routine I add a minute again but this also does not revert the time to GMT.
I would have expected one of the following to occur
a) for BST to always remain GMT+1
b) to any time where BST is "invalid" to automatically change to the correct GMT time (invalid BST is between 2am after the last Sunday in October and 2am after the last Sunday in March the following year)
c) an error to be thrown if a date is created within those dates with BST (and perhaps other daylight saving hours in other countries).
Otherwise I'll have to check if a user enters a date in BST whether that date lies outside BST and adjust or force users to UTC times, which kind of defeats the object of having daylight saving functions built into the library.
Whilst researching I spotted this https://www.youtube.com/watch?v=-5wpm-gesOY
and have decided it is definitely not as simple as I had at first assumed...
Any insight or a better way to handle daylight saving times would be appreciated.
Using go version 1.0.2 on Debian Wheezy
Edited: retried using go version 1.3.3
and got this output
2015-10-25 01:59:00 +0100 BST 2015-10-25 00:59:00 +0000 UTC <nil>
2015-10-25 01:00:00 +0000 GMT 2015-10-25 01:00:00 +0000 UTC
--------------------
2015-10-25 01:00:00 +0000 GMT 2015-10-25 01:00:00 +0000 UTC <nil>
2015-10-25 01:01:00 +0000 GMT 2015-10-25 01:01:00 +0000 UTC
So appears to work as I expected in later versions...
Have also found this question
Daylight saving time and time zone best practices
So will be giving it a thorough read.
Thanks.

Go, like everybody else but Microsoft, uses the IANA Time Zone Database which has regular updates that are included in the current Go release.
You used go1.0.3 which was released in March 2012 (Release History). The British time zone data for 2015 was added later.
ALWAYS use a current version of Go for time zone calculations.

Related

Calculate the Closest Time Difference in HH:MM(am/pm) Format using Go

I got a bit problem when calculating the time difference from PM to AM or vice versa. For instance:
ref, _ := time.Parse("03:04pm", "11:59pm")
t, _ := time.Parse("03:04am", "12:00am")
fmt.Println(t.Sub(ref).Minutes()) // Got -719, my expectation is 1 (minutes)
Actually that's true, but I want to get the smallest difference.
The reason you got -719 is that you do not provide date information and in second time.Parse you have typo in template. Template has to contain pm
time.Parse("03:04pm", "11:59pm") // 0000-01-01 23:59:00 +0000 UTC
time.Parse("03:04am", "12:00am") // 0000-01-01 12:00:00 +0000 UTC
You need to provide day information and pm in template
time.Parse("02 03:04pm", "01 11:59pm") // 0000-01-01 23:59:00 +0000 UTC
time.Parse("02 03:04pm", "02 12:00am") // 0000-01-02 00:00:00 +0000 UTC
see https://stackoverflow.com/a/69338568/12301864

convert string to time and parse in golang

I'm reading a timestamp from a file, and I assign the value to t:
t := "2016-11-02 19:23:05.503705739 +0000 UTC"
When I attempt to parse the string:
time, err := time.Parse("2016-11-02 19:18:57.149197306 +0000 UTC", t)
The result is:
0001-01-01 00:00:00 +0000 UTC
But I expected result to be:
"2016-11-02 19:18:57.149197306 +0000 UTC" ?
Please advise.
You're not correctly providing the layout argument to Parse. You're supposed to be using Mon Jan 2 15:04:05 MST 2006 (this is magic value, you put create a string in the format you want but with that date) in the given format so in your case, it would be 2006-01-02 15:04:05.000000000 +0000 UTC plus the offset which I don't know off the top of my head for MST.

Golang timestamp parsing

I'm trying to parse the timestamp Oct 12 2016 13:59:27 UTC using the following code.
eventDateLayout := "Jan _2 2006 15:04:00 UTC"
eventCheckDate, _ := time.Parse(eventDateLayout,"Oct 12 2016 13:59:27 UTC")
fmt.Println(eventCheckDate)
Result if 0001-01-01 00:00:00 +0000 UTC which is not the expected.
Can this timestamp parsed with golang time library?
In addition to the incorrect time layout, I'd recommend handling the error instead of throwing it away.
It gives you a helpful error message that you can use to efficiently debug:
cannot parse "27 UTC" as ":00 UTC"
Go playground (note the outputted time will be different)

Rails Time Zone and cron scheduling

I have an AWS server that runs daily cron jobs reporting on our user base. I want to ensure my report is run for the full day the previous day in MST. Currently I use this as the code for the data quering
Time.new(Time.now.year, Time.now.month, Time.now.day).yesterday.beginning_of_day.in_time_zone('MST)..Time.new(Time.now.year, Time.now.month, Time.now.day).yesterday.end_of_day.in_time_zone('MST)
I read it is bad practice to use Time.now as that is the system (UTC) time? I am wondering if what I am doing is a big no no or if there is a more efficient way?
thank you!
Mountain Standard Time is 7 hours behind UTC, so when you capture all the data points from the day of July 22rd in MST, you want the UTC times to be from 7/22 at 7:00AM UTC to 7/23 at 7:00AM UTC.
I don't think your code is correct because you are calling in_time_zone("MST") after beginning_of_day.
When you run this code on a server that is on UTC, the evaluated times are different:
>> Time.new.yesterday.beginning_of_day.in_time_zone('MST').utc
=> 2013-07-22 00:00:00 UTC
>> Time.new.in_time_zone("MST").yesterday.beginning_of_day.utc
=> 2013-07-22 07:00:00 UTC
Here is how you can determine the start and end times properly:
>> t = Time.new
=> 2013-07-23 19:45:10 +0000
>> start_time = t.in_time_zone("MST").yesterday.beginning_of_day
=> Mon, 22 Jul 2013 00:00:00 MST -07:00
>> end_time = t.in_time_zone("MST").yesterday.end_of_day
=> Mon, 22 Jul 2013 23:59:59 MST -07:00
When we convert the start and end times to UTC, we get the desired result.
>> start_time = t.in_time_zone("MST").yesterday.beginning_of_day.utc
=> 2013-07-22 07:00:00 UTC
>> end_time = t.in_time_zone("MST").yesterday.end_of_day.utc
=> 2013-07-23 06:59:59 UTC
I don't know what you are trying to do, but
Time.new(Time.now.year, Time.now.month, Time.now.day)
is definitely a terrible code fragment. For example, if the time lag between the execution time of Time.now.year and that of Time.now.month overlaps the moment of the change of the year, then the time object created with the main Time.new will be neither of the two moments. If you want to get the current time, just do
Time.new
or
Time.now
If you are trying to create a time range calculated out of a single time, then whatever your code should be, create time only once:
t = Time.now
and use that in the rest of your code:
t.some_method..t.some_other_method

How to get Rails to interpret a time as being in a specific time zone?

In Ruby 1.8.7, how to set the time zone of a time?
In the following examples, my system time zone is PST (-8:00 hours from UTC)
Given a time (21 Feb 2011, 20:45), presume that the time is in EST:
#this interprets the time as system time zone, i.e. PST
Time.local(2011,02,21,20,45)
#=> Mon Feb 21 20:45:00 -0800 2011
#this **converts** the time into EST, which is wrong!
Time.local(2011,02,21,20,45).in_time_zone "Eastern Time (US & Canada)"
#=> Mon, 21 Feb 2011 23:45:00 EST -05:00
But, the output I want is:
Mon Feb 21 20:45:00 -0500 2011 (Note the -0500 (EST) as opposed to -0800 (PST) and the hour is same, i.e. 20, not 23)
UPDATE (see the better version of this below)
I managed to get this to work, but I don't like it:
DateTime.new(2011,02,21,20,45).change :offset => -(300.0 / 1440.0)
# => Mon, 21 Feb 2011 20:45:00 +0500
Where
300 = 5 hrs x 60 minutes
1440 = number of minutes in a day
or the "right" way:
DateTime.civil(2011,02,21,20,45,0,Rational(-5, 24))
Question: Now, is there a way to determine the accurate(i.e. catering for daylight saving time etc) UTC offset from Time.zone so that I can pass it to the change method?
Reference: DateTime::change method
UPDATE (better version)
Thanks to #ctcherry for all the help!
Determine the accurate time zone info from Time.zone:
DateTime.civil(2011,02,21,20,45,0,Rational((Time.zone.tzinfo.current_period.utc_offset / 3600), 24))
In ruby 1.8.7 it doesn't appear to be very easy to do what are asking for according to the documentation:
http://www.ruby-doc.org/core-1.8.7/classes/Time.html
However in 1.9 it looks a lot easier by passing the timezone offset to the localtime() method on a Time object:
http://www.ruby-doc.org/core/classes/Time.html#M000346
UPDATE
The offset for Time.zone is easy since its an object on its own: (This is in a Rails console)
ruby-1.8.7-p248 :001 > Time.zone
=> #<ActiveSupport::TimeZone:0x103150190 #current_period=nil, #name="Central Time (US & Canada)", #tzinfo=#<TZInfo::TimezoneProxy: America/Chicago>, #utc_offset=nil>
ruby-1.8.7-p248 :002 > Time.zone.utc_offset
=> -21600
ruby-1.8.7-p248 :003 > Time.zone.formatted_offset
=> "-06:00"
So I think this will (almost) accomplish what you want:
require 'time'
t = "21 Feb 2011, 20:45"
Time.parse(t) # => Mon Feb 21 20:45:00 -0700 2011
t += " -05:00" # this is the trick
Time.parse(t) # => Mon Feb 21 18:45:00 -0700 2011
It still returns the time based on your system time zone, but the actual time is the correct time that you are seeking.
By the way, this is tested on 1.8.7-p334.

Resources