How a calculation of previous month in golang can break production
Tags: go, csharp, python, typescript
Published: 2021-05-31 20:21:56

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2andkvd64o81mtobjacb.png)
*Illustration from “A Journey With Go”, created by Renee French.*

In one of the applications that I'm working on, I have to calculate what is a previous month of a given date. It seems easy, isn't it? Golang provides the [time package](https://golang.org/pkg/time/) which has a method: AddDate.  

I'm quite fresh in Go, previously I was working with C#, Python and (for some time) TypeScript. Every one of these languages has also some methods to calculate time. Let's see how they work.

## C# .NET 5: 

Let's use [System.DateTime.AddMonths](https://docs.microsoft.com/pl-pl/dotnet/api/system.datetime.addmonths?view=net-5.0):

```C#
using System;
				
public class Program
{
	public static void Main()
	{
      	var endOfMay = new DateTime(2021, 05, 31, 12, 00, 00);
      	var endOfApril = endOfMay.AddMonths(-1);
		var enfOfJune = endOfMay.AddMonths(1);
		Console.WriteLine("End of May: {0}", endOfMay);
		Console.WriteLine("End of April: {0}", endOfApril);
		Console.WriteLine("End of June: {0}", enfOfJune);
	}
}
```

Output:

```
End of May: 05/31/2021 12:00:00
End of April: 04/30/2021 12:00:00
End of June: 06/30/2021 12:00:00
```

Looks good 👍

## Python 3.9

Here I will use [dateutil.relativedelta](https://dateutil.readthedocs.io/en/stable/relativedelta.html):

```python
import datetime
from dateutil.relativedelta import relativedelta

end_of_may = datetime.datetime(2021, 5, 31, 12, 0, 0, 0)
end_of_april = end_of_may + relativedelta(months=-1)
end_of_june = end_of_may + relativedelta(months=1)
print(f'End Of May: {end_of_may}')
print(f'End Of April: {end_of_april}')
print(f'End of June: {end_of_june}')
```

Let's see logs:

```
End Of May: 2021-05-31 12:00:00
End Of April: 2021-04-30 12:00:00
End of June: 2021-06-30 12:00:00
```

Exactly what I was looking for 👌

## Go

Now, the code which broke my service:

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	endOfMay := time.Date(2021, 5, 31, 12, 00, 00, 00, time.UTC)
	endOfApril := endOfMay.AddDate(0, -1, 0)
	endOfJune := endOfMay.AddDate(0, 1, 0)
	fmt.Printf("End of May: %s\n", endOfMay)
	fmt.Printf("End of April: %s\n", endOfApril)
	fmt.Printf("End of June: %s\n", endOfJune)
}
```

And the output:

```
End of May: 2021-05-31 12:00:00 +0000 UTC
End of April: 2021-05-01 12:00:00 +0000 UTC
End of June: 2021-07-01 12:00:00 +0000 UTC
```

🤔 🤔 🤔 
Something is wrong... 
... and exactly that happened on production on 31 May 2021 in my project. This piece of code broke production. Funny, because it was broken just for one day. And in the next few days, until the end of July, it will work fine again. 

This behavior of the `AddDate` was totally unexpected for me. I wanted to blame Go for doing it, but... I can only blame myself 😅 because in [documentation of AddDate method](https://golang.org/pkg/time/#Time.AddDate) we can find:

>  AddDate normalizes its result in the same way that Date does, so, for example, adding one month to October 31 yields December 1, the normalized form for November 31.

Not what I was expecting from my experience with Python and C# 🤷‍♂️ But, documented. So - my fault 😕

## Takeaways

In the end, 3 takeaways for you and myself from the future:

1. Reading documentation can be useful 😄
{% twitter 1392459191353286656 %}
1. Write tests for edge cases.
1. Handling dates in the programming world is a tough job.

## Postscriptum - TypeScript

TypeScript is doing the same thing as Go.

```TypeScript
const endOfMay: Date = new Date(2021, 4, 31, 12, 0, 0, 0); // months are starting from 0 here 🤦
console.log(`End of May: ${endOfMay}`);
const endOfApril = new Date(endOfMay);
endOfApril.setMonth(endOfMay.getMonth() - 1);
console.log(`End of April: ${endOfApril}`);
const endOfJune = new Date(endOfMay);
endOfJune.setMonth(endOfMay.getMonth() + 1);
console.log(`End of June: ${endOfJune}`);
```

Output:

```
End of May: Mon May 31 2021 12:00:00 GMT+0200 (Central European Summer Time)
End of April: Sat May 01 2021 12:00:00 GMT+0200 (Central European Summer Time)
End of June: Thu Jul 01 2021 12:00:00 GMT+0200 (Central European Summer Time)
```

but [dayjs](https://github.com/iamkun/dayjs) handles it perfectly.

```TypeScript
import dayjs from "dayjs";

const endOfMayDate = new Date(2021, 4, 31, 12, 0, 0, 0);
const endOfMay = dayjs(endOfMayDate);
console.log(`End of May: ${endOfMay}`);
const endOfApril = dayjs(endOfMayDate).add(-1, "month");
console.log(`End of April: ${endOfApril}`);
const endOfJune = dayjs(endOfMayDate).add(1, "month");
console.log(`End of June: ${endOfJune}`);
```

Output:

```
End of May: Mon, 31 May 2021 10:00:00 GMT 
End of April: Fri, 30 Apr 2021 10:00:00 GMT 
End of June: Wed, 30 Jun 2021 10:00:00 GMT 
```