I'm implementing count down timer for the android app using JodaTime.
Depending of devices the output is different.
DateTime openingDateTime = new DateTime(2018, DateTimeConstants.JUNE, 14, 21, 0, 0, DateTimeZone.forID("Europe/Moscow"));
DateTime nowDateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"));
long difference = openingDateTime.getMillis() - nowDateTime.getMillis();
(...)
onTick(difference);
(...)
PeriodFormatter periodFormatter = new PeriodFormatterBuilder()
.printZeroAlways()
.appendDays().appendSuffix(" day", " days")
.appendSeparator(" ")
.appendHours()
.appendSeparator(":")
.appendMinutes()
.appendSeparator(":")
.appendSeconds()
.toFormatter();
(...)
#Override
public void onTick(long millisUntilFinished) {
Duration duration = new Duration(millisUntilFinished);
Period period = duration.toPeriod(PeriodType.dayTime());
tvCounter.setText(periodFormatter.print(period));
}
On the one device output is correct: 491 days 4:39:18
on the other is wrong: 0 days 11788:49:11.
What am I doing wrong?
Thanks to your comments, I can now reproduce your problem. Just add following static initializer to your test class (at first place) to simulate the device where you observe your expected output:
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}
According to the spec (see also the accepted answer on this SO-post), the conversion duration.toPeriod(periodType) should only use so-called precise duration fields, that is hours, minutes, seconds and milliseconds but not days.
My analysis of source code of Joda-Time (v2.9.6):
The internal class org.joda.time.chrono.BasicChronology contains following constant:
private static final DurationField cDaysField = new PreciseDurationField(DurationFieldType.days(), 86400000L);
So we see that here this duration field is marked as "precise", but: The subclass ZonedChronology wraps it and override the behaviour of method isPrecise():
public boolean isPrecise() {
return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed();
}
This shows an extra zone dependency of the precision property of the days()-duration-field, namely precise for fixed zones like UTC and else imprecise.
I don't know if the analyzed and observed behaviour is a feature or a bug. Let's say, it is dangerous to expect the creation of Period-objects by duration.toPeriod(...) to be zone-independent. And it is not documented there to have a precise days-component if the system zone is fixed.
Unfortunately, the implicit dependency on the default time zone is deeply coded into Joda-Time via its chronology-design. As workaround, you can use:
Period p = new Period(nowDateTime, openingDateTime, PeriodType.dayTime());
Related
I have an app on the PlayStore and I am building a feature where the user will not see ads more than a specific number in one day.
I am thinking about comparing the current date and time to the previously saved one but haven't find a proper way to do that.
How can I compare date and time to know if 24 hours have passed or not?
Some posts that I found but not helpful:
medium.com
stackoverflow
stackoverflow
tl;dr
[This Answer uses Java syntax. You’ll have to translate to Kotlin syntax.]
if
(
Duration // Represents elapsed time on the scale of hours-minutes-seconds.
.between( // Calculates elapsed time between two points in time.
Instant.parse( "2021-03-23T15:30:57.013678Z" ) , // Last moment when an ad was show.
Instant.now() // Current moment.
) // Returns a `Duration` object.
.toHours() // Extract total number of whole hours from the `Duration` object.
>= 24L // Test if equals-to or greater-than 24 hours.
)
{ show ad }
java.time
You asked:
… know if 24 hours have passed or not?
Use the modern java.time classes defined in JSR 310. The java.time classes are built into Android 26 and later. Most of the functionality is available in earlier Android using the latest tooling’s “API desugaring“.
Instant adShown = Instant.parse( "2021-03-23T15:30:57.013678Z" ) ;
Instant now = Instant.now() ;
Duration d = Duration.between( adShown , now ) ;
long hoursSinceAdShown = d.toHours() ;
if( hoursSinceAdShown >= 24L ) { … show ad }
Record your next ad-showing as text in standard ISO 8601 format.
String output = Instant.now().toString() ;
2021-03-23T15:30:57.013678Z
Your Question asked for two different things:
Once per day
Every 24 hours
The first involves a calendar, dates, and a time zone. The second does not. I showed you code for the second.
You can use a scheduled executor service to trigger from a background thread the next showing of an ad at a specific moment. Search Stack Overflow to learn more as this has been covered many times already.
Use this code to check the current date, Yesterday or Particulardate. Pass Epoch time to this method
// input format (we get a value as Epoch)
private val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private val outputFormat = SimpleDateFormat("MMM dd")
// have to pass the time value as Epoch time.
private fun calculateDateMonth(time: String): String {
var returnValue = ""
val dateTime = DateTime((time.toLong()) * 1000L)
val inputTime = inputFormat.parse(dateTime.toString())
val convertDateMonth = outputFormat.format(inputTime!!)
val timeInMilliseconds = outputFormat.parse(convertDateMonth)!!
val mTime: Calendar = Calendar.getInstance()
mTime.setTimeInMillis(timeInMilliseconds.time)
val now = Calendar.getInstance()
returnValue = when {
now[Calendar.DATE] == mTime[Calendar.DATE] // check isToday
now[Calendar.DATE] - mTime[Calendar.DATE] == 1 // check Yesterday
else -> convertDateMonth // Month and Date
}
return returnValue
}
I have a requirement to display a custom Activity which contains a listview showing all the timeZones as per below format:
ZoneID (UTC <+/-> hh:mm)
These timezones should be sorted (similar to TimeZone settings shown in Windows) such that:
All UTC negative timezones appear first
All UTC+00:00 timezones appear next
All timezones greater than UTC+00:00 appear last
I came across a lot of examples but they all are using JDK 1.8 (Android API level 26) with ZoneId class. Few such examples are explained here and here.
I want some alternative to these examples, which I can use in Android API level 23 or JDK 1.7.
Expected Output:
America/Thule (UTC-03:00)
America/Argentina/La_Rioja (UTC-03:00)
America/Belem (UTC-03:00)
America/Jujuy (UTC-03:00)
America/Bahia (UTC-03:00)
America/Goose_Bay (UTC-03:00)
America/Argentina/San_Juan (UTC-03:00)
America/Argentina/ComodRivadavia (UTC-03:00)
America/Argentina/Tucuman (UTC-03:00)
America/Rosario (UTC-03:00)
SystemV/AST4ADT (UTC-03:00)
America/Argentina/Buenos_Aires (UTC-03:00)
America/St_Johns (UTC-02:30)
Canada/Newfoundland (UTC-02:30)
America/Miquelon (UTC-02:00)
Etc/GMT+2 (UTC-02:00)
America/Godthab (UTC-02:00)
America/Noronha (UTC-02:00)
Brazil/DeNoronha (UTC-02:00)
Atlantic/South_Georgia (UTC-02:00)
Etc/GMT+1 (UTC-01:00)
Atlantic/Cape_Verde (UTC-01:00)
Africa/Dakar (UTC+00:00)
Africa/Bissau (UTC+00:00)
WET (UTC+00:00)
Etc/Greenwich (UTC+00:00)
Africa/Timbuktu (UTC+00:00)
Africa/Monrovia (UTC+00:00)
Europe/Bratislava (UTC+01:00)
Arctic/Longyearbyen (UTC+01:00)
Europe/Vatican (UTC+01:00)
Europe/Monaco (UTC+01:00)
Africa/Harare (UTC+02:00)
Europe/Tallinn (UTC+02:00)
Use a list of java.util.TimeZone and sort them by the offset:
String[] ids = TimeZone.getAvailableIDs();
List<TimeZone> zones = new ArrayList<>();
for (String id : ids) {
zones.add(TimeZone.getTimeZone(id));
}
Collections.sort(zones, new Comparator<TimeZone>() {
// compare by the offset, reverse order
#Override
public int compare(TimeZone o1, TimeZone o2) {
return o1.getRawOffset() - o2.getRawOffset();
}});
Then you print this list, using the ID and a helper method to format the offset:
public String formatOffset(int rawOffset) {
StringBuilder sb = new StringBuilder("UTC").append(rawOffset < 0 ? "-" : "+");
int secs = Math.abs(rawOffset) / 1000;
int hours = secs / 3600;
secs -= hours * 3600;
int mins = secs / 60;
sb.append(hours < 10 ? "0" : "").append(hours).append(":");
sb.append(mins < 10 ? "0" : "").append(mins);
return sb.toString();
}
for (TimeZone tz : zones) {
System.out.println(tz.getID() + " (" + formatOffset(tz.getRawOffset()) + ")");
}
The output will be something like:
Etc/GMT+12 (UTC-12:00)
Etc/GMT+11 (UTC-11:00)
Pacific/Midway (UTC-11:00)
Pacific/Niue (UTC-11:00)
... lots of timezones
Note that I didn't include the "pretty tabs" formatting, it's up to you.
I also used getRawOffset, that returns the current offset for that zone - check the javadoc: https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getRawOffset()
This method doesn't consider Daylight Saving Time changes or any other historical data (like countries that used a different offset in the past - and believe me, most countries - if not all of them - had at least one offset change during their history). To use those other offsets, you can use the getOffset method, passing a reference date/timestamp as argument - check the javadoc for details.
I also used getAvailableIDs, that returns all the timezones, but you can filter this list to have only the zones you want.
But honestly, this old API (java.util's Date, Calendar, SimpleDateFormat, TimeZone and so on) is really bad, with lots of flaws and annoying bugs, and nobody deserves to use them anymore. Specially today, that we have better API's.
In API levels that don't have the java.time classes (such as ZoneId), you can use the ThreeTen Backport, as already told in the other answer. Then you can follow the examples you found.
One main difference is that this API always requires an Instant as a reference to get the offset - because it checks all the timezone's historical data to know what's the offset used in that specific moment (in the code below, I'm using the current instant):
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
List<ZoneId> timezones = new ArrayList<>();
for (String id : availableZoneIds) {
timezones.add(ZoneId.of(id));
}
// current instant
final Instant now = Instant.now();
Collections.sort(timezones, new Comparator<ZoneId>() {
// compare by offset in reverse order, use the Instant as a reference
#Override
public int compare(ZoneId o1, ZoneId o2) {
return o2.getRules().getOffset(now).compareTo(o1.getRules().getOffset(now));
}
});
for (ZoneId zone : timezones) {
ZoneOffset offset = zone.getRules().getOffset(now);
System.out.println(zone + " (UTC" + (offset.getTotalSeconds() == 0 ? "+00:00" : offset.toString()) + ")");
}
Also note that the ZoneOffset class has a nice toString() method that already prints the offset in the correct format (except when it's zero, which is printed as Z, but that wasn't hard to fix).
As a complement to the other answer, you can print the zones and offsets using a formatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("VV '(UTC'xxx')'");
for (ZoneId zone : timezones) {
System.out.println(fmt.format(now.atZone(zone)));
}
It produces the same output: "VV" prints the zone ID and "xxx" prints the offset.
Just get ThreeTenABP and add it to your Android project in order to use ZoneId and the other goodies from java.time, the modern Java date and time API that came out with Java 8 and was also backported to Java 6 and 7.
Links
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.timeto Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Solution using Joda-Time:
First get all the ID's using DateTimeZone class and then sort them using DateTimeZone.getOffset(Instant) method. DateTimeZone considers daylight savings unlike TimeZone.getrawOffset() provided by java.util.
Set<String> availableIDs = DateTimeZone.getAvailableIDs();
List<DateTimeZone> dateTimezones = new ArrayList<>();
for (String id : availableIDs) {
dateTimezones.add(DateTimeZone.forID(id));
}
final Instant now = Instant.now();
Collections.sort(dateTimezones, new Comparator<DateTimeZone>() {
#Override
public int compare(DateTimeZone o1, DateTimeZone o2) {
return o1.getOffset(now)-o2.getOffset(now);
}
});
for (DateTimeZone dateTimeZone : dateTimezones) {
int offset = dateTimeZone.getOffset(now);
String out = String.format("%35s %s%n", dateTimeZone, " (" + formatOffset(offset) + ")");
System.out.printf(out);
}
formatOffset(int) is reused from the above example.
Links :
Link to Download Joda-Time
I am writing a cross-platform application in Cocos2d-x. I need to get the time to create a countdown clock to a certain time of day. Since it is in C++, I can use time(...), mktime(...), and difftime(...) if I need to as a direct approach.
Is there a preferred method in Cocos2d-x for doing this in a cross-platform way (i.e. something built directly into the framework)? I want the app to work the same on iPhones, iPads, and Android.
try this:
time_t rawtime;
struct tm * timeinfo;
time (&rawtime);
timeinfo = localtime (&rawtime);
CCLog("year------->%04d",timeinfo->tm_year+1900);
CCLog("month------->%02d",timeinfo->tm_mon+1);
CCLog("day------->%02d",timeinfo->tm_mday);
CCLog("hour------->%02d",timeinfo->tm_hour);
CCLog("minutes------->%02d",timeinfo->tm_min);
CCLog("seconds------->%02d",timeinfo->tm_sec);
Try this code
static inline long millisecondNow()
{
struct cc_timeval now;
CCTime::gettimeofdayCocos2d(&now, NULL);
return (now.tv_sec * 1000 + now.tv_usec / 1000);
}
I used this function to get current time in millisecond. I am new in cocos2d-x so hope this can be helpful.
You should try this lib, I just tested and it works fine.
https://github.com/Ghost233/CCDate
If you receive some wrong values, set timezoneOffset = 0;
Note: 0 <= month <= 11
You can sheduleUpdate in clock class.
The update call with a float argument which is a delta time in seconds after last calls, this method is called every frame and cocos2d-x get time through from the system and count the delta.
I thought this code would do the trick:
static inline long millisecondNow()
{
struct cc_timeval now;
CCTime::gettimeofdayCocos2d(&now, NULL);
return (now.tv_sec * 1000 + now.tv_usec / 1000);
}
HOWEVER, only gives a part of what I need. In general, I need a real "date and time" object (or structure), not just the time of day in milliseconds.
The best solution, for now, seems to be using the "classic" localtime, mktime, difftime trifecta in C++. I have a few examples below of some basic operations...I may cook up a general class to do these kinds of operations, but for now, these are a good start and show how to get moving:
double Utilities::SecondsTill(int hour, int minute)
{
time_t now;
struct tm target;
double seconds;
time(&now);
target = *localtime(&now);
target.tm_hour = hour;
target.tm_min = minute;
target.tm_sec = 0;
seconds = difftime(mktime(&target),now);
return seconds;
}
DAYS_OF_WEEK_T Utilities::GetDayOfWeek()
{
struct tm tinfo;
time_t rawtime;
time (&rawtime);
tinfo = *localtime(&rawtime);
return (DAYS_OF_WEEK_T)tinfo.tm_wday;
}
I current have (from server) a date stamp returned as ticks (.NET Date).
In general I managed to convert the above by subtracting by 10000 to produce secs and offset accordingly to get EPOC ms.
Now, the issue is that the ms passed from server include the zone offset and what I needed to do is get a TimeZone object for the zone (always the same) and subtract the ms offset (depending on DST) from the original value to produce a new object to properly get a Date.
Any better way of doing this without so many conversion?
private static long netEpocTicksConv = 621355968000000000L;
public static Date dateTimeLongToDate(long ticks) {
TimeZone greeceTz = TimeZone.getTimeZone("Europe/Athens");
Calendar cal0 = new GregorianCalendar(greeceTz);
long time = (ticks - netEpocTicksConv)/ 10000;
time -= greeceTz.getOffset(time);
cal0.setTimeInMillis(time);
Date res = cal0.getTime();
return res;
}
Here's some code which doesn't quite do the right thing near DST transitions:
private static final long DOTNET_TICKS_AT_UNIX_EPOCH = 621355968000000000L;
private static final TimeZone GREECE = TimeZone.getTimeZone("Europe/Athens");
public static Date dateTimeLongToDate(long ticks) {
long localMillis = (ticks - DOTNET_TICKS_AT_UNIX_EPOCH) / 10000L;
// Note: this does the wrong thing near DST transitions
long offset = GREECE.getOffset(localMillis - GREECE.getRawOffset());
long utcMillis = localMillis - offset;
return new Date(utcMillis);
}
There's no need to use a Calendar here.
You can get it to be accurate around DST transitions unless it's actually ambiguous, in which case you could make it either always return the earlier version or always return the later version. It's fiddly to do that, but it can be done.
By subtracting the offset for standard time, we're already reducing the amount of time during which it will be incorrect. Basically this code now says, "Subtract the standard time (no daylight savings) offset from the local time, to get an approximation to the UTC time. Now work out the offset at that UTC time."
I have some things in my Android application that need to update once per day.
It's pretty simple I think, but I have no idea in what format I need to format the date and time (if time is needed) and how to check if an update has been done today (where today is between 00:01am and 23:59pm in the user's local time). The update should not be done if it was already done for today.
Here's what I DO know how to do:
Save the last update date in SharedPreferences (but how do I get a
string of it, I do not know)
Get things from SharedPreferences (but I
don't know how to compare dates in string format)
It is irrelevant what format you choose. It is just matter of recalculations.
I'd suggest using milliseconds since epoch, as all system calls use it, so it would be easier for you to use the same.
As 1000 millis is 1 second it's easy to figure out that 1000*60*60*24 equals to one day (24hrs). So, if storedMillis is bigger than NOW - 1000*60*60*24, (and NOW is i.e. System.currentTimeMillis()), then it is too early to do the check. If storedMillis is smaller, then save your NOW timestamp and do the check:
long now = System.currentTimeMillis();
long diffMillis = now - lastCheckedMillis;
if( diffMillis >= (3600000 * 24) ) {
// store now (i.e. in shared prefs)
// do the check
} else {
// too early
}
EDIT
I am interested in doing it when the app is first opened for the
current day, even if the last update was done 10 minutes ago.
It's just the matter how to get the proper millis to compare against. Replace long now = System.currentTimeMillis(); from above code with following code block:
Calendar cal = Calendar.getInstance();
cal.clear(Calendar.HOUR);
cal.clear(Calendar.HOUR_OF_DAY);
cal.clear(Calendar.MINUTE);
cal.clear(Calendar.SECOND);
cal.clear(Calendar.MILLISECOND);
long now = cal.getTimeInMillis();
which shall do the trick.
If you store your date in format 20121122 (YYYYmmdd) then you can compare is like 20121122 > 20121123. But it must be stored as int or cast to int when comparing.
Store the timestamp (System.currentTimeMillis() ) of the Last execution and compair it with the currient one. If the difference is more than 24 hours... You know it or?
Set up an Alarm with AlarmManager that executes every 24 hours, then do stuff
Check this question: Alarm Manager Example
It's a more complicated approach than the rest, but makes sure things are done, while with the other options the app must be executed in order to check if it has to update whatever.
Here is the method
public boolean runOnceADay() {
SharedPreferences shp= c.getSharedPreferences(Constants.GENERAL_SHP, MODE_PRIVATE);
SharedPreferences.Editor editor= shp.edit();
long lastCheckedMillis = shp.getLong(Constants.ONCE_A_DAY, 0); // "KEY" you may change yhe value
long now = System.currentTimeMillis();
long diffMillis = now - lastCheckedMillis;
if (diffMillis >= (3600000 * 24)) { // set up your time circulation
editor.putLong(Constants.ONCE_A_DAY, now);
editor.commit();
Util.showMessage(c, "Once a Day Test");
return false;
} else {
Util.showMessage(c, "Too Early");
return true;
}
}