0
\$\begingroup\$

People here often ask to share as much code as possible. My app is just a sandbox, a typical freshly created AspNetCore + Angular template without much code. So please don't blame me - I provide ALL OF the code related to dates & timezones.


I'm playing with dates in .NET and started learning Moment.js in detail.

I think the best way to learn it is to code something like rudimentary Hotel Booking app - simply because it brings enough complexity and you deal with a lot of timezone-related nuances.

Tutorials on the web only cover most common cases like operations with dates. Very little is written about handling timezone offsets.

We all know that when a date is fetched by back-end it must be stored in the DB as UTC date - there's a great article about it and I agree with this guy completely.

Let's start!


INITIAL INFO:

Location of the Hotel: Spain (GMT+1 / GMT+2 - depending on DST)

Check-In Date & Time: 24 Apr 2019 14:30:00 Spanish Local Time


HOTEL BOOKING WEB SERVICE:

(say, we haven't started creating separate portals for hotel staff and hotel guests - to keep it simple we just have 2 separate pages as our app is more like a sandbox - I've picked ASP.NET Core & Angular template in Visual Studio)

  • there's a page where we, as a guest, choose the date and time of our arrival - the page contains DateTimePicker UI control
  • there's a separate page where the hotel staff can see the Check-Ins

Sounds pretty feasible? I've got bad news. Let's dive in...

We are dealing with JavaScript front-end - it may be AngularJS / Angular 2+ / Vue / React or even jQuery. No big difference as AJAX calls are involved.


PART 1 - BOOKING PAGE:

Multiple users from different time-zones are accessing this page. They want to book a hotel - they pick Date & Time using DateTimePicker.

Nuance 1 - your timezone:

What happens if your time-zone is GMT+3 and you new up a date variable in JavaScript?

The ANSWER is:

your date now has an offset e.g. Mon Apr 24 2019 00:00:00 GMT+0300 (Moscow Standard Time).

And if you pass this date to the back-end it will be 3 hours out - Apr 23 2019 21:00:00 - hence it's no longer 24th but April 23rd.

You think it's the only nuance? Diving deeper...

Nuance 2 - timezone of the hotel:

Hotels are located in different timezones. In most cases they wouldn't necessarily match your timezone.

What if you are booking a hotel in Spain? The timezone of your hotel is GTM+1 / GMT+2. And your timezone is GMT+3. You are picking Date & Time. Which timezone does the freshly picked Date & Time belong to?

The ANSWER is:

By all means the freshly picked Date belongs to the timezone of the hotel.

When you pick a hotel the page must take into account the timezone of the hotel and use it when converting date to UTC.

What are our next steps? Well, it depends. The DatePicker I use is from ng-bootstrap and it produces the following object:

{
  "year": 2019,
  "month": 4,
  "day": 24
}

to keep it yet simpler I'm excluding the time part, so we only pick date - I'm hard-coding the time, so from now on it's 14:30:00 moment([e.year, e.month - 1, e.day, 14, 30, 0]).

We need to pass a UTC date to the back-end - what should we do about it?

My workaround:

  1. produce a date using Moment.js
  2. format it to exclude the timezone offset
  3. convert it from GMT+1 / GMT+2 (the timezone of your hotel in Spain) to UTC
  4. format it again to exclude the timezone offset

code:

let format = 'YYYY-MM-DDTHH:mm:ss';
let zone = "Europe/Madrid";

let initialDateString = moment([e.year, e.month - 1, e.day, 14, 30, 0]).format(format); // "2019-04-24T14:30:00"

let resultDateString = moment.tz(initialDateString , format, zone).utc().format(format); // results in "2019-04-24T12:30:00" - Spain is 2 hours ahead of UTC right now in April

So, the only remaining part is AJAX request and controller action invocation where the Booking record gets created.

You may be still wondering how come the time part of the Date being stored in the DB is 12:30:00 and not 14:30:00. In our specs (see INITIAL INFO section above) it reads

Check-In Date & Time: 24 Apr 2019 14:30:00 GMT+1 / GMT+2.

The ANSWER is:

Relax, this is a UTC DateTime. When UTC clock hits 12:30:00 - the Spanish time is 14:30:00.

Your next question might be: If the time stored in the DB is 12:30:00 UTC how does the hotel staff know you are arriving at 14:30:00 ? The answer is in the next part.


PART 2 - HOTEL STAFF PAGE:

Remember in the Part 1 we used the timezone of the hotel for date conversion? (Converting from Hotel's Timezone to UTC)

Right now we must consider user's local timezone - the hotel staff person opens browser, navigates to a web service and the bookings for their particular hotel are getting rendered on the page. Under each booking they can see the Check-In Date & Time.

What should we do with UTC date so it is displayed as GMT+1 / GMT+2 ?

The ANSWER is:

We must convert it to GMT+1 / GMT+2 which is obvious.

  1. back-end returns 24 Apr 2019 12:30:00

  2. front-end converts it from UTC to GMT+1 / GMT+2

Here's the bit which handles conversion:

let fmt = 'YYYY-MM-DDTHH:mm:ss';
let zone  = "Europe/Madrid";

let utc = moment.tz("2019-04-24T12:30:00",'UTC');
let spanishTime = moment.tz(utc.format(), zone).format(fmt);

CODE:

So, I've revealed all of the JavaScript code involved.

You may be wondering what does the back-end code look like, right?

Like, it must be something tricky and smart... Or, whether it involves any timezone-related operations or any convoluted stuff.

Surprise!

[HttpGet("[action]")]
public List<Booking> CheckBookings()
{
    using (var db = new AppDbContext())
    {
        var booking = db.Bookings.ToList();
        return booking;
    }
}

[HttpPost("[action]")]
public bool BookHotel([FromBody] CheckIn input)
{
    using (var db = new AppDbContext())
    {
        var booking = new Booking() { Name = "NAME", Date = input.Value };
        db.Bookings.Add(booking);
        db.SaveChanges();
    }

    return true;
}

we're not interested in anything except for dates, so I use hardcoded name...


I've heard about Noda Time library. A few people recommended it to me for this booking functionality. It does look great. But in all honesty I can't see where it may be applicable here.

The back-end deals with UTC dates only. That is to say the back-end receives UTC dates from the front-end and sends UTC dates to it.

Looks like all we need is Moment.js - so I can't see where any other libraries may be of help.

\$\endgroup\$
1
\$\begingroup\$

Drop the timezones

As a learning exercise (dealing with timezones) I think you have picked the wrong application.

Date time as string

I suggest that you use a date time string with no time zone info rather than the UTC Unix tick. Standard format ISO 8601 using moment.js client side (browser ISO 8601 compliance is all over the place) to parse for the date picker, and format without timezone (offset zero "Z") for transport to the back-end

The back-end (.net) supports ISO 8601. Parse the booking date time string to store, then send as a date time string using round-trip format specifier eg bookingDate.ToString("o");

Why

Handing multiple timezones on the client will be problematic at best. You will need a robust library for the timezones. moment.js is not up to the job, its successor Luxon will provide better timezone management but that comes with a hefty data overhead.

All you want is a date time picker for the client to make a booking, for that I do not think the complication of juggling timezones is worth the effort when a local time string (location of hotel) will do the job.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.