Unix time with NSDate reveals erroneous results - ios

I live in Denmark (UTC + 1 ) and I am working with a webapi that sends my app a unix timestamp since 1970-1-1 00:00:00. The time is in the future (train depatures)
If I check the timestamp in Numbers or Excel it gives me the correct time
To calculate the number of minutes until the train departures I do like this:
let unixTimeTrainDeparture = 1419327780 //(or some time in the future)
let unixRightNow = NSDate().timeIntervalSince1970
let minutesToDeparture = (Int(unixTimeTrainDeparture) - Int(unixRightNow))/60
However this gives 60 minutes too much?
And If I do a
let dateTest = NSDate(string: "1970-01-01 00:00:00 +0000")!
will give me 1 jan 1970 :01:00:00 +0000
This does not make sense to me. It is like the timeIntervalSince1970 gives me 3600 sec too less, as it starts from 1970-1-1 01:00 rather than 00:00? It this a bug or is it the way it should be?
I can correct the time by using the
let tz = NSTimeZone.defaultTimeZone()
let seconds = tz.secondsFromGMTForDate(NSDate())
and then subtracting the seconds from my result. However, what happens when we move into summer time?

timeIntervalSince1970 always gives the time in GMT. Your unixTimeTrainDeparture is probably the time in GMT+1, which explains the 60 minute difference (or 120 minutes in summer time). Same goes with the string conversion - you input a GMT time and it outputs the date in whatever timezone you have configured (I'm guessing your computer's setting is GMT+1 as well).
When working with timezones, always start with GMT/UTC and don't do any timezone conversions until displaying the date to the user.
Do you have any control over the web API? If so - configure it to send GMT instead. This should completely avoid time zone and daylight savings issues.
If you cannot do that you will have to implement some function to convert the timestamp yourself, accounting for the possibility that a future timestamp could be in a different timezone (eg daylight savings). NSTimeZone might be very useful for this!
Hope I have understood your problem correctly!
Edit, added example that should handle DST:
// Date far in the future in DST, replace this
let unixTimeTrainDeparture = NSDate(timeIntervalSince1970: 1436447418)
let now = NSDate()
// Assume unixTimeTrainDeparture is in the Copenhagen timezone
let tz = NSTimeZone(name: "Europe/Copenhagen")
// This is 3600 in non-DST, otherwise 7200
let offset = tz!.secondsFromGMTForDate(unixTimeTrainDeparture)
let realUnixTimeTrainDeparture = Int(unixTimeTrainDeparture.timeIntervalSince1970) - offset
let timeToDeparture = realUnixTimeTrainDeparture - Int(now.timeIntervalSince1970)

Related

DateTime.fromMillisecondsSinceEpoch seems wrong

From the documentation
The constructed DateTime represents 1970-01-01T00:00:00Z + millisecondsSinceEpoch ms in the given time zone (local or UTC).
Therefore, if my local timezone is +1, this test should pass -
test('DateTime', () {
var dt = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
expect(dt.toIso8601String(), '1970-01-01T00:00:00.000Z');
var dtLocal = DateTime.fromMillisecondsSinceEpoch(0, isUtc: false);
expect(dtLocal.toIso8601String(), '1970-01-01T00:00:00.000');
});
However, it fails as dtLocal.toIso8601String() gives 1970-01-01T01:00:00.000. Is it just me or is the documentation unclear? I would expect it to just change the timezone, not the milliseconds since epoch based on the local timezone.
The start of the epoch is 1970-01-01T00:00:00 UTC. The start of the the epoch is not different for different time zones. So the start of the epoch in my local time zone (EST/UTC-5) would be 5 hours before that date: 1969-12-31T19:00:00.
This is exactly what your code is trying to do. dt is getting the time of the epoch start in UTC+0. dtLocal is getting that exact same time, but putting it in your time zone, which appears to be UTC+1.
The behavior you're getting is expected.

Transform Epoch UTC to String EST using momentjs

I have two input variables: an epoch time in UTC time zone and the name of the actual time zone. How do I get a formatted day/time using moment.js that would take in account the DST changes. I tried this code but it doesn't do the trick. What am I doing wrong, please?
var abs_time = 1611188219.277; // this is UTC coresponding to 1/21/2021 18:31:37 UTC
var timezone = "America/New_York"; // this the actual time zone
var mom = moment(abs_time * 1000).format();
var date_time = moment.tz(mom, timezone).format('ddd, MMM DD YYYY - HH:mm');
console.log(date_time);
//actual result: Thu, Jan 21 2021 - 18:31
//desired result: Thu, Jan 21 2021 - 13:31 - in the summer this should only be 4 hour difference
First, 1611188219.277 actually corresponds to 2021-01-21T00:16:59.277Z, not the time you gave in your question (assuming it is a Unix timestamp with seconds precision). This can be seen with the following code:
const d = new Date(1611188219.277 * 1000);
const s = d.toISOString();
console.log(s);
You can get the equivalent local time in a specific time zone without any libraries, as long as you're satisfied with the output produced by the toLocaleString function.
const d = new Date(1611188219.277 * 1000);
const s = d.toLocaleString(undefined, { timeZone: 'America/New_York' });
console.log(s);
Note that undefined in the above code will use the browser's current language. If you want a specific language, you could pass its language code there instead (such as en or en-US, etc.)
In general, due to its project status, you should avoid using Moment unless it's already being used in an existing project. If however, you must use Moment and Moment-TimeZone for this, you can do the following to get the same result:
const m = moment.tz(1611188219.277 * 1000, 'America/New_York');
const s = m.format('ddd, MMM DD YYYY - HH:mm');
console.log(s);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.32/moment-timezone-with-data-10-year-range.min.js"></script>
I used the same format from your question, but of course you could change this as desired.
You might also consider using Luxon (the successor to Moment), or Date-fns, or several other libraries.
And yes, all of the above will correctly take daylight saving time into account.

Converting unknown date to human format in SQLite Database

There's an iOS app that I'm looking at where the SQLite table for date shows up as the following
ZRECORDDATE
423942471
423873218
423512431
423950419
423954082
423954975
424551647
at first I believe it was UNIX time but after using a converter - 1983 definitely isn't it.
I'm probably missing the obvious here but can anyone point me in the right direction for converting this to a human date/time?
Thanks in advance
With your indication, it may be a time interval since 01 Jan 2001 in seconds.
let sec : TimeInterval = 423942471
let date=Date(timeIntervalSinceReferenceDate:Sec)
print("date \(date)")

Getting current time of day down to milliseconds error

I have been attempting to access the time of day in hour:minute:second format however all that these
println(CFTimeZoneCopySystem())
println(CFTimeZoneCopyDefault())
println(gettimeofday)
println(CACurrentMediaTime)
print are
America/New_York (EDT) offset -14400 (Daylight)
America/New_York (EDT) offset -14400 (Daylight)
(Function)
(Function)
I don't understand why these are simply printing out "(function)" and I am also unsure as to what time format 14400 is. I want to be able to access something that would be accurate across bluetooth devices. so that is why I would prefer to access milliseconds. I know there was another question similar to this one however I do not think they received this article as well as I do not understand objective-C
You can use the code below:
let formatter = NSDateFormatter()
//formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) // you can set GMT time
formatter.timeZone = NSTimeZone.localTimeZone() // or as local time
formatter.dateFormat = "HH:mm:ss.SSS"
println(formatter.stringFromDate(NSDate()))
Output: "05:57:26.123"

Jodatime get milliseconds with offset

New to JodaTime library, i would like to get a DateTime's milliseconds field with the specified TimeZone's offset.
So far my attempt is:
private DateTimeZone timeZone = DateTimeZone.forID("Europe/Amsterdam");
private long now=new DateTime().withZone(timeZone).getMillis();
But i always get the UTC millis, the timezone offset isnt applied,
Is there any way to apply the timezone's offset to the DateTime object?
Thx!
First: What do you intend to do with these "local" millis? What do you really try to achieve? Normally only UTC-millis are needed.
Anyway, remember the general timezone offset definition which is:
UTC + Offset = Local Time
Then the solution is simple:
DateTimeZone tz = DateTimeZone.forID("Europe/Amsterdam");
long nowUTC = new DateTime().withZone(tz).getMillis();
long nowLocal = nowUTC + tz.getOffset(nowUTC);
But once again: What is your use-case for "local" millis? They are not even related to UNIX epoch any longer because the UTC-link is cut off.
About your last question ("Is there any way to apply the timezone's offset to the DateTime object?"):
Your DateTime-object already has got a timezone, namely "Europe/Amsterdam". It is internally used to compute the field tuple representation once you have a global UTC-timestamp expressed as millis since UNIX epoch. No need to apply an extra offset on DateTime. It is already there.
JodaTime is using machine time inside. So to find miliseconds, you can use a constant storing LocalDateTime referring to Jan 1, 1970(Because of UNIX Time).
Unix time, or POSIX time, is a system for describing points in time,
defined as the number of seconds elapsed since midnight proleptic
Coordinated Universal Time (UTC) of January 1, 1970, not counting leap
seconds.
Then calculate the difference between your DateTime.
I tried like this;
public static void main(String[] args) {
final LocalDateTime JAN_1_1970 = new LocalDateTime(1970, 1, 1, 0, 0);
DateTime local = new DateTime().withZone(DateTimeZone.forID("Europe/Amsterdam"));
DateTime utc = new DateTime(DateTimeZone.UTC);
System.out.println("Europe/Amsterdam milis :" + new Duration(JAN_1_1970.toDateTime(DateTimeZone.forID("Europe/Amsterdam")), local).getMillis());
System.out.println("UTC milis :" + new Duration(JAN_1_1970.toDateTime(DateTimeZone.UTC), utc).getMillis());
}
And the result is;
Europe/Amsterdam milis :1429695646528
UTC milis :1429692046534
And #leonbloy write here a good comment.
Your local and utc represent the same instants of time, (only with
different timezones attached). Hence, getMillis() (which gives the
"physical" time interval elapsed from the "instant" corresponding to
the unix epoch), must return the same value.
I will also look for better solution with no constant.

Resources