Friday, 17 March 2017

Understanding HTTP Strict Transport Security (HSTS)

Background

Have you any time tried to visit a site by typing "http://" in your web browser and the site that loads corresponds to "https://". Well it happens a lot of time. A website may support only https - secure connections and browser is smart enough to know this. How? We will see that in a moment.

My use case was for using a proxy to intercept browser traffic. As you know any proxy will intercept traffic and then send it's own certificate to the browser and if browser does not recognize the cert it shows the error -
  • "Your connection is not secure"... SEC_ERROR_UNKNOWN_ISSUER
You may go in Advanced tab and then add exception to start trusting the new certs for your testing. But it is not always possible and well see why?

NOTE : If you are not trying something on your own and dont understand why above prompt came its time to turn back. Your network traffic may be getting intercepted by some hacker using MITM (Man in the middle) attach to steal sensitive data like passwords.

WARNING : All the below information is provided only for the use case of pen testing and other security testing. Please do not use it otherwise. All of the below features are provided for your security. Playing around with it without understanding can compromise your security. So proceed on your own risk.

HTTP Strict Transport Security (HSTS)

As per Wiki,

HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections,[1] and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797.

How this works is that your webserver eg. Apache or NGINX is configured so that in each response header it sends header corresponding to  HSTS
  • Strict-Transport-Security "max-age=63072000"
max-age is the expiry time. So the HSTS will be applicable till this much time.

NOTE : This is updated every time you visit the site. So if this time period is 2 years them each time you visit the site this will be set for next 2 years.

For eg see below in case of techcrunch -



Working of HTTP Strict Transport Security (HSTS)

Browser once it receives stores this data corresponding to each site. In case of Firefox you can see it as follows -
  1. Type about:support in firefox tab
  2. Click on "Show in Folder". This should open your firefox profile folder.
  3. In this search for a file called SiteSecurityServiceState.txt and open it.
  4. Firefox stored HSTS data for sites in this file.
Steps in screenshots -




NOTE : As mentioned earlier this entry is updated every time you hit the url in your browser. Exception is incognito mode in which case entry from this file is removed once incognito is close. However note that if you have visited the site even once in non incognito mode then the security restrictions will be obeyed even in incognito mode.

Working :
  • This tells browser that every subsequent connection should always be https (secure). So even if you try to access http version of the site browser will convert it to https and proceed. 
    • For eg. You can try hitting "http://techcrunch.com/" and it will automatically hit "https://techcrunch.com/"
  • Another effect it has it that if this header is set then you cannot add exception for the certificate which does not match the original one (the one browser does not trust). Eg in screenshot below -

NOTE : I am using Zap proxy. You can use any others like Fiddler or Burp. I have written posts on this before you can revisit them if needed.

How to bypass HSTS?

Well now that we know where firefox stores this data it is easy to bypass this. All you have to do is remove the entry from this file. You can probably change the permission of this file so that new entry is not made in it. When tried for techcrunch you can see you can get back option to add certificate exception -



NOTE : For above to work make sure firefox is closed else it can overwrite the file.

NOTE : Above workaround will not work for the well known sites like google since for them the entries are hardcoded into browser code. Please do share if you know of any workaround for this.

NOTE : In the above mentioned file you might also see entries corresponding to HPKP. A HTTP Public Key Pinning (HPKP) header instructs clients to pin a specific public key to a domain. So, if a HPKP-supporting browser encounters a HPKP header, it will remember the specified public key hashes and associate them with that domain. In the future (until the specified max-age timeout expires), the browser will only accept a certificate for that domain if any key in the certificate's trust chain matches one of the associated hashes.


Wednesday, 8 March 2017

Understanding Cross-site scripting (XSS)

Background

As per wiki
             Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications. XSS enables attackers to inject client-side scripts into web pages viewed by other users. A cross-site scripting vulnerability may be used by attackers to bypass access controls such as the same-origin policy.
       We will see this in details as to how web applications can be vulnerable to an XSS attack and how can it be safeguarded. This is the most common known security vulnerability. You can use 3rd party produces like IBM App scan to check for security vulnerabilities in your web applications.



Types of XSS attacks

There are many variants of XSS attacks but the major ones are -
  • Reflected or Non persistent XSS attack
  • Stored or persistent XSS attack

Reflected or Non persistent XSS attack

Out of this reflected is most common. In this type if xss malicious script is executed on clients browser and sensitive data might be stolen.

For eg. lets take a search field on some social networking site. You generally type is words their to either search for a particular word in your feed or to search your friend/family members. When you do this in the resultant page you see something like - "Search result for - your search query". Now if your social networking site is not safeguarded from xss attacks, attacker might inject a malicious script in the search query that will get executed when you do a search which will result in the attacker stealing your data. Worst case your authorization cookie is stolen and your account is compromised.

As stated in Wiki - A reflected attack is typically delivered via email or a neutral web site. The bait is an innocent-looking URL, pointing to a trusted site but containing the XSS vector. If the trusted site is vulnerable to the vector, clicking the link can cause the victim's browser to execute the injected script.

So when you get that mail again saying hey please review these bull**** documents please do not click it. It's either a spam or worst - attempt to steal your sensitive data, since you are logged into multiple sites with active sessions. Always see who is it sent from? Do you know that person? Does the link look suspicious? No one will give you money just like that or you will not be selected for a lucky draw without applying for it :). If you are curious as to what it actually is then copy the link and open it in incognito mode which have no active sessions or logins. Now if it asks you to login it's time to turn around.

Now what do we mean by  innocent-looking URL? The URL is infact that for a trusted website. All attacker will do is inject an XSS vector in one of the request parameters. Say search parameter like in example above. He will also encode script tags and other characters so that the victim user will not be able to guess it's the affected URL. Anyway well soon see a real example of this with working code and the fix. Now lets see persistent xss.

Stored or persistent XSS attack

This is even worst that then the reflected one because this xss attack vector is stored on websites server (perhaps in it's persistent DB) and it will get executed every time user navigates to the section that uses this xss affected code. Lets see an example.

Consider a forum used to discuss programming related QnA. Again if the site is not safeguarded against XSS attacks then an attacker can ask a question which seems normal and legit but at the end contains a malicious script/attack vector. Now when he publishes the question it gets stored in the sites persistent storage and when any user tries to visit this question this malicious script will get executed on his/her browser and again your sensitive data can be stolen.

It's not always about the victims sensitive data though. An attacker can use XSS attack to permanently change the websites appearance or change display texts. Consider the damage it can do. For eg. attacker can change the announcement made by a reputed company which can result in it's stock prices crashing.

Enough with the theory let's see a practical example -

Testing for XSS

Code for this demo is available on my git repository  -
It's a web app. So you can download it and run it in a web server like apache tomcat. For this demo I will use my local setup.

NOTE : This web project has multiple workflows. The files you are interested in are -
This demo also assumes you are familiar with webapps, JSP, Java, Spring etc. I have written multiple posts on these topics in the past. You can search those in this blog if you are interested.You can find everything starting from configuring and using ivy dependency management to developing sample Spring application to deploying apps on tomcat. Above web app uses all these.

Once you have deployed the web app you can open it in your browser. It should look like following -



You can add normal text in the search box and do a "Search" or a "Search Xss Protect". Everything should work fine. For eg. when I input - "I am groot!", it just shows me the query I have input -



Now lets do "Search Again" and perform a normal "Search". But this time we will search differently. Enter following query in the search box -
  • I am groot!<script>alert(10)</script>
The result is -



As you can see we do see our normal result but we also see a prompt with 10 in it. Now if you revisit our input query we added a script tag at the end and the browser executed it. This was just for the demo, attackers script will execute silently in the background without prompts and steal/modify your data.

Now lets repeat the same search with text -
  • I am groot!<script>alert(10)</script>
Only this time you press "Search Xss Protect". What do you see?



What happened here? No prompts? That's because we escaped out input so that it is not harmful which is exactly what the fix is. You need to escape your user inputs so that they are not executed by your browser as scripts. Now that you have seen a demo of how XSS behaves and how you could safeguard it lets see how it actually works from code perspective.

Code working

You need to revisit the JSP file -
The main culprit line here is -
  • Your input : <%=query %
This essentially evaluates the query from Java. If this has script tags those will be evaluated too. What do we do? We escape it before this line is reached which is precisely what the code does when you press protected search -

        String xssProtect = request.getParameter("xssProtect");
        if(xssProtect != null && xssProtect.equalsIgnoreCase("yes")) {
            query = ESAPI.encoder().encodeForJavaScript(query);
        } 


NOTE : xssProtect is something I have internally used to differentiate between normal search and protected search for demo purposes. You need not worry about it. In production you should never have a normal search like scenario.

NOTE : ESAPI is a library that OWASP (Open Web Application Security Project) recommends. You can see a bunch of links in the related links section below. But you can technically use any library that escapes html like coverity security lib.

NOTE : Above was an example of reflected or non persistent XSS attack. What if we were storing this search queries in our internal DB and showing search history for a user? In that case it would be a persistent XSS attack.

Though above demo was a means to show you how XSS actually works and how it can be safeguarded you should never code it this way. JSP should never have such logic. All of this should be in backend logic possibly your servlets and controllers.

Related Links

Sunday, 26 February 2017

Working with primitives in Streams

Background

In one of the previous posts on Understanding Java 8 Stream API we saw basics of working with primitives. To reiterate - there are 3 types of primitive streams -
  1. IntStream: Used for the primitive types int, short, byte, and char
  2. LongStream: Used for the primitive type long
  3. DoubleStream: Used for the primitive types double and float
These primitives are special they got their own Optional classes , their own functional interfaces. With our previous understanding in place we will see this new working with primitives.


Why primitive Streams?

Lets say we have a list of integers and we want to compute its average. How would you do it with streams.

    public static void main(String[] args) {
        List<Integer> myIntList = Arrays.asList(1,2,3,4,5);
        Optional<Integer> sum = myIntList.stream().reduce((x,y) -> x + y);
        long count  = myIntList.stream().count();
        double avg = sum.get()/count;
        System.out.println(avg);
    }

Output : 3.0

Well sure based on what we know so far we aced this. But do you see any drawback in above approach?
  • You are creating stream twice from the list - once to get sum and once to get count.
  • Then you are dividing it to get the average.
Though this works Java provides more optimized solution and this is where IntStream comes into picture.

    public static void main(String[] args) {
        List<Integer> myIntList = Arrays.asList(1,2,3,4,5);
        OptionalDouble intAvg = myIntList.stream().mapToInt(x->x).average();
        System.out.println(intAvg.getAsDouble());
    }


Output : 3.0

That's the one liner with IntStream. mapToInt() intermediate operation gives you a IntStream which has predefined operations like sum(), average() etc. Couple of points to note -
  • mapToInt() intermediate method returned IntStream
  •  average() terminal operation on IntStream returned a OptionalDouble. This is a new class. Notice it is not Optional<Double> but entirely a new class. 
  • Also note the method to get double value from  OptionalDouble class. It is getAsDouble() and not get() like you would have seen in Optional class.
  • Recollect out discussion before primitives have their own streams eg. IntStream , own Optional classes eg. OptionalInt and even functional interfaces eg. BooleanPredicate. But that's for later.
  • sum() on IntStream returns int instead of OptionalInt because if there are no elements in the Stream it would return 0.
    public static void main(String[] args) {
        List<Integer> myIntList = Arrays.asList(1,2,3,4,5);
        int sum = myIntList.stream().mapToInt(x->x).sum();
        System.out.println(sum);
    } 


Output : 15

NOTE : If you want Object stream back from primitive streams you can use mapToObj intermediate method.

Creating primitive streams

Now that we have basics out of our way let see how can we create a primitive stream. One method we already saw by using mapToInt() method on a normal Stream. Similarly you have mapToLong() and mapToDouble() methods to get LongStream and DoubleStream respectively. 

Other normal methods that we say in Stream class like of() or generate() or iterate() still apply to primitive streams as well.

    public static void main(String[] args) {
        System.out.println("of");
        IntStream.of(1,2,3,4,5).forEach(System.out::print);
        System.out.println("\ngenerate");
        IntStream.generate(() -> 1).limit(4).forEach(System.out::print);
        System.out.println("\niterate");
        IntStream.iterate(1,(x) -> x+1).limit(4).forEach(System.out::print);
    }

Output :
of
12345
generate
1111
iterate
1234

Some other methods are 
  • range(int startInclusive, int endExclusive)
  • rangeClosed(int startInclusive, int endInclusive)
    public static void main(String[] args) {
        System.out.println("range");
        IntStream.range(1, 5).forEach(System.out::print);
        System.out.println("\nrangeClosed");
        IntStream.rangeClosed(1, 5).forEach(System.out::print);
    } 


Output :
range
1234
rangeClosed
12345

Example demonstrates for InStream but same applies for LongStream and DoubleStream as well.

NOTE : Notice the range limits. For range end limit is exclusive where as for rangeClosed it is inclusive.


Understanding summaryStatistics

Now lets say we have an IntStream and we need to to output difference between the max element and min element in the stream. How would you do that?

    public static void main(String[] args) {
        List<Integer> myList = Arrays.asList(1,2,3,4,5);
        int max = myList.stream().mapToInt(x -> x).max().getAsInt();
        int min = myList.stream().mapToInt(x -> x).min().getAsInt();
        int difference = max - min;
        System.out.println("Difference : " + difference);
    }

Output :
Difference : 4
which is expected since 5-1 = 4.
But again we need to create stream twice and need two terminal operations to get result. Java helps us here too with summaryStatistics method.

    public static void main(String[] args) {
        List<Integer> myList = Arrays.asList(1,2,3,4,5);
        IntSummaryStatistics intSummaryStatistics = myList.stream().mapToInt(x -> x).summaryStatistics();
        int difference = intSummaryStatistics.getMax() - intSummaryStatistics.getMin();
        System.out.println("Difference : " + difference);
    }

Output :
Difference : 4

Related Links

Monday, 20 February 2017

Java 8 Collection improvements

Background

With introduction of functional interfaces and Lambda expressions there are some new APIs introduced in collection class. In this post we will look at those.


Conditional removal [Collection.removeIf]

There is a new method added in Collection interface as -

  • boolean removeIf(Predicate<? super E> filter)

This basically removes elements from the list that match the predicate. No magic here if you see the default implementation it is as follows -

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

So you basically iterate on your collection using a iterator and remove all elements that match your predicate condition. 

Let's see an example now -

        List<String> countriesList = new ArrayList<>();
        countriesList.add("India");
        countriesList.add("Srilanka");
        countriesList.add("Nepal");
        countriesList.add("Italy");
        countriesList.add("Bhutan");
        countriesList.add("Ireland");
        System.out.println("Before : " + countriesList);
        countriesList.removeIf(s -> s.startsWith("I"));
        System.out.println("After : " + countriesList);

Output is :
Before : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]
After : [Srilanka, Nepal, Bhutan]

As you can see all Strings in the list starting with 'I' are removed as defined by the predicate.


Updating all elements(List.replaceAll)

Another method that is introduced is - 

  • void replaceAll(UnaryOperator<E> o)
It replaces all the elements in the current List to new values based on the UnaryOperator.  Let's revisit above example with minor modification -

        List<String> countriesList = new ArrayList<>();
        countriesList.add("India");
        countriesList.add("Srilanka");
        countriesList.add("Nepal");
        countriesList.add("Italy");
        countriesList.add("Bhutan");
        countriesList.add("Ireland");
        System.out.println("Before : " + countriesList);
        countriesList.replaceAll(s -> "Miss " + s);
        System.out.println("After : " + countriesList);


and the output is :
Before : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]
After : [Miss India, Miss Srilanka, Miss Nepal, Miss Italy, Miss Bhutan, Miss Ireland]

NOTE : Recollect UnaryOperator takes a single argument of type t and returns a value of same type t. If you wish to recollect common functional interfaces check out the links in the Related links section below.

Iterating over a List (List.forEach)

Another useful method that is added in List is -
  • public void forEach(Consumer<? super E> action)
It lets you take an action for  all the elements in the List. Lets see this now.

        List<String> countriesList = new ArrayList<>();
        countriesList.add("India");
        countriesList.add("Srilanka");
        countriesList.add("Nepal");
        countriesList.add("Italy");
        countriesList.add("Bhutan");
        countriesList.add("Ireland");
        System.out.println("Before : " + countriesList);
        countriesList.forEach(System.out::println);
        System.out.println("After : " + countriesList);


and the output is -
Before : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]
India
Srilanka
Nepal
Italy
Bhutan
Ireland
After : [India, Srilanka, Nepal, Italy, Bhutan, Ireland]


NOTE : Careful. Do not alter the List with the action or you will get - java.util.ConcurrentModificationException

NOTE : Above method reference System.out::println is same as saying s -> System.out.println(s)

New APIs added in Map

New APIs added -
  1. merge()
  2. putIfAbsent() : puts in map is key is not present or the value is null
  3. computeIfPresent() : calls the BiFunction when the requested key is found
  4. computeIfAbsent() : calls the BiFunction when the key isn’t present or
    is null
 Let's see merge first -

It's method is as follows -

    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }
Above is the default implementation in Map method.  Quick observation :
  1. If old value is null, bifunction is never called. 
  2. If mapping function returns null key is removed from the Map.
 computeIfAbsent and computeIfPresent are again similar. They also take bifunction. computeIfAbsent executes the mapping function if key is not present or value is null and computeIfPresent executes the mapping function if key is present and the value is not null. Following are it's signatures -

  •     default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
  •     default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)


Related Links


Saturday, 18 February 2017

Working with Java 8 Date/Time API

Background

Before Java 8 how do we work with Date/Time. We used java.util.Date class. Like below -

        Date d = new Date();
        System.out.println(d);
        Date cd = Calendar.getInstance().getTime();
        System.out.println(cd);
        System.out.println(d.getDate());
        System.out.println(d.getTime());
        System.out.println(System.currentTimeMillis());//same as getTime above

Output :
Sat Feb 18 10:42:17 IST 2017
Sat Feb 18 10:42:17 IST 2017
18
1487394737708
1487394737728

In Java 8 Date/Time APIs are completely revamped. Most of them are in java.time.* package. We will look at them now.

New Date/Time classes

New Date/Time classes are as follows -
  • LocalDate : Contains date. No time or timezone.
  • LocalTime : Contains time. No date or timezone.
  • LocalDateTime : Contains date and time but not the timezone.
  • ZonedDateTime : Contains date and time with the timezone.
You can test it as follows -

            System.out.println(LocalDate.now());
            System.out.println(LocalTime.now());
            System.out.println(LocalDateTime.now());
            System.out.println(ZonedDateTime.now()); 


For me the output is as follows -
2017-02-18
10:59:48.133
2017-02-18T10:59:48.133
2017-02-18T10:59:48.134+05:30[Asia/Kolkata]

NOTE : Notice how T separator is used to separate Date and time in the output of  LocalDateTime and ZonedDateTime. In the output of ZonedDateTime what you see as +5.30 is relative to GMT. So current time in Asia/Kolkata time zone is 5hours 30 minutes ahead of GMT.


More ways to create Date/Time class instances

LocalDate :
public static LocalDate of(int year, int month, int dayOfMonth)
public static LocalDate of(int year, Month month, int dayOfMonth)

NOTE : month starts with 1 (end at 12) unlike normal convention used Java which is 0 based. Also Month is an enum. You cannot compare it with int.

LocalTime :
public static LocalTime of(int hour, int minute)
public static LocalTime of(int hour, int minute, int second)
public static LocalTime of(int hour, int minute, int second, int nanos)

LocalDateTime:
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute)
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second)
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanos)
public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute)
public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second)
public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanos)
public static LocalDateTime of(LocalDate date, LocalTime time)

NOTE : Notice how you can can use a LocalDate and LocaleTime instance to create a LocalDateTime instance.

ZonedDateTime :
public static ZonedDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanos, ZoneId zone)
public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
public static ZonedDateTime of(LocalDateTime dateTime, ZoneId zone)

NOTE : Notice how each constructor needs a ZoneId instance.  It basically tells what time zone we are working with. You can print out all the ZoneIds available or filter it to suit your needs.

        ZoneId.getAvailableZoneIds().stream()
        .filter(z -> z.contains("Kolkata"))
        .sorted().forEach(System.out::println);
        ZoneId zone = ZoneId.of("Asia/Kolkata");
        System.out.println(zone);


And it prints :
Asia/Kolkata
Asia/Kolkata

NOTE : Note that there are no explicit constructors. You need to use static method as I have mentioned above. Also you will get DateTimeException if you pass invalid arguments

You can try :
        System.out.println(LocalDate.of(2017, 13, 21));
        System.out.println(LocalDate.of(2017, 2, 29));


You will get :

Exception in thread "main" java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 13
    at java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
    at java.time.temporal.ChronoField.checkValidValue(ChronoField.java:703)
    at java.time.LocalDate.of(LocalDate.java:267)
    at HelloWorld.main(HelloWorld.java:83)

and

Exception in thread "main" java.time.DateTimeException: Invalid date 'February 29' as '2017' is not a leap year
    at java.time.LocalDate.create(LocalDate.java:429)
    at java.time.LocalDate.of(LocalDate.java:269)
    at HelloWorld.main(HelloWorld.java:84)

NOTE : 2012 and 2016 are leap years.

Manipulating Date/Time

You can manipulate date/time as follows -

Using Periods

Period is well span of LocalDate. That means span consisting of Year, Month, Day, Week etc. 

Eg.

Period annually = Period.ofYears(1); // every 1 year
Period quarterly = Period.ofMonths(3); // every 3 months
Period everyTwoWeeks = Period.ofWeeks(2); // every 2 weeks
Period everyYearAndAWeek = Period.of(1, 0, 5); // every year and 5 days 


NOTE : There is no time involvement in Periods. You cannot use Period with LocalTime.Also note you cannot concatenate Period methods. Well you can technically but only last one will be picked and used since these are static methods. Eg.
Period incorrectUsage = Period.ofYears(2).ofWeeks(2); // every 2 week

Now lets try printing out a Period value -

System.out.println(Period.of(2, 12, 21));

It outputs : P2Y12M21D

Now lets analyze this string. It starts with a P denoting it's  a period. Then you have Y for years, M for months and D for day. If any of the parameters are not present then Java simply omits it (it will not say 0M).

Eg.

System.out.println(Period.of(2, 0, 21));

prints: P2Y21D
Also values greater than expected limit is fine. You can give months value as 14 and Java takes care of it.

Eg.

System.out.println(Period.of(2, 14, 21));

prints : P2Y14M21D

But while computing it will subtract 14M = 1Y and 2M. You get the point. Let's see one example of how it is used and then we move to next topic -

        LocalDate localDate = LocalDate.of(2017, 8, 2);
        System.out.println(locateDate.plus(Period.of(0, 1, 2)));


print : 2017-09-04
(Adds 0 years, 1 month and 2days to existing date.)

Also note the parameters in duration are Y,M and D. So if you give something like -

        System.out.println(Period.ofWeeks(2));

it will print : P14D

Using Duration

Same as Period but works with LocalTime rather than LocalDate. Like Period began with P Duration begins with PT (Period of time). As Period had parameters - Years,Months,Days - Y,M,D duration has Hours.Minutes,Seconds H,M,S.

Some examples -
Duration twoDays = Duration.ofDays(2); // PT48H
Duration hourly = Duration.ofHours(1); // PT1H
Duration everyTwoMinute = Duration.ofMinutes(2); // PT2M
Duration everyTwentySeconds = Duration.ofSeconds(20); // PT20S
Duration everyMilli = Duration.ofMillis(1); // PT0.001S

Duration and Period Usage 

Do not mix Period with Time and Duration with Date. Period is intended to be used with Date and Duration with Time. Refer following picture for reference.

Working with Instants

Instant class represents a specific moment in time in terms of GMT time zone. Simple example -

        Instant ins1 = Instant.now();
        Thread.sleep(1000);
        Instant ins2 = Instant.now();
        System.out.println(Duration.between(ins1, ins2));

and it prints : PT1.001S
That's a second and some time needed for processing.  You cannot use Instant with LocalDate or LocalTime or LocalDateTime. It has to be ZonedDateTime since it is associated with a time zone.

NOTE : Instant is related to ZonedDateTime but it always corresponds to GMT i.e point in time as per GMT.

Accounting for Daylight Savings Time

Daylight savings time is obeserved in some countries. In this clock is adjusted by an hour twice a year to make better use of the sunlight. This transition happens on Weekend Sunday 2 AM. So when clock is at 2 AM it is moved to 3 AM.

Lets say June 29, 2017, is the weekend that clocks spring ahead for daylight savings time. Now consider following code -

LocalDate date = LocalDate.of(2017, Month.JUNE, 29);
LocalTime time = LocalTime.of(1, 30);
ZoneId usZone = ZoneId.of("US/Eastern");
ZonedDateTime zonedDateTime1 = ZonedDateTime.of(date, time, usZone);
ZonedDateTime zonedDateTime2 = dateTime1.plus(1, ChronoUnit.HOURS);
long hours = ChronoUnit.HOURS.between(zonedDateTime1, zonedDateTime2);
int hours1 = dateTime1.getHour();
int hours2 = dateTime2.getHour();
System.out.println(hours + "," + hours1 + "," + hours2);

This will output 1,1,3. While the values are two hours apart, the time zone offset changes as well, making it only change from 6:30 GMT to 7:30 GMT.

NOTE : Java is smart enough to adjust for daylight savings time. So you don't have to worry about handling it in code. Logic may differ but you don't have to worry that it might cause exception or error.

Related Links


t> UA-39527780-1 back to top