0

I'm working at a code and I need to use Clock class to get the current time. And I want to subtract the current date with 2 months.

This is the code:

public class DemoApp {
    private Clock clock = Clock.systemDefaultZone();

    public List<String> subtractMonths(String[][] str) {
        List<String> ids = new ArrayList<>();
        // The code
        return ids;
    }
}

And in the main class I have this array of strings:

private static final String[][] str = {
    {"id1", "2017-11-01T14:10:30"},
    {"id2", "2018-01-20T18:01:34"},
    {"id3", "2018-01-22T08:45:22"},
    {"id4", "2018-02-18T12:42:37"},
    {"id5", "2019-03-16T03:56:32"},
};

In the main class I'll call the subtractMonths method with the str parameter and return the id if the date is not the current month and the previous month. For example if the current month is January only return id with month before December. Any feedback will be appreciated!

Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
  • Are only restricted to Clock class or suggesting LocalDateTime or Calender solution is also ok with you ? – Aditya T Apr 02 '19 at 17:43
  • Only restricted to Clock class –  Apr 02 '19 at 17:45
  • 1
    I suggest that you are restricted to getting the current time from the clock, and that you are then allowed to use other classes too. This makes your code testable since you can supply a clock instance with known time and get reproducible results. This would be normal practive. See [Writing and testing convenience methods using Java 8 Date/Time classes](https://stackoverflow.com/questions/52956373/writing-and-testing-convenience-methods-using-java-8-date-time-classes). – Ole V.V. Apr 02 '19 at 18:31
  • Your Question makes no sense to me. What does `Clock` have to do with subtracting months? And what is the list of date-time strings for? Do you want to subtract two months from each of those values? Are you looking to compare those input date-time strings to today? Voting to close as unclear. – Basil Bourque Apr 02 '19 at 20:29

3 Answers3

2

Using LocalDateTime or Instant you have methods for minus() and plus() to subtract or add an amount of time:

LocalDateTime beforeTwoMonths = LocalDateTime.now()
        .minus(2, ChronoUnit.MONTHS);

Or use the minusMonths() method:

LocalDateTime beforeTwoMonths = LocalDateTime.now().minusMonths(2);

Using only the Clock class it is not possible. The only way is to convert it to ZonedDateTime and subtract the months then:

ZonedDateTime beforeTwoMonths = Clock.systemDefaultZone().instant().atZone(ZoneId.systemDefault()).minusMonths(2);

Otherwise you can do the calculation yourself, but I don't think that's the best way to go.

Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
  • 3
    For your knowledge ChronoUnit.MONTHS is not supported on minus() method of Instant class, using your code would result in java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Months exception. – Aditya T Apr 02 '19 at 18:21
  • @AdityaT Yes you are right, didn't remember that. Rolledback my post. Thanks for noticing. – Samuel Philipp Apr 02 '19 at 18:25
  • Also to point out, @gaby is only interested in answer based on Clock class. :) – Aditya T Apr 02 '19 at 18:25
  • Your answer does not meet the criteria of this question, if you can edit it to work with Clock class it would be nice. – Aditya T Apr 02 '19 at 18:32
  • @AdityaT You can not do it only with the `Clock` class, you need to transform it in a `ZonedDateTime` or similar. I updated my question with that information. – Samuel Philipp Apr 02 '19 at 18:54
  • 1
    Thanks for updating your answer. – Aditya T Apr 02 '19 at 19:02
1

I don't think it's possible the easy way. Only method in Clock which performs addition/subtraction on Clock date is

static Clock offset(Clock baseClock, Duration offsetDuration)

Durations don't have months, because a duration is independent of the location and moment-in-time.

However, the length of a month is dependent on exactly that (adding a month in february is shorter than adding one in January. Similarly, adding a month while there is a leap-year or daylight saving change makes a difference).

There is currently no easy way to add one month to a specific date. It's one of the big changes to the core libraries for dart 2.0.

In the meantime the safest is to build a new DateTime with all the arguments passed in, but adding 1 to the month: new DateTime(other.year, other.month + 1, other.day, other.hour, ...).

Even then you still need to pay attention, though: what should it mean to add a month to the 31st of March? With the simple approach above, you would end up at May 1st. If that's not what you want, you have to clamp the months. That requires a bit more code (including knowing when there are leap years).

Reference link : https://github.com/flutter/flutter/issues/12141

Aditya T
  • 1,566
  • 2
  • 13
  • 26
  • Per the above answer, what does it mean to add a month to March 31? A "month" is not a precisely defined unit of time. Adding (or subtracting) one or more months is similarly not precisely defined. – Thomas Bitonti Apr 02 '19 at 19:12
0

I think that something like this was intended:

public List<String> subtractMonths(String[][] str) {
    YearMonth lastMonth = YearMonth.now(clock).minusMonths(1);
    List<String> ids = Arrays.stream(str)
            .filter(pair -> YearMonth.parse(pair[1], DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                    .isBefore(lastMonth))
            .map(pair -> pair[0])
            .collect(Collectors.toList());
    return ids;
}

When I call this method today and pass your str as argument, I get this result:

[id1, id2, id3, id4]

Yes, in addition to the given Clock instance I also use the YearMonth and DateTimeFormatter classes. But the YearMonth gets its time from the clock. This in turn guarantees the behaviour they were after when requiring you to use the clock. They may substitute another clock, and the code will use that clock exclusively for determining which month we’re in.

This is great for testability and an often used trick for that purpose. When you substitute a clock that always tells you it’s, say, January 2019, you know which results to expect. Then you can rerun your automated tests in 2025 and still expect the same result. And report a failure if you don’t get it, and sleep well at night if you do.

Link: Writing and testing convenience methods using Java 8 Date/Time classes

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161