Context
This is an Android App running on API 29 (with a Min of 23).
I'm using ThirteenTenABP
Issue
I'm simply debugging some code and I do the following:
val zonedDateTime = ZonedDateTime.of(
LocalDateTime.of(1900, 1, 1, 15, 15, 0),
ZoneId.of("Europe/Amsterdam"))
This prints (toString() as:
1900-01-01T15:15+00:19:32[Europe/Amsterdam]
Expected
I would have expected to see 1900-01-01T15:15+02:00:00[Europe/Amsterdam] or similar.
The current Offset from UTC in Amsterdam is +2 due to daylight savings. Yet, I see these 19 minutes and 32 seconds.
This means that if I convert that Zoned date time to UTC using something like:
val utcZoned = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC)
I get (consistently with the error above):
1900-01-01T14:55:28Z
So it's 14:55:28 or what is equivalent to the time 15:15 (3:15 pm) minus 19 minutes and thirty-two seconds.
Am I missing something here?
I'm running this on an Emulator. ZoneId.getSystemDefault() also returns the same ZoneId + Offset. I started hardcoding Amsterdam to see if I could spot a difference.
Another way to see this, is simply by doing:
ZonedDateTime.of(LocalDateTime.of(1900,01,01,15,15,0), ZoneId.of("Europe/Amsterdam")).offset
The result is a ZoneOffset
And consistently with the ninteen minutes above:
The offset is: +00:19:32
What am I doing wrong here?
Did I try java.time.*?
Yes, I removed ThirteenTenABP, and replaced all imports to java.time.* and ran this on an Android O (8.x) to see if the native Java8 time classes would yield a similar result and the answer is: yes.
The offsets come from the offset rules:
But why those numbers?
Looks like that's correct, Amsterdam's timezone used to be based off of the time in Westerkerk.
The reason for the specific offset of +0h 19m 32.13s was that the time zone was centered on the mean solar time of the Westertoren (4° 53' 01.95" E Longitude), the tower of the Westerkerk church in Amsterdam.
https://en.wikipedia.org/wiki/UTC%2B00:20
Bare in mind the docs for ZonedDateTime.of say
The local date-time is then resolved to a single instant on the time-line
So it will give you back a time using the timezone that was in use at that instant in time, not the current timezone in use in the Netherlands.
Related
I am using an android app that retrieves a user's weight(s) for a given time range. The user can manually sync it for a given day and it will use mm/dd/yyyy 00:00:00 and mm/dd/yyyy 23:59:59:999. In most cases this works correctly, however I have a workflow that seems to break it. I have a Wifi scale which I will weigh myself on, and then I have those weights synced to MyFitnessPal. I then have MyFitnessPal synced to Google Fit. This integration seems to work, as my weight is correctly displayed in Google Fit. However, MyFitnessPal seems to only save the date of the weighing and not the time. When I run the manually sync process for this android app on the same day that the weighing happened it does not return the weight.
I've tried to get the data from both android and the Google API Explorer. I can get past weights by changing my startTimeMillis, but I can't get the weight for the current date. It's as if the weight on the phone hasn't synced with the Google Fit data store, but even if that was the case I'd expect the android app to retrieve the weight.
Java code:
DataReadRequest.Builder()
.aggregate(DataType.TYPE_WEIGHT, DataType.AGGREGATE_WEIGHT_SUMMARY)
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startMillis, endMillis, TimeUnit.MILLISECONDS)
.enableServerQueries()
.build()
API Explorer call:
{
"aggregateBy": [
{
"dataSourceId": "derived:com.google.weight:com.google.android.gms:merge_weight"
}
],
"endTimeMillis": "1560488399999",
"startTimeMillis": "1560402000000"
}
My results from the API Explorer are here:
{
"bucket": [
{
"startTimeMillis": "1560402000000",
"endTimeMillis": "1560488399999",
"dataset": [
{
"dataSourceId": "derived:com.google.weight:com.google.android.gms:merge_weight",
"point": [
]
}
]
}
]
}
I would expect a weight to be listed as there exists a weight for that startTimeMillis (1560402000000 milliseconds since epoch is Thursday, Jun 13, 12:00 AM local time) property in my Google Fit app. The date for the weight listed is Thursday, Jun 13, 12:00 AM, however the API Explorer returned nothing.
That returns max, min and average. If you need to return all of them then user "read(DataType.TYPE_WEIGHT)" instead of "aggregate"
I'm successfully registering a SipProfile with 1 hour expiration. I see the REGISTER message in Wireshark on the SIP server machine, re-sent with authorization.
Brekeke replies immediately with a STATUS: 200 OK, passing back 'Expires:3600'.
Both Xamarin and Android docs tell me that lExpire should be duration in seconds before the registration expires, so it represents an interval. I want to expose received values, so multiply by TimeSpan.TicksPerSecond (to convert to TimeSpan), 3600 would result in 0.01:00:00:
void ISipRegistrationListener.OnRegistrationDone( string lclPrfUri, long lExpire )
{
long l= DateTime.Now.Ticks;
double d= (double)l / lExpire;
string s= string.Format( "RegSucced( '{0}', {1} ), {2}, {3}", lclPrfUri, lExpire,
new TimeSpan( lExpire * TimeSpan.TicksPerSecond )
.ToString( "d\\.hh\\:mm\\:ss" ), d );
..
}
But i'm getting lExpire values in the range of 1.5 trillions (1'541'195'345'242)!
As i saw it constantly growing with time, i thought it might be related to Ticks, so i exposed both and gathered the following statistics (H == T + 1 hour -- expected expiration):
# DT.Now.Ticks (T) lExpire (E) T/E ratio T+36000000000 (H) H/E ratio
1 636767624266839520 1541183611836 413167.918 636767660266839520 413167.941
2 636767669188704010 1541188122398 413166.738 636767705188704010 413166.761
3 636767670260843180 1541188229643 413166.710 636767706260843180 413166.733
4 636767670974718350 1541188301027 413166.691 636767706974718350 413166.715
5 636767693193745790 1541190522922 413166.110 636767729193745790 413166.133
And the ratios look surprisingly consistent, though the magic of 413166 escapes me.. And from that lExpire looks more like a reference to a point in time, than an interval, no?
But according to docs i should get 3600 without any scaling factors, right? What is going on??!
UPDATE (2019-Jan-25)
Finally got closer to an answer. Digging through Android source files (e.g. https://android.googlesource.com/platform/frameworks/base/+/431bb2269532f2514861b908d5fafda8fa64da79/voip/java/com/android/server/sip/SipService.java) i found the following fragment:
#Override
public void onRegistrationDone(ISipSession session, int duration) {
if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
synchronized (SipService.this) {
if (notCurrentSession(session)) return;
mProxy.onRegistrationDone(session, duration);
if (duration > 0) {
mSession.clearReRegisterRequired();
mExpiryTime = SystemClock.elapsedRealtime() + (duration * 1000);
..
SystemClock.elapsedRealtime() returns milliseconds since boot, including time spent in sleep.
Time in UNIX/Linux/Java is kept as number of seconds since epoch (1970-01-01T00:00:00Z).
Wikipedia's page shows Current Unix time as 1548450313 (2019-01-25T21:05:13+00:00), which is only a 1000 times (s-to-ms multiplier!) different in range from lExpire values i observe. The formula for mExpiryTime in the last line kinda gives hope.. "Eureka!"?
Let's check if they conform to "# of ms since epoch" theory:
DateTime dtEpoch = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc );
void ISipRegistrationListener.OnRegistrationDone( string lclPrfUri, long lExpire )
{
DateTime dt = dtEpoch.AddMilliseconds( lExpire ).ToLocalTime( );
string s= string.Format( "RegSucced( '{0}', {1}, {2} )", lclPrfUri, lExpire,
dt.ToString( "yyyy-MM-dd HH:mm:ss.fff" ) );
..
}
Yesterday's screenshots - with that addition (magenta arrows mark exposed values):
As you can see, the difference between lExpire converted into time and DateTime.Now (captured in the log) is negligible - in ms range!
I actually like the idea of being given an expiration moment instead of duration (which need to be added to an undefined starting point). Was about to answer my question..
But still, there are follow-up unresolved mysteries:
Why documentation says the argument is duration in seconds?
Xamarin may be just copying Android docs, but the source is way wrong!
Is this really how lame Android devs are? Reckon that should be rethorical ((..
Does anybody care that requested expiration period may be trumped [down] by PBX /SIP server, and thus they have to re-register more often?
All calls to .onRegistrationDone(session, duration) in the source files specify duration, not mExpiryTime.
There are constant definitions (EXPIRY_TIME = 3600, SHORT_EXPIRY_TIME = 10, MIN_EXPIRY_TIME = 60) and comparisons of duration to these..
Range of values is drastically different between the two (3600 vs 15 trillion).
How/where does mExpiryTime (expiration moment) make it into the argument of my method?
ms since boot and s (or ms) since epoch are still very different, what makes that adjustment?
and finally, asking a 1 hr expiration and receiving it in the OK reply from SIP server, i expect that to be reflected here too, so lExpire should be 1 hr in the future, not now!!
Any ideas?
I have been using std::chrono::steady_clock for interval calculation in an application i am making for Android platform.
Code:
// On application start
auto timeSinceEpoch = std::chrono::steady_clock::now().time_since_epoch();
auto timeInSec = std::chrono::duration_cast<seconds>(timeSinceEpoch).count();
log("On Enter Start Time Point - %lld", timeInSec);
Output:
On Enter Start Time Point - 521
Now i switch off the phone and restart the phone. I run my application and this time Output is:
On Enter Start Time Point - 114
As per definition at cppreference.com
"Class std::chrono::steady_clock represents a monotonic clock. The time points of this clock cannot decrease as physical time moves forward."
How is the output when i restart the phone giving lesser value?
If anyone has faced this issue please help me out here. Thanks!!
The formal requirement for a steady clock is that the result of a call to now() that happens before another call to now() is always less than or equal to the result of the second call. The happens before relationship only applies to actions within a program run. A steady clock is not required to be steady across different invocations of a program.
On Android, AFAICT steady_clock is the same as (from Java) System.Clock.elapsedRealtime, which resets to zero on boot -- https://developer.android.com/reference/android/os/SystemClock.html
I'm totally failing to dig up the source code for clock_gettime, though. https://android.googlesource.com/platform/ndk.git/+/43255f3d58b03cd931d29d1ee4e5144e86e875ce/sources/cxx-stl/llvm-libc++/libcxx/src/chrono.cpp#124 shows it calling clock_gettime(CLOCK_MONOTONIC), but I'm not sure how to penetrate the veil from there.
I'm reading timestamp values from SensorEvent data but I can't work out the reference time for these values. Android documentation just says "The time in nanosecond at which the event happened" As an example:
My current Android device date, October 14th 2011 23:29:56.421 (GMT+2)
System.currentTimeMillis * 1000000 (nanosec) = 1318627796431000000 (that's ok)
sensorevent.timestamp (nanosec) = 67578436328000 = 19 hours 46 min ????
May you help me?
thanks
It appears that what you are dealing with is the number of nanoseconds since the operating system started, also known as "uptime".
Further info on the issue: http://code.google.com/p/android/issues/detail?id=7981
I should add that the linked question SensorEvent.timestamp to absolute (utc) timestamp? deals with the same issue and is where I found the answer.
I know that it's a very old question, but, I'm also struggling for converting SensorEvent.timestamp to a human readable time. So I'm writing here what I've understood so far and how I'm converting it in order to get better solutions from you guys. Any comments will be welcomed.
As I understood, SensorEvent.timestamp is an elapsed time since the device's boot-up. So I have to know the uptime of the device. So if there is an API returning device's boot-up, it will be very easy, but, I haven't found it.
So I'm using SystemClock.elapsedRealtime() and System.currentTimeMillis() to 'estimate' a device's uptime. This is my code.
private long mUptimeMillis; // member variable of the activity or service
...
atComponentsStartUp...() {
...
/* Call elapsedRealtime() and currentTimeMillis() in a row
in order to minimize the time gap */
long elapsedRealtime = SystemClock.elapsedRealtime();
long currentTimeMillis = System.currentTimeMillis();
/* Get an uptime. It assume that elapsedRealtime() and
currentTimeMillis() are called at the exact same time.
Actually they don't, but, ignore the gap
because it is not a significant value.
(On my device, it's less than 1 ms) */
mUptimeMillis = (currentTimeMillis - elapsedRealtime);
....
}
...
public void onSensorChanged(SensorEvent event) {
...
eventTimeMillis = ((event.timestamp / 1000000) + mUptimeMillis);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(eventTimeMillis);
...
}
I think this works for Apps that a millisecond time error is okey. Please, leave your ideas.
I would like in my application to find a way to synch the date and time with something given by an external source.
I don't want to use the phone time because I might get a difference of maybe 5 minutes around real time. and 5 minutes extra or less = 10 minutes!
I have heard about time information in the GPS satellites or in Network antennas.
I have tried with System.getCurrentTime but i get the current the of the device, so, if my device is set up 5 minutes earlier, it display the wrong time.
EDIT
To make a short question: how can I get this time?
I didn't know, but found the question interesting. So I dug in the android code... Thanks open-source :)
The screen you show is DateTimeSettings. The checkbox "Use network-provided values" is associated to the shared preference String KEY_AUTO_TIME = "auto_time"; and also to Settings.System.AUTO_TIME
This settings is observed by an observed called mAutoTimeObserver in the 2 network ServiceStateTrackers:
GsmServiceStateTracker and CdmaServiceStateTracker.
Both implementations call a method called revertToNitz() when the settings becomes true.
Apparently NITZ is the equivalent of NTP in the carrier world.
Bottom line: You can set the time to the value provided by the carrier thanks to revertToNitz().
Unfortunately, I haven't found a mechanism to get the network time.
If you really need to do this, I'm afraid, you'll have to copy these ServiceStateTrackers implementations, catch the intent raised by the framework (I suppose), and add a getter to mSavedTime.
Get the library from http://commons.apache.org/net/download_net.cgi
//NTP server list: http://tf.nist.gov/tf-cgi/servers.cgi
public static final String TIME_SERVER = "time-a.nist.gov";
public static long getCurrentNetworkTime() {
NTPUDPClient timeClient = new NTPUDPClient();
InetAddress inetAddress = InetAddress.getByName(TIME_SERVER);
TimeInfo timeInfo = timeClient.getTime(inetAddress);
//long returnTime = timeInfo.getReturnTime(); //local device time
long returnTime = timeInfo.getMessage().getTransmitTimeStamp().getTime(); //server time
Date time = new Date(returnTime);
Log.d(TAG, "Time from " + TIME_SERVER + ": " + time);
return returnTime;
}
getReturnTime() is same as System.currentTimeMillis().
getReceiveTimeStamp() or getTransmitTimeStamp() method should be used.
You can see the difference after setting system time to 1 hour ago.
local time :
System.currentTimeMillis()
timeInfo.getReturnTime()
timeInfo.getMessage().getOriginateTimeStamp().getTime()
NTP server time :
timeInfo.getMessage().getReceiveTimeStamp().getTime()
timeInfo.getMessage().getTransmitTimeStamp().getTime()
Try this snippet of code:
String timeSettings = android.provider.Settings.System.getString(
this.getContentResolver(),
android.provider.Settings.System.AUTO_TIME);
if (timeSettings.contentEquals("0")) {
android.provider.Settings.System.putString(
this.getContentResolver(),
android.provider.Settings.System.AUTO_TIME, "1");
}
Date now = new Date(System.currentTimeMillis());
Log.d("Date", now.toString());
Make sure to add permission in Manifest
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
NITZ is a form of NTP and is sent to the mobile device over Layer 3 or NAS layers.
Commonly this message is seen as GMM Info and contains the following informaiton:
Certain carriers dont support this and some support it and have it setup incorrectly.
LAYER 3 SIGNALING MESSAGE
Time: 9:38:49.800
GMM INFORMATION 3GPP TS 24.008 ver 12.12.0 Rel 12 (9.4.19)
M Protocol Discriminator (hex data: 8)
(0x8) Mobility Management message for GPRS services
M Skip Indicator (hex data: 0)
Value: 0
M Message Type (hex data: 21)
Message number: 33
O Network time zone (hex data: 4680)
Time Zone value: GMT+2:00
O Universal time and time zone (hex data: 47716070 70831580)
Year: 17
Month: 06
Day: 07
Hour: 07
Minute :38
Second: 51
Time zone value: GMT+2:00
O Network Daylight Saving Time (hex data: 490100)
Daylight Saving Time value: No adjustment
Layer 3 data:
08 21 46 80 47 71 60 70 70 83
15 80 49 01 00
This seemed to work for me:
LocationManager locMan = (LocationManager) activity.getSystemService(activity.LOCATION_SERVICE);
long networkTS = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER).getTime();
Working on Android 2.2 API (Level 8)
Now you can get time for the current location but for this you have to set the system's persistent default time zone.setTimeZone(String timeZone) which can be get from
Calendar calendar = Calendar.getInstance();
long now = calendar.getTimeInMillis();
TimeZone current = calendar.getTimeZone();
setAutoTimeEnabled(boolean enabled)
Sets whether or not wall clock time should sync with automatic time updates from NTP.
TimeManager timeManager = TimeManager.getInstance();
// Use 24-hour time
timeManager.setTimeFormat(TimeManager.FORMAT_24);
// Set clock time to noon
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.HOUR_OF_DAY, 12);
long timeStamp = calendar.getTimeInMillis();
timeManager.setTime(timeStamp);
I was looking for that type of answer I read your answer but didn't satisfied and it was bit old. I found the new solution and share it. :)
For more information visit: https://developer.android.com/things/reference/com/google/android/things/device/TimeManager.html
I read that this
LocationManager locMan = (LocationManager) activity.getSystemService(activity.LOCATION_SERVICE);
long time = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER).getTime();
provides correct time, without internet at the cost of some blocking processing.
the time signal is not built into network antennas: you have to use the NTP protocol in order to retrieve the time on a ntp server. there are plenty of ntp clients, available as standalone executables or libraries.
the gps signal does indeed include a precise time signal, which is available with any "fix".
however, if nor the network, nor the gps are available, your only choice is to resort on the time of the phone... your best solution would be to use a system wide setting to synchronize automatically the phone time to the gps or ntp time, then always use the time of the phone.
note that the phone time, if synchronized regularly, should not differ much from the gps or ntp time. also note that forcing a user to synchronize its time may be intrusive, you 'd better ask your user if he accepts synchronizing. at last, are you sure you absolutely need a time that precise ?