TimeZoneInfo which was defined by local machine not found by local machine? - vb.net

I'm having trouble with timezones, a class I've built finds the local timezone of the user using:
Dim Timezone As String = TimeZoneInfo.Local.ToString
This is then stored in a MySQL DB.
When I pull the timezone, I compare it once again with the local timezone of the user to convert the time to the local timezone:
Dim D_0 As DateTime
Dim D_1 As DateTime
Dim Event_Timezone As TimeZoneInfo
Dim User_Timezone As TimeZoneInfo
Event_Timezone = TimeZoneInfo.FindSystemTimeZoneById(U_1(5).ToString)
User_Timezone = TimeZoneInfo.Local()
D_0 = TimeZoneInfo.ConvertTime(U_1(i + 4), Event_Timezone, User_Timezone)
D_1 = TimeZoneInfo.ConvertTime(U_1(i + 8), Event_Timezone, User_Timezone)
This returns the following error:
The time zone ID '(UTC-05:00) Eastern Time (US & Canada)' was not found on the local computer.
This is a confusing error because this is the timezone the local computer specified only seconds earlier. It works with nearly every other timezone. Is there a better way I should be doing this? Does anyone know why a timezone defined by the local machine is not found by the local machine seconds later?

You're calling ToString() on TimeZoneInfo - that doesn't give the ID, it gives the display name. Often they're the same in English cultures, but they don't have to be, and usually won't be in non-English cultures.
Basically you should persist TimeZoneInfo.Local.Id instead of TimeZoneInfo.Local.ToString().
(Note that using the Windows system time zone identifiers pins you down to Windows pretty heavily. You might want to consider using TZDB time zone information instead, e.g. via my Noda Time project. That's a separate decision though.)

Related

ASP.NET Core API ISO8601 not parsed in local time when suffix is 'Z'

Scenario:
I have a REST-API that takes a POST-Request. As Body-Data there is passed a Datetime in ISO8601 Format.
{
"validate": "2019-12-02T23:00:00Z",
"configuration": {},
"sortId": 1
}
With Modelbinding in MVC the Datetime gets parsed automaticly. The variable should be in the local timezone of the api-server. In this case Europe/Berlin. I would expect the time to be (refering to the example) to be on 2019-12-03:00:00:00. But this is not the case. It is still one hour off.
But when i post the following:
{
"validate": "2019-12-02T23:00:00+00:00",
"configuration": {},
"sortId": 1
}
The parsing into the local timezone works as expected. Because the Client posting the data is written in JS and uses the default Date.toISOString() function, i always get a 'Z' in the ending. According to the ISO8601 this is totally fine.
Z explicitly means UTC. +00:00 doesn't. The UK is at 00:00 now, but 01:00 in the summer. In 1970, summer time (01:00) was used for the entire year.
There are a couple of concepts involved here, and a bit of history. First of all, DateTime has no concept of offset or timezone. A DateTime can only be UTC, Local or Undefined, according to its Kind property.
Using DateTime means that the offset information is lost. The resulting value needs to be converted to something. To do that, the machine's offset is used. That is, the web service machine's offset, not the database server's.
And then, our container or application fails over to a machine with a default UTC timezone instead of our configured timezone. Over the weekend.
It's worth reading Falsehoods programmers believe about time, especially 8. The machine that a program runs on will always be in the GMT time zone.
A better solution would be to use DateTimeOffset, although even that won't be able to handle DST rule changes.
An even better solution would be to use IANA timezone names and pass Europe/Berlin instead of offsets. That's not common usage though. Airlines at least post flight times both with offsets and timezone names.
DateTime Parsing rules
DateTime parsing rules convert Z or offsets to Local with conversion and anything else to Unspecified without conversion. This sounds strange but consider that DateTime was built to work on desktop applications, where Local time makes sense.
This code
var values=new[]{
"2019-12-02T23:00:00+00:00",
"2019-12-02T23:00:00Z",
"2019-12-02T23:00:00"
};
foreach(var value in values)
{
var dt=DateTime.Parse(value);
Console.WriteLine($"{dt:s}\t{dt.Kind}");
}
Produces :
2019-12-03T01:00:00 Local
2019-12-03T01:00:00 Local
2019-12-02T23:00:00 Unspecified
The UTC kind is lost here and, as a developer of flight reservation systems, I don't like that at all. Flight times are in the airport's local time, not my servers. Now I'll have to convert that value back to UTC or something, before saving it to a database. I have to convert it back to the original airport offset to print the tickets and send notifications.
And I'll have to reimburse you guys if there's any error, even if it's due to an airline's error.
JSON.NET (really API) parsing rules
JSON.NET on the other hand, parses strings with Z to UTC, Offsets to Local with conversion and no offset to Undefined. For an API that receives requests from anywhere, UTC is far more useful. Most hosters and cloud services provide UTC machines by default too.
This code :
class It
{
public DateTime Dt{get;set;}
}
var values=new[]{
"{'dt':'2019-12-02T23:00:00+00:00'}",
"{'dt':'2019-12-02T23:00:00Z'}",
"{'dt':'2019-12-02T23:00:00'}"
};
foreach(var value in values)
{
var dt=JsonConvert.DeserializeObject<It>(value).Dt;
Console.WriteLine($"{dt:s}\t{dt.Kind}");
}
Produces :
2019-12-03T01:00:00 Local
2019-12-02T23:00:00 Utc
2019-12-02T23:00:00 Unspecified
Better, but I don't like it. I still lose information.
JSON with DateTimeOffset
DateTimeOffset includes the offset so no information is lost. Unspecified offsets are treated as Local time. This snippet :
class It
{
public DateTimeOffset Dt{get;set;}
}
void Main()
{
var values=new[]{
"{'dt':'2019-12-02T23:00:00+00:00'}",
"{'dt':'2019-12-02T23:00:00Z'}",
"{'dt':'2019-12-02T23:00:00'}"
};
foreach(var value in values)
{
var dt=JsonConvert.DeserializeObject<It>(value).Dt;
Console.WriteLine($"{dt:o}");
}
}
Produces :
2019-12-02T23:00:00.0000000+00:00
2019-12-02T23:00:00.0000000+00:00
2019-12-02T23:00:00.0000000+02:00
Why not UTC everywhere?
Because that loses a lot of information that could easily make that time value unusable. There are a lot of SO questions where the poster tried to compare UTC time and got unexpected results because of DST effects or even DST changes.
A few years back Egypt changed its DST rules with a couple of weeks notice. Airlines and online agents weren't thrilled.
Besides, what if there are more than two timezones in play? International flights rarely land in the same timezone, so storing UTC doesn't work. Airlines don't publish schedules in UTC, they publish them as local time, with offset and IANA TZ names as additional info.
It's worth reading Falsehoods programmers believe about time, especially the parts that refer to UTC or GMT.

Are all Instants comparable or are they machine-dependent in Perl 6?

This is a case where I can find the definition, but I don't quite grasp it. From the official documentation:
An Instant is a particular moment in time measured in atomic seconds, with fractions. It is not tied to or aware of any epoch.
I don't understand how you can specify a particular moment in time without having an epoch? Doesn't it have a reference point? On two different Linux machines it seemed that both Instants referred to seconds since the POSIX Epoch. My guess is that Instants do have an effective start time, but that that start time is implementation/device dependent.
# machine1
say(now * (1/3600) * (1/24) * (1/365.25)); # years from zero point
46.0748226200715
# machine2
say(now * (1/3600) * (1/24) * (1/365.25)); # years from zero point
46.0748712024946
Anyway, so my question is, can Instants be relied upon to be consistent between different processes or are they for "Internal" use only?
say (now).WHAT; # «(Instant)␤»
say (now * 1).WHAT # «(Num)␤»
Any numerical operator will coerce it's operands into Nums. If you want a proper string representation use .perl .
say (now).perl # «Instant.from-posix((<1211194481492/833>, 0))␤»
No matter what platform you are on, Instant.from-posix will always be relative to the Unix epoch.
see: https://github.com/rakudo/rakudo/blob/nom/src/core/Instant.pm#L15
All Instant objects currently on a particular machine are comparable, Instants from different machines may not be.
For practical purposes, on POSIX machines it is currently based on the number of seconds since January 1st, 1970 according to International Atomic Time (TAI), which is currently 36 seconds ahead of Coordinated Universal Time (UTC).
(This should not be relied upon even if you know your code will only ever be run on a POSIX machine)
On another system it may make more sense for it to be based on the amount of time since the machine was turned on.
So after a reboot, any Instants from before the reboot will not be comparable to any after it.
If you want to compare the Instants from different machines, or store them for later use, convert it to a standardized value.
There are several built-in converters you can use
# runtime constant-like term
my \init = INIT now;
say init.to-posix.perl;
# (1454172565.36938, Bool::False)
say init.DateTime.Str; # now.DateTime =~= DateTime.now
# 2016-01-30T16:49:25.369380Z
say init.Date.Str; # now.Date =~= Date.today
# 2016-01-30
say init.DateTime.yyyy-mm-dd eq init.Date.Str;
# True
I would recommend just using DateTime objects if you need more than what is shown above, as it has various useful methods.
my $now = DateTime.now;
say $now.Str;
# 2016-01-30T11:29:14.928520-06:00
say $now.truncated-to('day').utc.Str;
# 2016-01-30T06:00:00Z
# ^
say $now.utc.truncated-to('day').Str;
# 2016-01-30T00:00:00Z
# ^
Date.today and DateTime.now take into consideration your local timezone information, where as now.Date and now.DateTime can't.
If you really only want to deal with POSIX times you can use time which is roughly the same as now.to-posix[0].Int.

Making NSTimeZone Trouble Free in Unit Tests

So here's the problem with doing unit tests of calendar code in Objective-C: the Timezone contains the information about daylight savings, so you write a test in the summer, expecting sunrise on June 10th to occur at 6:02 am. Your test passes, later, you are running the test when it's not DST and you get a failure, because when you get the timezone, daylightsavings is turned off.
There doesn't seem to be a simple way to just tell it to give you the timezone with dst turned on?
I was thinking about doing a category so that I would intercept the timezone call but that sounds super messy as I don't know what date you are manipulating.
Of course, I could write all my tests to check the timezone setting and then just shift all my expectations but that sounds like the worst of all possible choices.
Region-specific timezones must take into account daylight saving time in order to accurately calculate intervals between two dates (and times). If you aren't interested in this, perhaps you could use a UTC “timezone” instead, which don't change at all.
For example, New Zealand Standard Time is defined as UTC+12:00, and New Zealand Daylight Saving Time is defined as UTC+13:00. Although the local time in New Zealand differs during Daylight Saving Time, the times in UTC+12:00 remain the same (that is, every other country that also uses UTC+12:00 don't magically move forward just because Daylight Saving Time has commenced in New Zealand).
You can achieve this simply by providing that UTC offset as the name:
NSTimeZone *utc_plus12 = [NSTimeZone timeZoneWithName:#"UTC+12:00"];
Find out what UTC offset your region's daylight saving time is based on and use that.
I encountered the similar problem. Finally I found OCMock and saved my life.
If you are using Cocoapods, that will be great! Here is the steps:
Edit your Podfile and add a lines to import OCMock.
target 'YourProjectTests' do
pod 'OCMock'
end
Add import in your unit test class
#import <OCMock/OCMock.h>
Write your test case like this
- (void)testLocalTimezoneFromPDT {
NSTimeZone* timeZone = [NSTimeZone timeZoneWithAbbreviation:#"PDT"];
id timeZoneMock = OCMClassMock([NSTimeZone class]);
OCMStub([timeZoneMock localTimeZone]).andReturn(timeZone);
// Implement your test case
// XCTAssertEqual(...);
}
That code will mock the original [NSTimeZone localTimeZone] method and return
the static value.
In this example, we return the timezone from Pacific Daylight Time (PDT) [GMT-07:00].
When your class call [NSTimeZone localTimeZone], your class will get the timezone we set with OCMock.
Hopes this answer will help you.

GPS position to Timezone

I'd like to know the local time where my user is sending his request from.
Basically, is there such a thing as a function like this
var localTime = getLocalTime( lat, long );
I'm not sure if a simple division on the lat could work, since most of the countries don't have perfect geometric shapes.
Any help would be great. Any language is accepted. I'd like to avoid calling distant APIs.
The Google Time Zone API seems to be what you're after. It, however does not have any free tier.
The Time Zone API provides time offset data for locations on the surface of the earth. Requesting the time zone information for a specific Latitude/Longitude pair will return the name of that time zone, the time offset from UTC, and the Daylight Savings offset.
The shapefile used to compute the timezone is not maintained anymore.
I just faced the same issue today, and I am not sure how relevant my answer is after all this time, but I basically just wrote a Python function that does what you want. You can find it here.
https://github.com/cstich/gpstotz
Edit:
As mentioned in the comments I should also post code. The code is based on Eric Muller's shapefile of timezones, which you can get here - http://efele.net/maps/tz/world/.
Edit 2:
As it turns out shapefiles have a somewhat archaic definition of exterior and interior rings (basically exterior rings are using the right hand rule, while interior rings are using the left hand rule). In any case fiona seems to take care of that and I updated the code accordingly.
from rtree import index # requires libspatialindex-c3.deb
from shapely.geometry import Polygon
from shapely.geometry import Point
import os
import fiona
''' Read the world timezone shapefile '''
tzshpFN = os.path.join(os.path.dirname(__file__),
'resources/world/tz_world.shp')
''' Build the geo-index '''
idx = index.Index()
with fiona.open(tzshpFN) as shapes:
for i, shape in enumerate(shapes):
assert shape['geometry']['type'] == 'Polygon'
exterior = shape['geometry']['coordinates'][0]
interior = shape['geometry']['coordinates'][1:]
record = shape['properties']['TZID']
poly = Polygon(exterior, interior)
idx.insert(i, poly.bounds, obj=(i, record, poly))
def gpsToTimezone(lat, lon):
'''
For a pair of lat, lon coordiantes returns the appropriate timezone info.
If a point is on a timezone boundary, then this point is not within the
timezone as it is on the boundary. Does not deal with maritime points.
For a discussion of those see here:
http://efele.net/maps/tz/world/
#lat: latitude
#lon: longitude
#return: Timezone info string
'''
query = [n.object for n in idx.intersection((lon, lat, lon, lat),
objects=True)]
queryPoint = Point(lon, lat)
result = [q[1] for q in query
if q[2].contains(queryPoint)]
if len(result) > 0:
return result[0]
else:
return None
if __name__ == "__main__":
''' Tests '''
assert gpsToTimezone(0, 0) is None # In the ocean somewhere
assert gpsToTimezone(51.50, 0.12) == 'Europe/London'
I was searching for the same thing couple of days ago and unfortunately I could not find an API or a simple function that does it. The reason being as you said that countries do not have perfect geometric shapes. You have to create a representation of the area of each time zone and see where your point lies. I think this will be a pain and I have no idea if it can be done at all.
The only one I found is described here: Determine timezone from latitude/longitude without using web services like Geonames.org . Basically you need a database with information about timezones and you are trying to see which one is closest to your point of interest.
However, I was looking for static solutions(without using internet), so if you can use internet connection you can use: http://www.earthtools.org/webservices.htm which provides a webservice to give you the timezone given lat/lon coordinates.
As of 2019, Google API does not have any free tier and the data source of #cstich answer is not maintained anymore.
If you want an API, timezonedb.com offers a free tier rate limited to 1 request/second.
The original maintainer of the data used by #cstich link to this project which retrieve data from OpenStreetMap. The readme contains link to look up libraries in a wide variety of languages.
Couldn't you simply use the user IP to determine which they live in ? And then you use an array of (Countries | Difference with GMT) to get the local time.

How to change time and timezone in iPhone simulator?

How do I change time and time zone in the iPhone simulator?
I'm guessing it uses your system timezone, so changing the TZ in System Preferences would probably do the trick
You can set the TZ environment variable in an Xcode Scheme to set the time zone just for that app.
You can use UTC, PST, EST, as well as place-based timezone names such as America/Los_Angeles. It's not well documented, but I suspect any time zone name should work.
It's not well documented, but the source is an Apple Developer Support rep on the developer forums.
Restart the Simulator after changing the system date time preferences and you shall see the changes reflected. It worked for me.
For the purpose of taking a screenshot, Apple finally made it possible to override time on the status bar of iOS simulator (since Xcode 11) by using simctl tool:
xcrun simctl status_bar "iPhone Xs" override --time "21:08"
Here is a solution available from iOS 13 and Xcode 11 at least. (did not test with previous versions)
[Edit] This will change only the status bar as in the comments!
By default the iOS Simulator shows whatever the time is on your Mac, however, you can use Xcode’s command line to override that with this command in the terminal:
xcrun simctl status_bar "iPhone 11 Pro Max" override --time '9:41'
Replace the simulator name with the device you want to change.
For the status bar you have this overrides:
You may specify any combination of these flags (at least one is required):
--time <string>
Set the date or time to a fixed value.
If the string is a valid ISO date string it will also set the date on relevant devices.
--dataNetwork <dataNetworkType>
If specified must be one of 'wifi', '3g', '4g', 'lte', 'lte-a', or 'lte+'.
--wifiMode <mode>
If specified must be one of 'searching', 'failed', or 'active'.
--wifiBars <int>
If specified must be 0-3.
--cellularMode <mode>
If specified must be one of 'notSupported', 'searching', 'failed', or 'active'.
--cellularBars <int>
If specified must be 0-4.
--batteryState <state>
If specified must be one of 'charging', 'charged', or 'discharging'.
--batteryLevel <int>
If specified must be 0-100.
The time can be any string. But if you want the device to show the date you will need use the ISO format.
For example a valid ISO date string would be '2007-01-09T10:41:00+01:00'
Otherwise you can use the time parameter as a string and it will display whatever you pass in it.
With thanks to the original post by Paul Hudson Here's the link!
This is an old thread but it is closest to my question. I need to simulate time zone for Europe, this method works for me. Go to 'TimeZone' tap instead of 'Date&Time' tap. Uncheck the 'Set time zone automatically using current location' box and slide the vertical rectangle bar (with blue dot on it) to simulate your system time.
When changing the timezone, I found the easiest way to do it was by clicking the clock in the menubar. And then selecting "Open Date & Time Preferences" then select the tab Time Zone.
Alternatively System Preferences -> Date and Time and select the tab Time Zone.
Just a pointer for anyone that might not know their way around OSX.
I have proposed an automatic solution to the problem of changing the time that includes hacky method swizzling: https://stackoverflow.com/a/34793193/829338. I assume that should also work for changing the time zone accordingly.
I needed to test my app automatically, which required changing the sys time. I did, what Tom suggested: happy hacky method swizzling.
For demonstrative purposes, I only change [NSDate date] but not [NSDate dateWithTimeIntervalSince1970:].
First your need to create your class method that serves as the new [NSDate date]. I implemented it to simply shift the time by a constant timeDifference.
int timeDifference = 60*60*24; //shift by one day
NSDate* (* original)(Class,SEL) = nil;
+(NSDate*)date{
NSDate* date = original([NSDate class], #selector(date));
return [date dateByAddingTimeInterval:timeDifference];
}
So far, pretty easy. Now comes the fun part. We get the methods from both classes and exchange implementation (it worked for me in the AppDelegate, but not in my UITests class). For this you will need to import objc/runtime.h.
Method originalMethod = class_getClassMethod([NSDate class], #selector(date));
Method newMethod = class_getClassMethod([self class], #selector(date));
//save the implementation of NSDate to use it later
original = (NSDate* (*)(Class,SEL)) [NSDate methodForSelector:#selector(date)];
//happy swapping
method_exchangeImplementations(originalMethod, newMethod);
My build server is UTC and some of my unit tests needed the timezone to be PST. Using a category on NSTimeZone you can override Apple's implementation to use your code. Works for swift only projects too.
//NSTimeZone+DefaultTimeZone.h
#import <Foundation/Foundation.h>
#interface NSTimeZone (DefaultTimeZone)
+(NSTimeZone *)defaultTimeZone;
#end
//NSTimeZone+DefaultTimeZone.m
#import "NSTimeZone+DefaultTimeZone.h"
#implementation NSTimeZone (DefaultTimeZone)
+(NSTimeZone *)defaultTimeZone
{
return [NSTimeZone timeZoneWithName:#"America/Los_Angeles"];
}
#end
Only working solution for now. XCode does provide option to set a Time Zone for a particular app.
In XCode, Click on your app, Edit Scheme -> Run configuration -> Arguments Tab -> Add Environment Variables
Create a variable with Name: TZ, Value: CST (Any other standard format. XCode didn't explicitly mention the allowed values. But you can use America/Chicago too)
After changing the system date time preferences I had to choose Hardware > Reset All Content And Settings.
Only this worked for me in Version 10.3 (SimulatorApp-880.5 CoreSimulator-681.5.4).