What does "-04:00" in Google Calendar's Event.Datetime mean? - android

No matter how I change the time zone of an Event in Google Calendar, there's always a "-04:00" string trailing behind the Start and End's DateTime attribute when pulled using the Google Calendar API on Android. What I do is I create an Event from the web and view the results on an Android emulator.
For example:
If I create an Event whose time zone is in Toronto, Canada (FYI, which is the Calendar's time zone), it will show
"dateTime" : "2012-04-25T10:00:00.000-04:00",
If I change it to Taipei, Taiwan, it will show
"dateTime" : "2012-04-24T22:00:00.000-04:00",
The hours change automatically, which is great for implementation, but what does that "-04:00" stand for? (FYI, Toronto's time zone is "GMT-05:00")

It's the UTC offset, telling you which time zone the event's time is being expressed in. That's probably coming from the time zone setting of the calendar. Toronto is currently at UTC-4:00, meaning it's four hours behind the UTC clock. So when you moved the event to Taipei, you kept it at the same local time (10 AM on the 25th), but the API expresses it in Toronto hours; i.e. the event will happen when it's 10 PM on the 24th in Toronto.
Toronto's time zone is GMT-5:00 (UTC and GMT are roughly equivalent) because without daylight savings, that's the offset. The API knows that Toronto is currently on daylight savings, though, and changes the offset accordingly.
It's also worth noting that this date/time format is ISO 8601 and is the usual way of representing time in APIs.

Related

Android : Why Time Zone with id "EST" returns false for useDaylightTime()

In my Android app why EST time zone always returns false for useDaylightTime(). Its found that EST uses daylight saving but the method is always returning false (Reference : time zone list).
TimeZone tz = TimeZone.getTimeZone("EST");
if(tz.useDaylightTime()){
//Never enters here
}
Please anyone can help me to figure out what's going wrong? I don't know much about the topic I appreciate any effort to get an Idea about this one.
Some more information regarding the situation i am facing: I know "EDT" is the ID used for time zone representing Daylight saving time zone but in my case, The Time zone Id is received from the server side and I am using that one to know do daylight saving is applicable on that time zone.
Don’t rely on three and four letter time zone abbreviations. They are ambiguous. EST may refer to North American Eastern Standard Time or Australian Eastern Standard Time (don’t know if there are more). Instead use a time zone ID in the region/city format, for example America/New_York or Australia/Sydney. See Wikipedia for list.
To answer your question: TimeZone interprets EST as a fixed UTC offset of -05:00. So it hasn’t got any daylight saving time. You may say that it’s logical: daylight saving time isn’t standard time. The abbreviation for Eastern Daylight Saving Time would have been EDT instead.
ZoneId
Now we’re at it, the TimeZone class is outdated and poorly designed. If you can, look into java.time, the modern Java date and time API, and its ZoneId class instead.
ZoneId zoneId = ZoneId.of( "America/New_York" ) ;
ZoneRules rules = zoneId.getRules() ;
boolean isNowInDst = rules.isDaylightSavings( Instant.now() ) ;
In order to get eastern time to return true don't use the ID for that standard time use "America/New_York" instead:
TimeZone timeZone = TimeZone.getTimeZone("America/New_York");
if(timeZone.useDaylightTime()){
Log.d(TAG, "Uses daylight savings time...");
}

How to convert LocalDateTime to UTC and back, without loading zones?

Background
I'm using threetenbp backport for Android (here), to handle various time related data operations.
One of them is to convert a time to a different timezone (current to UTC and back).
I know this is possible if you use something like that:
LocalDateTime now = LocalDateTime.now();
LocalDateTime nowInUtc = now.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime();
This works just fine, and it's also quite easy to do the opposite.
The problem
I'm trying to avoid initialization of the library, which loads quite a large file of zones into it. I've already figured out how to handle various date/time related operations without this, except this case of converting to UTC and back.
What I got has an error of a whole 1 hour off from the correct conversion.
What I've tried
This is what I've found and tried:
// getting the current time, using current time zone:
Calendar cal = Calendar.getInstance();
LocalDateTime now = LocalDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND) * 1000000);
//the conversion itself, which is wrong by 1 hour in my tests:
LocalDateTime alternativeNowInUtc = now.atZone(ZoneOffset.ofTotalSeconds(TimeZone.getDefault().getRawOffset() / 1000)).withZoneSameInstant(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))).toLocalDateTime();
The question
What's wrong exactly with what I wrote? How can I get an alternative code for converting the time without initialization of the library?
Given an instance of LocalDateTime as input, how can I convert it from current timezone to UTC, and from UTC to current timezone ?
This is probably happening because your JVM's default timezone is in Daylight Saving Time (DST).
To get the correct offset, you should check if the timezone is in DST and add this to the offset:
Calendar cal = Calendar.getInstance();
TimeZone zone = TimeZone.getDefault();
// if in DST, add the offset, otherwise add zero
int dst = zone.inDaylightTime(cal.getTime()) ? zone.getDSTSavings() : 0;
int offset = (zone.getRawOffset() + dst) / 1000;
LocalDateTime alternativeNowInUtc = now.atZone(ZoneOffset.ofTotalSeconds(offset))
.withZoneSameInstant(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0)))
.toLocalDateTime();
Another way to create the nowInUtc as a LocalDateTime is to create an Instant from the Calendar:
LocalDateTime nowInUtc = Instant.ofEpochMilli(cal.getTimeInMillis())
.atOffset(ZoneOffset.ofHours(0)).toLocalDateTime();
Actually, you don't need the Calendar at all, just use Instant.now() to get the current instant:
LocalDateTime nowInUtc = Instant.now().atOffset(ZoneOffset.ofHours(0)).toLocalDateTime();
Or, even shorter, use an OffsetDateTime directly:
LocalDateTime nowInUtc = OffsetDateTime.now(ZoneOffset.ofHours(0)).toLocalDateTime();
Not sure if any of those loads timezone data, it's up to you to test.
And I think that the constant ZoneOffset.UTC can be used instead of ZoneOffset.ofHours(0), because it won't load tz data as well (but I haven't tested it).
Final solution
Assuming the default timezone is in Israel (TimeZone.getDefault() is Asia/Jerusalem):
// April 11th 2018, 3 PM (current date/time in Israel)
LocalDateTime now = LocalDateTime.of(2018, 4, 11, 15, 0, 0);
TimeZone zone = TimeZone.getDefault();
// translate DayOfWeek values to Calendar's
int dayOfWeek;
switch (now.getDayOfWeek().getValue()) {
case 7:
dayOfWeek = 1;
break;
default:
dayOfWeek = now.getDayOfWeek().getValue() + 1;
}
// get the offset used in the timezone, at the specified date
int offset = zone.getOffset(1, now.getYear(), now.getMonthValue() - 1,
now.getDayOfMonth(), dayOfWeek, now.getNano() / 1000000);
ZoneOffset tzOffset = ZoneOffset.ofTotalSeconds(offset / 1000);
// convert to UTC
LocalDateTime nowInUtc = now
// conver to timezone's offset
.atOffset(tzOffset)
// convert to UTC
.withOffsetSameInstant(ZoneOffset.UTC)
// get LocalDateTime
.toLocalDateTime();
// convert back to timezone
LocalDateTime localTime = nowInUtc
// first convert to UTC
.atOffset(ZoneOffset.UTC)
// then convert to your timezone's offset
.withOffsetSameInstant(tzOffset)
// then convert to LocalDateTime
.toLocalDateTime();
The answer of carlBjqsd is okay, just awkward and should maybe a little bit clearer.
Why one hour difference
See the final solution of #carlBjqsd: It uses the expression
int offset = zone.getOffset(1, now.getYear(), now.getMonthValue() - 1, now.getDayOfMonth(), dayOfWeek, now.getNano() / 1000000);
instead of
getRawOffset().
That has caused the difference of one hour you observed. Applications have normally no need only to calculate with the raw offset which leaves out the dst-offset for some periods of the year. It is only the total offset which matters in any conversion from local timestamp to UTC and back. The main purpose of the fine-granular differentiation of partial offsets like raw offsets or dst offsets is just proper naming of the zone (shall we call it standard time or not?).
Misleading title of question: "without loading zones"
No, you can never avoid loading zones if you want to convert between local timestamps and UTC using zones. Your real question is rather: How to avoid loading the zones of ThreetenABP and to use/load the zones of the Android platform instead. And your motivation seems to be:
I'm trying to avoid initialization of the library, which loads quite a
large file of zones into it
Well, I have not measured which zone data have more impact on performance. I can only say based on my studies and knowledge of the source code of involved libraries that java.time and ThreetenBP load the whole file TZDB.dat into a binary array cache in memory (as first step) and then pick out the relevant part for a single zone (i.e. interprete a part of the binary data array via deserialization into a set of zone rules and finally a single ZoneId). Old Java platforms instead work with a set of different zi-files (one for each zone), and I suspect that Android zones behave in a similar way (but please correct me if you know that detail better).
If only ONE zone shall be used at all then the traditional approach of using separate zone files might be better but once you want to iterate over all available zones then it is better to have only one zone file at all.
Personally, I think that the performance aspect is neglectable. If you use the Android zones you will also have some loading times, inevitably. In case you really want to speed up the initialization time of ThreetenABP, you should consider to load it in a background thread.
Are Android zones and ThreetenABP zones equivalent?
Generally not. Both timezone repositories might give the same offset for a concrete zone. And often they do so but sometimes there will be differences which are not under your control. Although both timezone repositories use the data of iana.org/tz in final consequence, differences are mainly caused by possible different versions of tzdb-data. And you cannot control which version of zone data exists on the Android platform because this is up to the user of mobile phone how often he/she updates the Android OS. And this is also true for the data of ThreetenABP. You can offer the latest version of your app including the latest version of ThreetenABP but you cannot control if the mobile device user really updates the app.
Other reasons why to care about choosing the proper tz repository?
Beyond performance and initialization times, there is indeed one special scenario which might be interesting for the choice. If the Android OS is somehow old and uses an outdated version of zone rules then some mobile phone users do not update their operating system but manipulate the device clock in order to compensate the wrong timezone data. This way, they still get the correct local times on the mobile phone (in ALL apps).
In this scenario, ThreetenABP does not offer a good solution because combining their correct zone data with wrong device clock will result in wrong local timestamps (annoying the user). This has been a problem for example in Turkey which changed the dst-rules not a long time ago.
Using just the old calendar and timezone API of Android (in the package java.util) can take into account the problem so correct local timestamps are created. However, if an app communicates UTC-times (for example as count of millisecs since 1970-01-01T00Z) to other hosts (for example servers) then the wrong device clock is still a problem.
We could say why bother because the user has done "nonsense" with the device configuration but we also live in real world and should think about how to make even such users happy. So when thinking about a solution I had introduced at least in my calendar library Time4A methods like SystemClock.inPlatformView() which uses the (probably) most actual zone data and obtains the correct UTC clock based on the assumption that the user will at least observe correct local device time (whatever he/she had done to achieve this goal, either by updating the OS or by clock/zone configuration). I am quite happy with avoiding the old calendar and zone API altogether this way. My API even allows to simultaneously use both zone repositories:
Timezone.of("java.util.TimeZone~Asia/Jerusalem") // uses Android data
Timezone.of("Asia/Jerusalem") // uses Time4A data
Maybe you can profit from these ideas when to find/develop suitable helper classes for your usage of ThreetenABP. Time4A is open source.

android: timezone offset for "Europe/Russia" [duplicate]

This question already has an answer here:
android timezone difference is 1 hour less then expected
(1 answer)
Closed 9 years ago.
I need an offset for "Europe/Russia" to UTC.. in hours. here is my code:
Calendar mCalendar = new GregorianCalendar();
mCalendar.setTimeZone( TimeZone.getTimeZone("Europe/Moscow"));
TimeZone mTimeZone = mCalendar.getTimeZone();
int remote_offset = mTimeZone.getRawOffset()/1000/60/60;
For UTC it should be -4 hours. BUT! some user got 3 hours difference!!
I think, the problem is, Russia doesn't use winter time. And some devices now that, but some not.. how could I implement allway to get "-4" hours?
Regards
First, Russia isn't UTC-4.
The problem has to do with Russia not having daylight saving time. But your issue is probably only happening with android 2.x device and less. The daylight saving time was removed before 4.x as far as I remember (if it's a user input). On the other hand, if you receive a date that was created by the device without user input, you don't have to convert it as it's already as UTC.
But as I said, Russia isn't -4. Russia/Moscow will be +4 hours. But Russia is larger than Moscow really!
Look here: http://en.wikipedia.org/wiki/Time_in_Russia
UTC+03:00 MSK−1: Kaliningrad Time Europe/Kaliningrad
UTC+04:00 MSK: Moscow Time Europe/Moscow, Europe/Volgograd, Europe/Samara
UTC+06:00 MSK+2: Yekaterinburg Time Asia/Yekaterinburg
UTC+07:00 MSK+3: Omsk Time Asia/Omsk, Asia/Novosibirsk, Asia/Novokuznetsk
UTC+08:00 MSK+4: Krasnoyarsk Time Asia/Krasnoyarsk
UTC+09:00 MSK+5: Irkutsk Time Asia/Irkutsk
UTC+10:00 MSK+6: Yakutsk Time Asia/Yakutsk
UTC+11:00 MSK+7: Vladivostok Time Asia/Vladivostok, Asia/Sakhalin
UTC+12:00 MSK+8: Magadan Time Asia/Magadan, Asia/Kamchatka, Asia/Anadyr
So what you'll have to do is to check if we're in winter and that the TimeZone is one of those. If the timezone is one of those, you can add one more hour when you want to show. And remove 1 hour when you want to convert to UTC.
I don't believe it's possible to update the TimeZone on the android phones and that also means that it's not exactly possible to do that unless you find an alternative library for Dates that has timezones built-in and which are updated.
You could subclass the DateObject with the functions that you use to behave just like the old date object, all you'll have to do is to make sure it behaves differently on android2.x and not on android 4.x+.
Also check this: http://www.joda.org/joda-time/
I checked there and I guess it could be usable and less hacky than my suggestion above. The TimeZones are up to date so it could just work for every phone since it shouldn't use the internal timezones. On the other hand, if you have functions that require the Date, it might get tricky.
My suggestion is make sure you use UTC everywhere and use JodaTime to format the date with timezones and to do "datetime" operations. If you make sure that your Java Date never contain a TimeZone other than UTC. It should work.

Android: programmatically added event time is changed on daylight savings

When I add recurring event in android calendar programmatically it is changed by one hour when daylight savings time begin. It adds one hour at the end of March and subtracts one hour at the end of October.
So if I create an event that occurs every day at 8:00 am, at the end of Mart it will be shifted and will start at 9:00 am. It's like this until the end of October. At the end of October it shifts back and until the end of March it's at 8:00 am again.
To mention that the event has Events.EVENT_TIMEZONE and Events.EVENT_END_TIMEZONE set, and the calendar this event belongs to has Calendars.CALENDAR_TIME_ZONE set. All of these 3 are set to the same timezone.
I found the solution. The problem is that I added the time zone in format GMT+0100. I have to add time zones if format Europe/London.

GregorianCalendar and SQLite

I'm trying to setup a RPG that will keep track of a virtual time. After some reading GregorianCalendar seems to be the best way to do this but I have some questions and hoping someone with experience could give me some insight.
Here is what I'm trying to do. The game will start in the year 1675 and run for about 30 years. At that point the character will retire if they have survived that long. The character will be able to choose from actions I've preset for them through the coarse of the game. Some actions will be short and take a hour, others may take a week or a month. The real question comes from me using SQLite heavily. I want to save the current time as well as how long an action will take in my database. My first thought when setting this up was, if I want to start the game in Aug 15, 1675 to have my data base with 3 rows, set those fields to 8, 15, 1675. Then also have 2 more columns for the time. Pull these int via cursor and set them with something like
GregorianCalendar time = new GregorianCalendar();
time.set(year, month, date, hour, minute);
I figured I would pull how long an action takes in a similar fashion. Have an int X, and then a string to tell whether this time is in minutes, hours, days etc. Then either add this int to the int pulled from database to add to the calendar directly.
Q: If I add it directly to the calendar how would I pull int values from the calendar to store in database to load at a later time (when the player loads their game)?
Q: If I add it to the int stored in the database and set the calendar with this new int what will happen when I add enough to make the int out of scope for the calendar (Date is set to 31 but I add another day)?
You can convert from the GregorianCalendar object to/from UNIX time for example using getTimeInMillis() setTimeInMillis(). GregorianCalendar also has a roll() method:
Adds the specified amount to the specified field and wraps the value
of the field when it goes beyond the maximum or minimum value for the
current date. Other fields will be adjusted as required to maintain a
consistent date.
I would recommend using Joda Time as a substitute for the (somewhat lacking) standard java date and time utilities. It's much more flexible.
It has functions to do date math, it supports several different calendars (ISO8601, Buddhist, Coptic, Ethiopic, Gregorian, GregorianJulian, Islamic, Julian), has support for intervals, durations and periods. It has built in formatters that let you make your output look like just about anything you wish.
If it were me, I would use Joda and store the date in the native format presented by Joda (I don;t remember what that is right offhand) and then pull it out again and use Joda to do all the date math, as well as having it convert it to whatever calendar you wish to use for display to the user.
Otherwise, it seems to me you'd be re-inventing the wheel.

Categories

Resources