2

In my java (actually, Android, but it's irrelevant here) project, the user can enter start and end time for some activity and the system automatically computes the duration. On button press, I use System.currentTimeMillis() to get the current timestamp.

For reasons outside the scope of this question, I need to discard the date part of the timestamp and only store the time one. Easy, I thought, just divide by the number of milliseconds in a day and take the remainder:

long currentTimestamp = System.currentTimeMillis();
long timeOnly = currentTimeStamp % (1000 * 60 * 60 * 24);

This almost works - except it produces timestamp one hour off. Here's an example:

DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US);
long currentTimestamp = System.currentTimeMillis();
long timeOnly = currentTimestamp % (1000 * 60 * 60 * 24);
System.out.println("Full value: " + timeFormat.format(currentTimestamp));
System.out.println("Time only: " + timeFormat.format(timeOnly));

This code prints:

Full value: 10:53 PM
Time only: 11:53 PM


Full value: 11:19 PM
Time only: 12:19 AM

While I can just subtract one hour from the timestamp, I want to actually understand the reason why this is happening.

Aleks G
  • 56,435
  • 29
  • 168
  • 265
  • I'm not sure... but my intuition tells me that it has something to do with timezones. Let me check that - we are in DTS right now... that would explain the hour :). Your currentTimeMillis is a UNIX timestamp (in UTC your calculation would work...). I'll be back :) – Philipp Mar 23 '16 at 23:25
  • Worth noting that I myself am in the UK and we are not in the DST yet. I get exactly the same result if I use `Locale.UK` – Aleks G Mar 23 '16 at 23:26
  • add `timeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));` and you won't have the difference... it is a timezone problem for sure :). The `Locale` is just for the formatting, not the timezone. – Philipp Mar 23 '16 at 23:29
  • @Philipp Hm, strange. The timezone could be different between now and 1 Jan 1970 (although it really shouldn't) – Aleks G Mar 23 '16 at 23:32
  • It is British summer time in January 1st 1970, but today is not. –  Mar 23 '16 at 23:43
  • @saka1029 That appears to be the answer (the other way around), but why should that matter if you set the Locale as `US`? – Paul Boddington Mar 23 '16 at 23:44
  • British summer time in January? That's brilliant! (Not to say that it wasn't - it just sounds really funnny) – Aleks G Mar 23 '16 at 23:44
  • @PaulBoddington The Locale in TimeFormat only controls _how_ it's printed, not _what_ is printed. – Aleks G Mar 23 '16 at 23:45
  • Well that's the answer then. I did an experiment, and these jumps forward and back occur precisely when we British change the clocks. – Paul Boddington Mar 23 '16 at 23:46
  • Ok, for everyone that cares about it; the reason really **is** due to difference in GMT/BST between today (23/03/2016) and 01/01/1970. No matter how funny it sounds, it really was British Summer Time on 01/01/1970. According to [Wikipedia article on the subject](https://en.wikipedia.org/wiki/British_Summer_Time), _A [...] inquiry during 1966–67 led the government [...] to introduce the British Standard Time experiment, with Britain remaining on GMT+1 throughout the year. This took place between 27 October 1968 and 31 October 1971..._ – Aleks G Mar 23 '16 at 23:53
  • @saka1029 You are right - this is exactly the answer - see my previous comment. Post it as an answer and I'll accept. – Aleks G Mar 23 '16 at 23:55
  • @AleksG You're lucky you didn't write this code in 3 days' time because we change the clocks this Sunday. You wouldn't have realised the code was broken for six months! – Paul Boddington Mar 23 '16 at 23:57
  • @PaulBoddington Yeah, I thought exactly the same thing. If I had written it three days later, I wouldn't have spotted the difference until 6 month later the client would have come back to me with the bug - by which time I would completely forget the source and it would take me ages to get to the bottom of it. It only took a couple of hours today. – Aleks G Mar 23 '16 at 23:59

1 Answers1

0

Try:

    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new Date(System.currentTimeMillis()));
    calendar.set(Calendar.MONTH, 0);
    calendar.set(Calendar.YEAR, 0);
    calendar.set(Calendar.DAY_OF_MONTH, 0);

    System.out.println("Full value: " + timeFormat.format(currentTimestamp));
    System.out.println("Time only: " + timeFormat.format(calendar.getTimeInMillis()));

The Calendar instance will use your current Timezone and all setters will take the change the timezone into consideration. Thus, it should work.

Note:

The question remains, why you want to do that. Do you just want to store time information in a database, instead of having the 'full date' stored? If you want to do that, than you could map your current locale time to a UTC time (instead of using currentTimeMillis()).

Philipp
  • 1,289
  • 1
  • 16
  • 37
  • Ok, setting date to 0/0/0 works. The reason for stripping the date part is due to how selected time can be changed. The UI used to change to the time only shows time, but updates it with date/time of the date when it's updated. If it's updated on another date, then instead of, say, 1 hour interval, I'll end up with 1 day plus 1 hour - therefore I need to normalise everything to the same day. The easiest is to just remove the date. – Aleks G Mar 23 '16 at 23:44
  • I think you should work with the `Calendar` implementation and if you are using Java 8 have a look at http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html and http://stackoverflow.com/questions/29750221/is-joda-time-deprecated-with-java-8-date-and-time-api-java-time – Philipp Mar 23 '16 at 23:49
  • Well, java 8 isn't an option, as I am writing specifically for Android 4.2.2 devices (internal client's estate) – Aleks G Mar 23 '16 at 23:55
  • Ohh yeah, I forgot you mentioned that... sorry! – Philipp Mar 24 '16 at 00:05