I need to calculate if a certain time is between 30 minutes earlier and 20 minutes later than the current time.
Any idea how to do this?
The problem is when the time is 23:50h, for example. So I can't do a simple comparison since 23 is greater than 00. But I must consider it smaller since it's another day.
Example:
Now is 23:45. Testing 23:50.
23:45 - 30 minutes = 23:15.
23:45 + 20 minutes = 00:05.
Is 23:50 between 23:15 and 00:05?
Another example:
Now is 00:05. Testing 00:15.
00:05 - 30 minutes = 23:35.
00:05 + 20 minutes = 00:25.
Is 00:15 between 23:35 and 00:25?
--
minSdkVersion is 22, and this further limits the available solutions.
The easiest way to go is :
Compare Hours separately from minutes.
Or also you can take the Hours, multiply them for 60 and then add the returning value to the minutes amount, that will end up with a "only minute" calculation between the 2 times. You can make whatever operation you need.
The only case you should calculate is that one you are in a different day, but that dipends and what you are trying to accomplish!
CODE IN JAVA (OLD VERSION):
public class Main
{
public static void main(String[] args)
{
//Input TIME
String date = "23.45";
//Calculating the TIME in MINUTES ONLY
int date_m = normalizeTime(date);
//CALCULATE MAX AND MIN TIMES
int max = date_m+20;
int min = date_m-30;
/*
Working like that we don't
need to worry about the day Before or After
*/
//JUST DEBUG PRINTS TO SHOW YOU THAT
System.out.println("MAX TIME : "+max);
System.out.println("MIN TIME : "+min);
//The TIME that has to be tested
String testDate = "23.50";
//Calculating the TIME in MINUTES ONLY
int testDate_m = normalizeTime(testDate);
//JUST A DEBUG PRINT TO SHOW YOU THE TESTED TIME
System.out.println("TESTED TIME : "+testDate_m);
/*
If the testDate_m is Between the MAX and MIN values it's
TRUE else it's FALSE
If needed you can adjust with >= or <=
That doesn't matter for the logic.
*/
if(testDate_m<max && testDate_m>min)
System.out.println("IT IS BETWEEN!");
else
System.out.println("IT ISN'T BETWEEN!");
//DONE!
}
/*
Just a Method to clean up the code:
Basically it will Split the string in HOURS and MINUTES
and it will make a simple operation of : Hour*60(Transforming it to minutes) + minutes
The return is an INT that represent the inserted TIME as a MINUTE ONLY TIME.
If the returned number is more than 24*60 it's the Day Next (don't need to worry about that)
If the returned number is less than 0 it's the Previous Day (don't need to worry about that)
*/
private static int normalizeTime(String time)
{
int h = Integer.parseInt(time.substring(0,2));
int m = Integer.parseInt(time.substring(3,5));
return h*60+m;
}
}
CODE IN JAVA (NEW VERSION) :
public class Main
{
public static void main(String[] args)
{
boolean inTime = true;
//Input TIME
String date = "23.50";
//Calculating the TIME in MINUTES ONLY
int date_m = normalizeTime(date);
//CALCULATE MAX AND MIN TIMES
int max = date_m+20;
int min = date_m-30;
int prevMin = max;
int nextMax = min;
if(min<0)
{
prevMin = 24*60+min;
nextMax = 24*60+max;
}
else if(max>24*60)
{
prevMin = min-24*60;
nextMax = max-24*60;
}
/*
Working like that we don't
need to worry about the day Before or After
*/
//JUST DEBUG PRINTS TO SHOW YOU THAT
System.out.println("Between :"+min+" and "+max);
System.out.println("OR");
System.out.println("Between : "+prevMin+" and "+nextMax);
//The TIME that has to be tested
String testDate = "00.05";
//Calculating the TIME in MINUTES ONLY
int testDate_m = normalizeTime(testDate);
//JUST A DEBUG PRINT TO SHOW YOU THE TESTED TIME
System.out.println("TESTED TIME : "+testDate_m);
/*
If the testDate_m is Between the MAX and MIN values it's
TRUE else it's FALSE
If needed you can adjust with >= or <=
That doesn't matter for the logic.
*/
if((testDate_m<max && testDate_m>min) || (testDate_m<nextMax && testDate_m>prevMin))
System.out.println("IT IS BETWEEN!");
else
System.out.println("IT ISN'T BETWEEN!");
//DONE!
}
/*
Just a Method to clean up the code:
Basically it will Split the string in HOURS and MINUTES
and it will make a simple operation of : Hour*60(Transforming it to minutes) + minutes
The return is an INT that represent the inserted TIME as a MINUTE ONLY TIME.
If the returned number is more than 24*60 it's the Day Next (don't need to worry about that)
If the returned number is less than 0 it's the Previous Day (don't need to worry about that)
*/
private static int normalizeTime(String time)
{
int h = Integer.parseInt(time.substring(0,2));
int m = Integer.parseInt(time.substring(3,5));
return h*60+m;
}
}
The easiest way is just to work with timestamps.
long time = new Date().getTime();
long thiry_earlier = time - minutes_to_ms(30);
long twenty_later = time + minutes_to_ms(20);
if(compare < twenty_later && compare > thirty_earlier) {
//do whatever
}
long minutes_to_ms(long minutes) {
return minutes*60*1000;
}
There's some nicer conversion functions you can use nowdays I'm just too lazy to look them up. But working with raw timestamps makes everything easier for comparisons.
Related
I am working on an Android app that gets a time (duration) value as string.
For example, the app can get a value like: 6 hours 43 mins
or a value like: 15 mins
I am looking for a way to convert this strings to an integer value in minutes.
I have tried using this function, but I can´t extract the needed values:
str = str.replaceAll("[^\\d.]", "");
Edit:
it could be possible a result like 6 hours, the only known condition is that minutes are always rounded to an integer, the minimum value is 1 min
Using a Regex to get each couple numeric/time_unit. You can easily parse that with :
(\\d+) (\\w+)
Group 1 : numeric value
Group 2 : time unit
(note that I used a space between the two (could be optional if you want)
Using Java Pattern class to use that regex
Pattern p = Pattern.compile("(\\d+) (\\w+)");
Then you just have to iterate on each match to get the couple type/value
Matcher m = p.matcher(s);
while(m.find()){
String type = m.group(2);
int value = Integer.parseInt(m.group(1))
...
}
From that, just use a switch to convert the number into minute and add it to the variable, omitting the break to have a nice (but not efficient) converter :
switch(type){
//Add more time unit in the correct order if needed
case "days":
value *= 24;
case "hours":
value *= 60;
case "mins":
mins += value;
break;
}
Each type will convert the value into a correct number of minute, at the end, you will have the result in minutes.
Problem: There are few concerns as you never know without putting extra conditions like:
15 hours and 15 mins both will be stored in same integer value , you eventually need to differentiate them on some conditions to cater all the issues.
Coming to the question, you may achieve all this by using String split cases but you need to manually cater all the cases keeping in mind a user can use any spell words like hours can be hrs and so on
You could split the String at the whitespace and use the values in the array.
String value1 = "6 hours 43 mins";
String value2 = "15 mins";
String[] resultList1 = value1.split(" ");
String[] resultList2 = value2.split(" ");
int minutes1 = 0;
int minutes2 = 0;
if(resultList1.length == 4) {
minutes1 = Integer.parseInt(resultList1[0]) * 60 + Integer.parseInt(resultList1[2]);
} else {
minutes1 = Integer.parseInt(resultList1[0]);
}
if(resultList2.length == 4) {
minutes2 = Integer.parseInt(resultList2[0]) * 60 + Integer.parseInt(resultList2[2]);
} else {
minutes2 = Integer.parseInt(resultList2[0]);
}
System.out.println(minutes1);
System.out.println(minutes2);
The result is:
403
15
Either String split() or Pattern Matcher, as earlier answers suggest, will work. I'm not sure which will be more efficient though, but it's probably irrelevant in this case. My version:
String timeStr = "2 hours 15 mins";
String[] parts = timeStr.split(" ");
int totalMins = 0;
for(int i=1; i< parts.length; i+=2) {
// Add checking for "days", etc., if necessary.
if(parts[i].equals("hours")) {
int h = Integer.parseInt(parts[i-1]);
totalMins += 60 * h;
} else if(parts[i].equals("mins")) {
int m = Integer.parseInt(parts[i-1]);
totalMins += m;
}
}
System.out.println("totalMins = " + totalMins);
>> totalMins = 135
If you can get each minute and hours separately you can use string.replace("mins", "") then use Integer.parseInt().
If you get overral like 6 hours 43 mins you must split the string.
I am not sure whether this can be done in a single regex, but if I were you I would use a different regex to find the number of hours, the number of minutes, the number of seconds, etc.
Given a string in the format you mentioned, you can first extract the number of hours by using this regex:
\d+(?= hours?)
Then extract the number of minutes:
\d+(?= mins?)
If seconds can appear in the input string, you can use this to extract seconds as well:
\d+(?= secs?)
If any of the regexes don't match, that means there isn't that information in the string.
String time = "6 hours 43 mins";//or (43 mins) or (6 hours)
int h, m;
String[] parts = time.split(" ");
if (parts.length == 4) {
h = parts[1];
m = parts[3];
} else if (parts.length == 2) {
if (parts[1].isEqualTo("hours") {
h = parts[0];
} else if (parts[1].isEqualTo("mins") {
m = parts[0];
}
}
return h*60+m;
Consider this piece of code:
// calculate age of dog
long interval = 1000*60*60*24*30;
int ageInWeeks = Weeks.weeksBetween(geburtsDatumDateTime, nowDateTime).getWeeks();
if (ageInWeeks < 20){
// wöchentlich
interval = 1000*60*60*24*7;
} else if (ageInWeeks >= 20 && ageInWeeks < 52){
// 2-wöchentlich
interval = 1000*60*60*24*14;
} else if (ageInWeeks >= 52){
// monatlich
interval = 1000*60*60*24*30;
}
The debugger shows, that in case of ageInWeeks >= 52 the interval is: -1702967296, but it should be: 2592000000
The minus sign suggests some kind of overflow error.
However the maximum value of a long in Java is 2E63-1 which is: 9.2233E18
What I am missing here? Is an Android maximum value for a long smaller?
You're computing 32-bit signed ints, the computation overflows, and then you assign the result to a long.
Do your calculation in 64 bits by making one of the operands a long. For example, add L to one of the operands to make it a long literal:
interval = 1000L*60*60*24*30;
As laalto said, adding a 'L' should make it work.
However, for avoiding these kind of errors in the future, you could use the TimeUnit class (available in Android SDK):
long interval = TimeUnit.DAYS.toMillis(30);
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 runing this code on android after load a cursor with the query i pass to the adapter, but my date is in long in milliseconds format so i need to format properly before load the adapter!
problem is this code is taking 14 seconds to pass a 50 items load, the problem get worst if i call it inside the adapter getView cause get slow when i scrool, if i take this function out the program runs smoothly
this is call inside my listfragment
private String dateFormatPatternEdited(long timeMS) {
android.text.format.DateFormat df = new android.text.format.DateFormat();
final Calendar eDate = Calendar.getInstance();
Calendar sDate = Calendar.getInstance();
sDate.setTimeInMillis(timeMS);
long daysBetween = 0;
while (sDate.before(eDate)) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
String mDateFormatPattern = FuelTrackerApplication.dateFormat.format(timeMS);
if (daysBetween < 2){
mDateFormatPattern = FuelTrackerApplication.timeFormat.format(timeMS);
} else if(daysBetween < 365){
mDateFormatPattern = df.format(FuelTrackerApplication.dateFormatPattern,timeMS).toString();
}
return mDateFormatPattern;
}
and this is were i initialize the date formats i gonna use its called inside onCreate in FuelTrackerApplication i dont think theres nothing wrong with this
public void initializeDateFormat() {
android.text.format.DateFormat df = new android.text.format.DateFormat();
dateFormatPattern = "MMM dd";
if (android.os.Build.VERSION.SDK_INT >= 18){
dateFormatPattern = df.getBestDateTimePattern(Locale.getDefault(), dateFormatPattern);
}
dateFormat = df.getMediumDateFormat(getApplicationContext());
timeFormat = df.getTimeFormat(getApplicationContext());
dateFormat2 = df.getLongDateFormat(getApplicationContext());
}
Ok just a few things. Depending on how long ago your dates are going back. You are only interested if the days between go more then 365. So if your dates are going back for years, you're doing extra work.
while (sDate.before(eDate) && daysBetween <= 365) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
Will let it break, it means if you have 20 entries going back 5 years, you don't do so much work.
It might be worth while to possibly just check the milliseconds difference. I'm not sure if this is precise enough, but it should work. It means you don't need to loop everything E.g
long millisecondsToday = getMilliseconds;
long timeMs = // you already have this
long millisecondsDifference = millisecondsToday - timeMs;
if (millisecondsDifference < MILLISECONDS_TWO_DAYS) // set a final variable out of this method
// etc
If might also be worth while initialising some of your variables once outside of the method. Like your df, that is being created 50 times, and then just having something set on it. Same with your eDate.
i got this incredible faster and practicaly remove the hole function
instead goes like this
lDateBetween = NOW - timeMS;
if (lDateBetween < DAY)
return FuelTrackerApplication.timeFormat.format(timeMS);
else if (lDateBetween < YEAR)
return df.format(FuelTrackerApplication.dateFormatPattern,timeMS).toString();
else return FuelTrackerApplication.dateFormat.format(timeMS);
all calculation is made using milliseconds i also put 2 final NOW and YEAR, also df and lDateBetween
i think is the fastest i can get!
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."