Timezone confusion with Ruby's Time class - ruby

It's worth noting I am using Ruby 2.1.2, and so the Time class uses a signed 63 bit integer. The integer is used when it can represent a number of nanoseconds since the Epoch; otherwise, bignum or rational is used, according to the documentation.
When I use ::new without arguments, it gives me the current time using my local time zone (not UTC):
> Time.new
=> 2015-06-30 18:29:08 -0400
It's correct. It's 6:29pm on the East coast of the US. Now I want to check the local time in two timezones to the east of EDT:
> t = Time.new(2015,6,30,18,29,8,"+02:00")
=> 2015-06-30 18:29:08 +0200
This is where my confusion comes in. When I specify two timezones to the east of me, I expect there to be two additional hours because each timezone is 15 degrees longitude and each is represented by 1 hour.
Why did it give me the same time as my local time, instead of two hours later?

What you think is happening isn't what's happening. What you've done is give that time an offset of GMT +2 as opposed to two hours out from your current timezone.
If you want to see the time at the offset of two hours ahead of where you are, then you want to create the instance of Time, get your local time, and shift that by the GMT offset.
Time.now.getlocal("-02:00")
If you want to compute this, you have to look at your local time's utc_offset first, then either add or subtract the product of 3600 and however many timezones you want to move. Note that this only moves timezones in whole number increments and will break in cases where a timezone that requires a different precision is needed (i.e. Newfoundland).
t = Time.now
t.getlocal(t.utc_offset + (3600 * 2))

Related

Is there any way by which I can get TimeZone value using time stamp

This is the value which I have,
Sun Mar 29 2020 02:55:00 GMT+0530
and I want to get,for example
Asia/Calcutta
as ouput. Thanks in advance.
Offset does not indicate zone
get TimeZone value using time stamp
No.
You cannot determine a time zone from an offset.
Many time zones can share the same offset-from-UTC (the number of hours-minutes-seconds ahead or behind the prime meridian).
See the list of time zone names in Wikipedia. Click on the column header to sort by offset. Notice how often several zones share the same offset.
Specific to your example, notice how we currently have two zones that coincidentally share an offset of five and a half hours ahead of UTC:
Asia/Kolkata (India)
Asia/Colombo (Sri Lanka)
So, without further input, there is no way to know if the author of your input string intended India time or Sri Lanka time.
By the way, the name Asia/Calcutta has been changed to Asia/Kolkata. If your system has no such name, then your tzdata is several years out of date. Always keep all the copies of tzdata up-to-date in OSes, database servers such as Postgres, and runtimes such as Java.
Another complication: politicians frequently change the offset used in their jurisdictions.
So while all of India today uses the same offset of +05:30, that has not always been the case, nor is it likely to always be true in the future (based on the history of how often zones change around the world).
ISO 8601
The ISO 8601 standard defines many sensible formats for representing date-time values as text.
2020-01-23T12:34:56.123456789+05:30
The java.time framework built into Java 8 and later extends one of those format wisely by appending the name of the time zone in square brackets. I suggest using this format if feasible.
2020-01-23T12:34:56.123456789+05:30[Asia/Kolkata]

Is there a one to one and onto relation between ISO-8601 UTC and Unix Timestamp?

Time Formats
A point in time is often represented as Unix Time, or as a human-readable ISO 8601 date in UTC time string.
For example:
Unix Time
Seconds since Epoch, or Unix timestamp, in seconds or milliseconds:
1529325705
1529325705000
ISO 8601 Date
2018-06-18T15:41:45+00:00
My question
Is there a one-to-one and onto relationship between the two? In other words, is there a point in time with a single representation in one format, and more than one, or zero, representations in the other?
Yes, it is possible to find such a date. From the wiki article on Unix time:
Every day is treated as if it contains exactly 86400 seconds,[2] so leap seconds are not applied to seconds since the Epoch.
That means that the leap seconds themselves cannot be represented in Unix time.
For example, the latest leap second occurred at the end of 2016, so 2016-12-31T23:59:60+00:00 is a valid ISO 8601 time stamp. However, the Unix time stamp for the second before, at 23:59:59, is represented as 1483228799 and the second after, 00:00:00 (on January 1 2017) is 1483228800, so there is no Unix timestamp that represents the leap second.
In practice, this is probably not a problem for you; there has only been 27 leap seconds since they were introduced in 1972.
It might be worthwhile to mention that most software implementations of ISO 8601 does not take leap seconds into account either, but will do something else if asked to parse "2016-12-31T23:59:60+00:00". The System.DateTime class in .NET throws an exception, while it's also conceivable that a library would return 2017-01-01 00:00:00.
No. there is a nice correspondence between the two, but the relationship is 1 to many, and strictly speaking there may not even exist a precise Unix millisecond for a given ISO date-time string. Some issues are:
There are some freedoms in the ISO 8601 format, so the same Unix millisecond may be written in several ways even when we require that the time be in UTC (the offset is zero).
Seconds and fraction of seconds are optional, and there may be a varying number of decimals on the seconds. So a milliseconds value of 1 529 381 160 000, for example, could be written as for example
2018-06-19T04:06:00.000000000Z
2018-06-19T04:06:00.00Z
2018-06-19T04:06:00Z
2018-06-19T04:06Z
The offset of 0 would normally be written as Z, but may also be written as you do in the question, +00:00. I think the forms +00 and +0000 are OK too (forms with a minus are not).
Since there may be more than three decimals on the seconds in ISO 8601, no exact Unix millisecond may match. So you will have to accept truncation (or rounding) to convert to Unix time. Of course the error will be still greater if you convert to Unix seconds rather than milliseconds.
As Thomas Lycken noted, leap seconds can be represented in ISO 8601, but not in Unix time.
In other words, is there a point in time with a single representation in one format, and more than one, or zero, representations in the other?
No. The UTC time depends on your geographic location, ie. your latitude and longitude. However, the UNIX timestamp is a way to track time as a running total of seconds. This count starts at the Unix Epoch on January 1st, 1970 at UTC.
From Unix TimeStamp,
It should also be pointed out that this point in time technically does not change no
matter where you are located on the globe.

Displaying local time to a user when all you have is an offset (from GMT)

I understand that it's pretty easy to display local time to a user given an offset from GMT for the timezone (e.g. -7 hours for Pacific Daylight Time).
But, what if I want to continue to display the correct local time for a long period of time (say 1 year). Given just an offset from GMT, you do not know what timezone the user is in. E.g. given -7, the user may live in the US or in Canada (or in some other country). These countries my have different local times at different points of the year (e.g. if the US changes to daylight time in March and CA in April).
My question is, is the above paragraph correct? Is there a standard way to take a GMT offset and make a good guess as to which timezone the user is in?
Your conclusion is correct: There is no reliable way to identify a time zone from an offset-from-UTC.
For your example of an offset of -07:00, I count about three dozen possible time zones in the current list including: America/Boise, America/Chihuahua, and America/Edmonton.
A time zone is actually a collection of offsets, recording changes made over time with a certain offset used in that region for a certain period of time but then changed over another certain period of time.
For example, in America/Los_Angeles part of each year has an offset of -08:00 while another part of the year has -07:00. So that time zone is accumulating a history of at least two offsets per annum. In contrast, the neighboring region using the time zone America/Phoenix is not accumulating changes in its offset, having retained the same -07:00 offset for decades.
So we have a many-to-many relationship between offsets and time zones. An offset may appear in one or more time zones. And each time zone can have one or more offsets (having changed over history).
Include time zone name
This is why the designers of the java.time class ZonedDateTime have taken the liberty of having its toString method extend the standard ISO 8601 format using only offsets to also append the name of the time zone in square brackets.
For example, instead of simply generating the standard format: 2007-12-03T10:15:30+01:00, the class generates 2007-12-03T10:15:30+01:00[Europe/Paris] with the name of the zone Europe/Paris appended.
ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )
.toString()
2007-12-03T10:15:30+01:00[Europe/Paris]
I hope this practice of appending the time zone name catches on. The lack of time zone name is a surprising omission by the standards committees that otherwise did an outstanding job in designing ISO 8601.
You can guess about the time zone using clues from the context of your business scenario. But I advise against that. Guessing is risky, especially because politicians around the world have a penchant for frequently re-defining time zones.
UTC
The best practice in storing, serializing, and exchanging date-time values is generally to adjust into UTC. Assuming a date-time library’s tzdata is up-to-date, adjusting into UTC provides a reliable value that is always correct and unambiguous.
In Java, for example, that means using or extracting an Instant object. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now();
…or…
ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
Instant instant = zdt.toInstant();
The standard ISO 8601 formatted string representing such a value uses a Z as short for Zulu and meaning UTC.
2007-12-03T09:15:30Z
OffsetDateTime
If you are given a string representing a date-time with only an offset, parse it as a OffsetDateTime object.
OffsetDateTime odt = OffsetDateTime.parse( "2007-12-03T10:15:30+01:00" );
odt.toString(): 2007-12-03T10:15:30+01:00
From there you can extract a value in UTC, an Instant.
Instant instant = odt.toInstant();
instant.toString(): 2007-12-03T09:15:30Z
Or you can adjust into a desired time zone to get the same moment as a ZonedDateTime object.
ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = odt.atZoneSameInstant( z );
zdt.toString(): 2007-12-03T14:45:30+05:30[Asia/Kolkata]
Terminology: “local”
I suggest you avoid the term “local” when referring to a date-time adjusted into a region’s particular wall-clock time. The word “local” in the java.time classes and in other contexts means any locality, rather than a particular locality. A local date-time is not a specific moment on the timeline, only a rough idea about a range of possible moments.
For example the local date-time of the beginning of Christmas this year is 2017-12-25T00:00:00, but that moment of midnight is much earlier in Auckland than in Kolkata, and still hours later in Paris, while even more hours later in Montréal.
I suggest instead you use the terms zoned or wall-clock time when you mean a specific moment seen through the lens of a particular time zone.
Tip: Offset literals
The Question happened to use the letters -7 when referring to an offset. I suggest always:
Using double-digits with padding zero, as required by ISO 8601
Using both hour and minutes, as expected by some libraries and formats.
So use -07:00 rather than -7.

gps time, time conversion

I have time in UTC seconds format. Could any one assist on how to convert such numbers to GPS
time in normal timestamp (dd-mm-yyyy hh:mm:ss)? I need a C code or, perhaps, algorithm.
Update (June 2017): Currently 18 leap seconds.
GPS time is simply UTC time, but without leap seconds. As of this writing, there have been 15 leap seconds since the GPS epoch (January 6, 1980 # 00:00:00), so if it's 2012/02/13 # 12:00:00 (UTC), then it's 2012/02/13 # 12:00:15 in GPS time. If you want to do correct conversions for other times, you'll have to take into account when each leap second went into effect.
Here's how you can compute the current offset, from a couple different "authoritative" sources:
http://www.ietf.org/timezones/data/leap-seconds.list -- Count the number of lines starting from the 2571782400 20 # 1 Jul 1981 line. Or just subtract 19 from the last number in the list (e.g., 37-19 = 18) as of May 2017.
https://www.nist.gov/pml/time-and-frequency-division/atomic-standards/leap-second-and-ut1-utc-information -- Count the number of leap seconds inserted (from the Leap Seconds Inserted into the UTC Time Scale section), starting with (and including) the 1981-06-30 entry.
There is a Javascript library that can convert to and from unixtime. The library is available at
http://www.lsc-group.phys.uwm.edu/~kline/gpstime/
Whatever algorithm you use, you must update it when new leap seconds are announced.
for an algorithm check this site source code: UTC Converter
for built-in functions in c++ check here - especially ctime()

What is the range of times that Ruby's Time class can represent?

Times farthest in the past and farthest in the future that can be represented?
Is it absolute moments in time, or distance in time from the present moment?
I couldn't find it in the docs for the Time class.
Does it depend on the system? If so, how can I access it in my code?
UPDATE
After some experimentation, I found that it's from about 108 years in the past to about 29 years in the future. Still wondering if it's system dependent.
DateTime (in the Date library, included with ruby) goes back to 1 January 4713 BCE and farther into the future than you are likely to need.
"Time is stored internally as the number of seconds and microseconds since the epoch, January 1, 1970 00:00 UTC. On some operating systems, this offset is allowed to be negative."
So clearly its an abosolute time not relative to now
Sounds like there is a "C" time implementation under cover (integers can be signed or unsigned depending on OS / processor / compiler) : it means the bounds are system dependent.
But if you need to handle dates that are that long ago / far in the future, I guess you won't really need the "time of day" part and can use a Date !?

Resources