Sort list with string dates - android

I have a small problem. I have a ArrayList listOfSData where every element is some like a date: for example:
[30-03-2012, 28-03-2013, 31-03-2012, 2-04-2012, ...]
Now I was wondering how can I sort this list. I mean I want to sort to this
[28-03-2013, 30-03-2012, 31-03-2012, 2-04-2012, etc].
This list must have String values. How can I sort this list? Help me because I have no idea how can I do that.

You will need to implement a Comparator<String> object that translates your strings to dates before comparing them. A SimpleDateFormat object can be used to perform the conversion.
Something like:
class StringDateComparator implements Comparator<String>
{
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
public int compare(String lhs, String rhs)
{
return dateFormat.parse(lhs).compareTo(dateFormat.parse(rhs));
}
}
Collections.sort(arrayList, new StringDateComparator());

here is a small example based on your input. This could be done with a few lines less, but I thought this would be better to understand. Hope it helps.
List<String> values = new ArrayList<String>();
values.add("30-03-2012");
values.add("28-03-2013");
values.add("31-03-2012");
Collections.sort(values, new Comparator<String>() {
#Override
public int compare(String arg0, String arg1) {
SimpleDateFormat format = new SimpleDateFormat(
"dd-MM-yyyy");
int compareResult = 0;
try {
Date arg0Date = format.parse(arg0);
Date arg1Date = format.parse(arg1);
compareResult = arg0Date.compareTo(arg1Date);
} catch (ParseException e) {
e.printStackTrace();
compareResult = arg0.compareTo(arg1);
}
return compareResult;
}
});

At first already given answers are write, but that decisions they are not very fast.
Standard java Collections.sort use timsort. In average case it takes O(n*log(n)) comparations, so your custom comparator will call O(n*log(n)) times.
If performance is important for you, for example if you have large array you can do following things:
Convert string dates to int or long timestamps. This takes O(n) operation. And then you just sort array of longs or integers. Comparation of two atomic int are MUCH faster than any comparator.
If you want to get MORE speed for this sort, you can use use Radix sort (http://en.wikipedia.org/wiki/Radix_sort). I takes much memory but we can optimize it. As I see you don't need to specify time of day. So the range of values is not very big.
At firts pass (O(n)) you can convert date to integer value, with next assumptions:
1970 01 01 is start date (or more specific time if you know it) and encode like 1
lets max date is 2170 01 01
all month have 31 day. So You get 31*12 = 372 values per year
And than you can just sort array of integers using radix sort. Sorting values with 200 years range takes only 200 * 372 * 4 = 297600 bytes for merge sort array, but you get O(2 * n) complexisty.

Here is a method I wrote for ordering an array of objects by their time parameter. It's done by comparing 2 String times every time, this can be easily adjusted for your date comparison by changing the pattern parameter to: "dd-MM-yyyy"
int repositorySize = tempTasksRepository.size();
int initialRepositorySize = repositorySize;
Task soonTask = tempTasksRepository.get(0);
String pattern = "HH:mm";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
for (int i= 0; i < initialRepositorySize; i++)
{
for (int j= 0; j < repositorySize; j++)
{
Task tempTask = tempTasksRepository.get(j);
try
{
Date taskTime = simpleDateFormat.parse(tempTask.getTime());
Date soonTaskTime = simpleDateFormat.parse(soonTask.getTime());
// Outputs -1 as date1 is before date2
if (taskTime.compareTo(soonTaskTime) == -1)
{
soonTask = tempTask;
}
}
catch (ParseException e)
{
Log.e(TAG, "error while parsing time in time sort: " + e.toString());
}
}
tasksRepository.add(soonTask);
tempTasksRepository.remove(soonTask);
if ( tempTasksRepository.size() > 0 )
{
soonTask = tempTasksRepository.get(0);
}
repositorySize--;

Try to use SimpleDateFormat with "d-MM-yyyy" pattern:
1. Create SimpleDateFormat
2. Parse listOfSData string array to java.util.Date[]
3. Sort date array using Arrays.sort
4. Convert Date[] to string array using the same SimpleDateFormat

Related

How can i get all the days name in the week and all the date in week without saturday and sunday in android?

Explanation:
I want to get the name of the day in week and date of the week without saturday and sunday.Is there any way to satisfy my requirement??
Using below code i got all the date of the week
String[] days_si = new String[7];
for (int i = 0; i < 7; i++) {
days_si[i] = format.format(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
for (int i = 0; i < days_si.length; i++) {
Log.i(TAG, "Days : " + days_si[i].toString());
}
please, help me solve out this problem?
You should look into the various formatting patterns which the SimpleDateFormat provides.
For example:
DateFormat format = new SimpleDateFormat("u EEEE");
will give you the output as
Days : 7 Sunday
Days : 1 Monday...
Try out the various patterns as per your needs.
Regarding the part where you want to skip Saturday and Sunday:- You can add a simple if check in your first for loop to see if the formatted string which the format() method returns contains the strings Saturday or Sunday and if so, do not add them to your string array.
find below how to skip certain day,
and how to get day name (short or full)
String[] days_si = new String[7];
for (int i = 0; i < 7; i++) {
//skip SATURDAY's
if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY){
calendar.add(Calendar.DAY_OF_MONTH, 1);
--i; //THIS PART TO AVOID EMPTY ARRAY ITEMS
continue;
}
//in your format pattern, use EEE to get short day name
//or use EEEE to get full name
days_si[i] = format.format(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
for (int i = 0; i < days_si.length; i++) {
Log.i(TAG, "Days : " + days_si[i].toString());
}
** more on --i; //THIS PART TO AVOID EMPTY ARRAY ITEMS
in an iteration, if a day is Saturday, then the loop will skip without adding value in days_si[i], which will result in an empty array slot, because next loop i will be incremented by 1 and so days_si[i-1] will remain empty or null.
you may need an array of object (custom object) contains 2 properties
String dateOfDay and String nameOfDay, use this array to store day date and day name for each iteration in the for loop
if you want your output to be in a single line, ex: 2016-07-19 Tuesday then skip the custom object part

How to get Time range in array using android

I have an array which having time ranges like below,
String[] str ={"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
i want to get the minimum time and maximum time in above array such as "6.10 AM" and "10.00 PM".i can find out using sorting but it takes long time.Is any other method avail.Guide me,Below i sorted like,
String[] str ={"1:0 PM","2:0 AM","3:0 PM",.....};
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm aa", Locale.getDefault());
Date TimeToCompare = null,Time1 = null;
for(int i=0;i<10;i++)
{
TimeToCompare=sdf.parse(str[i]);
for(int j=i+1;j<10;j++)
{
Time1=sdf.parse(str[j]);
if(TimeToCompare.after(Time1))
{
//sorting
}
}
}
This solution makes one pass through the array, keeping track of the min and max times. Runs in O(n).
double maxTime = 0.0;
double minTime = 0.0;
for(String s : str) {
String[] parts = str.split(" ");
double time = Double.parse(parts[0]);
if (parts[1].equals("PM")) {
time += 12;
}
if (time > maxTime) {
maxTime = time;
}
if (time < minTime) {
minTime = time;
}
}
// convert doubles back into strings and print
Date-Time Values
When working with date-time values, it's usually best to work with them as date-time values.
Parse the strings as date-time values, collect them, sort the collection, and retrieve the first and last elements in collection to get earliest & latest values. Convert back to strings if needed.
Joda-Time & java.time
You can easily parse the strings to create date-time objects.
However avoid using the bundled java.util.Date & .Calendar classes in Java as they are notoriously troublesome. Furthermore, they always combine date and time-of-day while in your case you have only a time-of-day.
Use either Joda-Time or the new java.time package in Java 8. Both offer a day-of-time only class, LocalTime.
Example Code
Example code using Joda-Time 2.3.
Convert your array to Collection as I prefer to not work with arrays.
String[] strings = { "6.30 AM", "6.10 AM", "10.00 PM", "7.00 PM" };
List<String> stringList = Arrays.asList( strings );
Create an empty collection to collect our LocalTime objects as we instantiate them.
List<LocalTime> localTimes = new ArrayList<>();
Create a formatter to parse your particular string format. By the way, if you can change the source of these strings, I suggest creating strings in 24-hour format without the "AM/PM", akin to the standard ISO 8601 format.
DateTimeFormatter formatter = DateTimeFormat.forPattern( "h'.'mm aa" );
Loop through our collection of strings, parsing each one. Store the new LocalTime instance in a collection.
for ( String string : stringList ) {
LocalTime localTime = formatter.parseLocalTime( string );
localTimes.add( localTime );
}
Sort the collection of LocalTime objects, to determine the earliest and latest.
Collections.sort( localTimes ); // Ascending order. Earliest first, latest last.
Retrieve the earliest and latest.
LocalTime earliest = localTimes.get( 0 );
LocalTime latest = localTimes.get( localTimes.size() - 1 );
Dump to console.
System.out.println( "localTimes: " + localTimes );
if ( !( localTimes.isEmpty() ) ) {
System.out.println( "earliest: " + formatter.print( earliest ) );
System.out.println( "latest: " + formatter.print( latest ) );
}
When run…
localTimes: [06:10:00.000, 06:30:00.000, 19:00:00.000, 22:00:00.000]
earliest: 6.10 AM
latest: 10.00 PM
Here's a sample solution picked from ggreiner #
How to sort a list of time strings in Java or Groovy
String[] str ={"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
List<String> times = Arrays.asList(str); // convert int to list
Collections.sort(times, new MyComparator()); // use a custom comparator
Log.i("Min time is ",""+times.get(0));
Log.i("Max Time is ",""+times.get(times.size()-1));
Custom Comparator
class MyComparator implements Comparator<String>
{
private DateFormat primaryFormat = new SimpleDateFormat("h.mm a");
#Override
public int compare(String time1, String time2){
return timeInMillis(time1) - timeInMillis(time2);
}
public int timeInMillis(String time){
return timeInMillis(time, primaryFormat);
}
// in milliseconds
private int timeInMillis(String time, DateFormat format) {
Date date = null ;
try {
date = format.parse(time); //
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (int)date.getTime();
}
}
// Try this way,hope this will help you to solve your problem.
String[] str =new String[]{"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
ArrayList<Double> list = new ArrayList<Double>();
HashMap<Double,String> map = new HashMap<Double, String>();
for (int i=0;i<str.length;i++){
list.add(Double.parseDouble(str[i].split(" ")[0]));
map.put(Double.parseDouble(str[i].split(" ")[0]),str[i]);
}
System.out.println("Min >> " +map.get(Collections.min(list)));
System.out.println("Max >> "+map.get(Collections.max(list)));
Can you use these, But you may need some pre-arragments
Collections.max(arrayList);
Collections.min(arrayList);
Depending on how you're originally filling the array of values, like are you getting the long from the system then you could compare those. Or create an class that holds the value part and the AM or PM seperately like a flag or something, so then sort between AM and PM then values. I dabbled a lot with java date and calendars, and just use JodaTime. It's convenient!

date display in listview in android

I am trying to display a date in descending order in listview in android... I have written a program... It is showing it correctly, but when the first date of the month coming, the last month date are not displaying only one date is showing... What is the reason? How do I improve my code? Please guide me..
my code is here...
public void datesadd()
{
listview.setAdapter(new ListAdapter(this));
cc1=Calendar.getInstance();
int mon1=cc1.getTime().getDate();
Date dd=new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
while(mon1>=count)
{ pos=0;
line=new HashMap<String,String>();
String cdat=String.valueOf(cc1.get(Calendar.DATE));
#SuppressWarnings("deprecation")
String mons=String.valueOf(cc1.get(Calendar.MONTH));
String day1=String.valueOf(cc1.getTime().getDay());
#SuppressWarnings("deprecation")
String year1=String.valueOf(cc1.get(Calendar.YEAR));
try {
dd=format.parse(year1+"-"+mons+"-"+cdat);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
line.put("yeari", year1);
line.put("mont",mons);
line.put("dayi",dd.toString().substring(0, 3));
line.put("datei",cdat);
mon1--;
cc1.add(Calendar.DATE, -1);
Log.v("", "line");
disp.add(line);
// here disp is ArrayList<Hashmap<String,String>> object, i was declared it on top of my main program
}
}
I don't think you're going about date iteration right -- you're just iterating over the value in miliseconds.
Consider this:
/* From your code I'm not sure what count is, but you get the idea, you need
* a Date object here or you can just use a for() loop if you know the number
* |
* V */
while (cc1.getTime().after(count)) {
cc1.add(Calendar.DAY_OF_YEAR /*or month, year, whatever*/, -1);
// and then continue with your own code
line = new HashMap<String,String>();
String cdat = String.valueOf(cc1.get(Calendar.DATE));
// ...
}

\n (Newline) deleting Android

There was some XML parsed text that looked like this:
06:00 Vesti<br>07:15 Something Else<br>09:10 Movie<a href="..."> ... <br>15:45 Something..
and there was a lot of it..
Well, I have done this:
String mim =ses.replaceAll("(?s)\\<.*?\\>", " \n");
there was no other way to show text nicely.
Now, after few showings, and some time, I need that same text separated into alone strings like this:
06:00 Vesti
... or
07:15 Something Else
I've tried something like this, but it does not work:
char[] rast = description.toCharArray();
int brojac = 0;
for(int q=0; q<description.length(); q++){
if(rast[q]=='\\' && rast[q+1]=='n' ) brojac++;
}
String[] niz = new String[brojac];
int bf1=0;
int bf2=0;
int bf3=0;
int oo=0;
for(int q=0; q<description.length(); q++){
if(rast[q]=='\\'&& rast[q+1]=='n'){
bf3=bf1;
bf1=q;
String lol = description.substring(bf3, bf1);
niz[oo]=lol;
oo++;
}
}
I know that in description.substring(bf3,bf1) are not set as they should be but I think that this:
if(rast[q]=='\\' && rast[q+1]=='n)
does not work that way.. is there any other solution?
Note. there is no other way to get that resource. , It must be through this.
Calling Html.fromHtml(String) will properly translate the <br> into \n.
String html = "06:00 Vesti<br>07:15 Something Else<br>09:10 Movie<a href=\"...\"> ... <br>15:45 Something..";
String str = Html.fromHtml(html).toString();
String[] arr = str.split("\n");
Then, just split it on a line basis - no need for regexps (which you shouldn't be using to parse HTML in the first case).
Edit: Turning everything into a bunch of Dates
// Used to find the HH:mm, in case the input is wonky
Pattern p = Pattern.compile("([0-2][0-9]:[0-5][0-9])");
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm");
SortedMap<Date, String> programs = new TreeMap<Date, String>();
for (String row : arr) {
Matcher m = p.matcher(row);
if (m.find()) {
// We found a time in this row
ParsePosition pp = new ParsePosition(m.start(0));
Date when = fmt.parse(row, pp);
String title = row.substring(pp.getIndex()).trim();
programs.put(when, title);
}
}
// Now programs contain the sorted list of programs. Unfortunately, since
// SimpleDateFormat is stupid, they're all placed back in 1970 :-D.
// This would give you an ordered printout of all programs *AFTER* 08:00
Date filter = fmt.parse("08:00");
SortedMap<Date, String> after0800 = programs.tailMap(filter);
// Since this is a SortedMap, after0800.values() will return the program names in order.
// You can also iterate over each entry like so:
for (Map.Entry<Date,String> program : after0800.entrySet()) {
// You can use the SimpleDateFormat to pretty-print the HH:mm again.
System.out.println("When:" + fmt.format(program.getKey()));
System.out.println("Title:" + program.getValue());
}
Use regex:
List<String> results = new ArrayList<String>();
Pattern pattern = Pattern.compile("(\d+:\d+ \w+)<?");
Matcher matcher = pattern.matcher("06:00 Vesti<br>07:15 Something Else<br>09:10 Movie<a href="..."> ... <br>15:45 Something..");
while(matcher.find()) {
results.add(matcher.group(0));
}
results will end up as a list of strings:
results = List[
"06:00 Vesti",
"07:15 Something Else",
"09:10 Movie",
"15:45 Something.."]
See Rexgex Java Tutorial for an idea of how javas regex library works.

Android Java - Joda Date is slow

Using Joda 1.6.2 with Android
The following code hangs for about 15 seconds.
DateTime dt = new DateTime();
Originally posted this post
Android Java - Joda Date is slow in Eclipse/Emulator -
Just tried it again and its still not any better. Does anyone else have this problem or know how to fix it?
I also ran into this problem. Jon Skeet's suspicions were correct, the problem is that the time zones are being loaded really inefficiently, opening a jar file and then reading the manifest to try to get this information.
However, simply calling DateTimeZone.setProvider([custom provider instance ...]) is not sufficient because, for reasons that don't make sense to me, DateTimeZone has a static initializer where it calls getDefaultProvider().
To be completely safe, you can override this default by setting this system property before you ever call anything in the joda.
In your activity, for example, add this:
#Override
public void onCreate(Bundle savedInstanceState) {
System.setProperty("org.joda.time.DateTimeZone.Provider",
"com.your.package.FastDateTimeZoneProvider");
}
Then all you have to do is define FastDateTimeZoneProvider. I wrote the following:
package com.your.package;
public class FastDateTimeZoneProvider implements Provider {
public static final Set<String> AVAILABLE_IDS = new HashSet<String>();
static {
AVAILABLE_IDS.addAll(Arrays.asList(TimeZone.getAvailableIDs()));
}
public DateTimeZone getZone(String id) {
if (id == null) {
return DateTimeZone.UTC;
}
TimeZone tz = TimeZone.getTimeZone(id);
if (tz == null) {
return DateTimeZone.UTC;
}
int rawOffset = tz.getRawOffset();
//sub-optimal. could be improved to only create a new Date every few minutes
if (tz.inDaylightTime(new Date())) {
rawOffset += tz.getDSTSavings();
}
return DateTimeZone.forOffsetMillis(rawOffset);
}
public Set getAvailableIDs() {
return AVAILABLE_IDS;
}
}
I've tested this and it appears to work on Android SDK 2.1+ with joda version 1.6.2. It can of course be optimized further, but while profiling my app (mogwee), this decreased the DateTimeZone initialize time from ~500ms to ~18ms.
If you are using proguard to build your app, you'll have to add this line to proguard.cfg because Joda expects the class name to be exactly as you specify:
-keep class com.your.package.FastDateTimeZoneProvider
I strongly suspect it's because it's having to build the ISO chronology for the default time zone, which probably involves reading all the time zone information in.
You could verify this by calling ISOChronology.getInstance() first - time that, and then time a subsequent call to new DateTime(). I suspect it'll be fast.
Do you know which time zones are going to be relevant in your application? You may find you can make the whole thing much quicker by rebuilding Joda Time with a very much reduced time zone database. Alternatively, call DateTimeZone.setProvider() with your own implementation of Provider which doesn't do as much work.
It's worth checking whether that's actually the problem first, of course :) You may also want to try explicitly passing in the UTC time zone, which won't require reading in the time zone database... although you never know when you'll accidentally trigger a call which does require the default time zone, at which point you'll incur the same cost.
I only need UTC in my application. So, following unchek's advice, I used
System.setProperty("org.joda.time.DateTimeZone.Provider", "org.joda.time.tz.UTCProvider");
org.joda.time.tz.UTCProvider is actually used by JodaTime as the secondary backup, so I thought why not use it for primary use? So far so good. It loads fast.
The top answer provided by plowman is not reliable if you must have precise timezone computations for your dates. Here is an example of problem that can happen:
Suppose your DateTime object is set for 4:00am, one hour after daylight savings have started that day. When Joda checks the FastDateTimeZoneProvider provider before 3:00am (i.e., before daylight savings) it will get a DateTimeZone object with the wrong offset because the tz.inDaylightTime(new Date()) check will return false.
My solution was to adopt the recently published joda-time-android library. It uses the core of Joda but makes sure to load a time zone only as needed from the raw folder. Setting up is easy with gradle. In your project, extend the Application class and add the following on its onCreate():
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
JodaTimeAndroid.init(this);
}
}
The author wrote a blog post about it last year.
I can confirm this issue with version 1, 1.5 and 1.62 of joda. Date4J is working well for me as an alternative.
http://www.date4j.net/
I just performed the test that #"Name is carl" posted, on several devices. I must note that the test is not completely valid and the results are misleading (in that it only reflects a single instance of DateTime).
From his test, When comparing DateTime to Date, DateTime is forced to parse the String ts, where Date does not parse anything.
While the initial creation of the DateTime was accurate, it ONLY takes that much time on the very FIRST creation... every instance after that was 0ms (or very near 0ms)
To verify this, I used the following code and created 1000 new instances of DateTime on an OLD Android 2.3 device
int iterations = 1000;
long totalTime = 0;
// Test Joda Date
for (int i = 0; i < iterations; i++) {
long d1 = System.currentTimeMillis();
DateTime d = new DateTime();
long d2 = System.currentTimeMillis();
long duration = (d2 - d1);
totalTime += duration;
log.i(TAG, "datetime : " + duration);
}
log.i(TAG, "Average datetime : " + ((double) totalTime/ (double) iterations));
My results showed:
datetime : 264
datetime : 0
datetime : 0
datetime : 0
datetime : 0
datetime : 0
datetime : 0
...
datetime : 0
datetime : 0
datetime : 1
datetime : 0
...
datetime : 0
datetime : 0
datetime : 0
So, the result was that the first instance was 264ms and more than 95% of the following were 0ms (I occasionally had a 1ms, but never had a value larger than 1ms).
Hope this gives a clearer picture of the cost of using Joda.
NOTE: I was using joda-time version 2.1
Using dlew/joda-time-android gradle dependency it takes only 22.82 ms (milliseconds). So I recommend you using it instead of overriding anything.
I found solution for me. I load UTC and default time zone. So it's loads very fast. And I think in this case I need catch broadcast TIME ZONE CHANGE and reload default time zone.
public class FastDateTimeZoneProvider implements Provider {
public static final Set<String> AVAILABLE_IDS = new HashSet<String>();
static {
AVAILABLE_IDS.add("UTC");
AVAILABLE_IDS.add(TimeZone.getDefault().getID());
}
public DateTimeZone getZone(String id) {
int rawOffset = 0;
if (id == null) {
return DateTimeZone.getDefault();
}
TimeZone tz = TimeZone.getTimeZone(id);
if (tz == null) {
return DateTimeZone.getDefault();
}
rawOffset = tz.getRawOffset();
//sub-optimal. could be improved to only create a new Date every few minutes
if (tz.inDaylightTime(new Date())) {
rawOffset += tz.getDSTSavings();
}
return DateTimeZone.forOffsetMillis(rawOffset);
}
public Set getAvailableIDs() {
return AVAILABLE_IDS;
}
}
This quick note to complete the answer about date4j from #Steven
I ran a quick and dirty benchmark comparing java.util.Date, jodatime and date4j on the weakest android device I have (HTC Dream/Sapphire 2.3.5).
Details : normal build (no proguard), implementing the FastDateTimeZoneProvider for jodatime.
Here's the code:
String ts = "2010-01-19T23:59:59.123456789";
long d1 = System.currentTimeMillis();
DateTime d = new DateTime(ts);
long d2 = System.currentTimeMillis();
System.err.println("datetime : " + dateUtils.durationtoString(d2 - d1));
d1 = System.currentTimeMillis();
Date dd = new Date();
d2 = System.currentTimeMillis();
System.err.println("date : " + dateUtils.durationtoString(d2 - d1));
d1 = System.currentTimeMillis();
hirondelle.date4j.DateTime ddd = new hirondelle.date4j.DateTime(ts);
d2 = System.currentTimeMillis();
System.err.println("date4j : " + dateUtils.durationtoString(d2 - d1));
Here are the results :
debug | normal
joda : 3s (3577ms) | 0s (284ms)
date : 0s (0) | 0s (0s)
date4j : 0s (55ms) | 0s (2ms)
One last thing, the jar sizes :
jodatime 2.1 : 558 kb
date4j : 35 kb
I think I'll give date4j a try.
You could also checkout Jake Wharton's JSR-310 backport of the java.time.* packages.
This library places the timezone information as a standard Android asset and provides a custom loader for parsing it efficiently. [It] offers the standard APIs in Java 8 as a much smaller package in not only binary size and method count, but also in API size.
Thus, this solution provides a smaller binary-size library with a smaller method count footprint, combined with an efficient loader for Timezone data.
As already mentioned you could use the joda-time-android library.
Do not use FastDateTimeZoneProvider proposed by #ElijahSh and #plowman. Because it is treat DST offset as standart offset for the selected timezone. As it will give "right" results for the today and for the rest of a half of a year before the next DST transition occurs. But it will defenetly give wrong result for the day before DST transition, and for the day after next DST transition.
The right way to utilize system's timezones with JodaTime:
public class AndroidDateTimeZoneProvider implements org.joda.time.tz.Provider {
#Override
public Set<String> getAvailableIDs() {
return new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
}
#Override
public DateTimeZone getZone(String id) {
return id == null
? null
: id.equals("UTC")
? DateTimeZone.UTC
: Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? new AndroidNewDateTimeZone(id)
: new AndroidOldDateTimeZone(id);
}
}
Where AndroidOldDateTimeZone:
public class AndroidOldDateTimeZone extends DateTimeZone {
private final TimeZone mTz;
private final Calendar mCalendar;
private long[] mTransition;
public AndroidOldDateTimeZone(final String id) {
super(id);
mTz = TimeZone.getTimeZone(id);
mCalendar = GregorianCalendar.getInstance(mTz);
mTransition = new long[0];
try {
final Class tzClass = mTz.getClass();
final Field field = tzClass.getDeclaredField("mTransitions");
field.setAccessible(true);
final Object transitions = field.get(mTz);
if (transitions instanceof long[]) {
mTransition = (long[]) transitions;
} else if (transitions instanceof int[]) {
final int[] intArray = (int[]) transitions;
final int size = intArray.length;
mTransition = new long[size];
for (int i = 0; i < size; i++) {
mTransition[i] = intArray[i];
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public TimeZone getTz() {
return mTz;
}
#Override
public long previousTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, false);
if (index <= 0) {
return instant;
}
return mTransition[index - 1] * 1000;
}
#Override
public long nextTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, true);
if (index > mTransition.length - 2) {
return instant;
}
return mTransition[index + 1] * 1000;
}
#Override
public boolean isFixed() {
return mTransition.length > 0 &&
mCalendar.getMinimum(Calendar.DST_OFFSET) == mCalendar.getMaximum(Calendar.DST_OFFSET) &&
mCalendar.getMinimum(Calendar.ZONE_OFFSET) == mCalendar.getMaximum(Calendar.ZONE_OFFSET);
}
#Override
public boolean isStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.DST_OFFSET) == 0;
}
#Override
public int getStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.ZONE_OFFSET);
}
#Override
public int getOffset(final long instant) {
return mTz.getOffset(instant);
}
#Override
public String getShortName(final long instant, final Locale locale) {
return getName(instant, locale, true);
}
#Override
public String getName(final long instant, final Locale locale) {
return getName(instant, locale, false);
}
private String getName(final long instant, final Locale locale, final boolean isShort) {
return mTz.getDisplayName(!isStandardOffset(instant),
isShort ? TimeZone.SHORT : TimeZone.LONG,
locale == null ? Locale.getDefault() : locale);
}
#Override
public String getNameKey(final long instant) {
return null;
}
#Override
public TimeZone toTimeZone() {
return (TimeZone) mTz.clone();
}
#Override
public String toString() {
return mTz.getClass().getSimpleName();
}
#Override
public boolean equals(final Object o) {
return (o instanceof AndroidOldDateTimeZone) && mTz == ((AndroidOldDateTimeZone) o).getTz();
}
#Override
public int hashCode() {
return 31 * super.hashCode() + mTz.hashCode();
}
private long roundDownMillisToSeconds(final long millis) {
return millis < 0 ? (millis - 999) / 1000 : millis / 1000;
}
private int findTransitionIndex(final long millis, final boolean isNext) {
final long seconds = roundDownMillisToSeconds(millis);
int index = isNext ? mTransition.length : -1;
for (int i = 0; i < mTransition.length; i++) {
if (mTransition[i] == seconds) {
index = i;
}
}
return index;
}
}
The AndroidNewDateTimeZone.java same as "Old" one but based on android.icu.util.TimeZone instead.
I have created a fork of Joda Time especially for this. It loads for only ~29 ms in debug mode and ~2ms in release mode. Also it has less weight as it doesn't include timezone database.

Categories

Resources