24

In flutter DateTime.now()returns the device date and time. Users sometimes change their internal clock and using DateTime.now() can give the wrong results.

  1. How can I get Network/Server Current DateTime in flutter?
  2. Is it possible to get Network/Server Current DateTime Without using any packages?
Hamed
  • 5,867
  • 4
  • 32
  • 56
Jai Techie
  • 1,657
  • 3
  • 14
  • 36
  • While I see that this question got quite a few upvotes, and so did the most popular answer (so apparently they are useful to some people), but I really don't think that there is such thing as "Network/Server Current DateTime". Why don't you trust your user (and their device) to have a "correctly" set up clock? Are you doing some kind of security solution? Which parties _are you_ willing to trust? – cubuspl42 Feb 03 '22 at 18:14
  • There's a decent chance that the only real solution to a problem you might have is implementing your checks on the _backend you control_. But that's of course just speculation. – cubuspl42 Feb 03 '22 at 18:18
  • Lol, trust your users?, Imagine you're writing an app that collects data from user, say maybe daily logging of transactions and the payments received, and they're not meant to go back to an already closed day to edit figures, they could get around this by changing their device time and opening the app, and it'll let them access the past date like it's today, I don't know about you, but saying to trust your user sounds really shallow – public static void Main Nov 27 '22 at 22:00

7 Answers7

23

It's not possible without any API. You can use ntp plugin:

A plugin that allows you to get precise time from Network Time Protocol (NTP). It implements the whole NTP protocol in dart.

This is useful for time-based events since DateTime.now() returns the time of the device. Users sometimes change their internal clock and using DateTime.now() can give the wrong result. You can just get clock offset [NTP.getNtpTime] and apply it manually to DateTime.now() object when needed (just add offset as milliseconds duration), or you can get already formatted [DateTime] object from [NTP.now].

Add this to your package's pubspec.yaml file:

dependencies:
  ntp: ^1.0.7

Then add the code like this:

import 'package:ntp/ntp.dart';

Future<void> main() async {
  DateTime _myTime;
  DateTime _ntpTime;

  /// Or you could get NTP current (It will call DateTime.now() and add NTP offset to it)
  _myTime = await NTP.now();

  /// Or get NTP offset (in milliseconds) and add it yourself
  final int offset = await NTP.getNtpOffset(localTime: DateTime.now());
  _ntpTime = _myTime.add(Duration(milliseconds: offset));

  print('My time: $_myTime');
  print('NTP time: $_ntpTime');
  print('Difference: ${_myTime.difference(_ntpTime).inMilliseconds}ms');
}
iDecode
  • 22,623
  • 19
  • 99
  • 186
Amon C
  • 1,710
  • 9
  • 17
  • 3
    Is there a web alternative for ntp package? Since ntp doesn't support Flutter web, how can I get network time for a web app? – rasitayaz Jun 11 '21 at 09:57
  • You can check the firebase network timestap. Or you can use any third-party API and call JSON request. – Amon C Jun 14 '21 at 10:50
  • Note, that in this case the trust is put on Google, as that's the time server this package is using by default. Also note that NTP, as far as I know, does not support any kind of certificates. If you, let's say, want to protect your online multiplayer game from clock-based cheating with this solution, _it's a bad idea_. It's an okeyish idea _only_ if you don't trust the user to have the correct local time _but_ you are perfectly fine with the fact that nothing really stops the user from spoofing the timestamps on the NTP level. – cubuspl42 Feb 03 '22 at 18:26
4

Try using the world clock api. Also, know that there is a chance the api could fail at some point... So I would recommend using a try-catch block around the http call, and if it does happen to fail, just return regular local time of the device....

  Future<void> getTime()async{
  var res = await http.get(Uri.parse('http://worldclockapi.com/api/json/est/now'));
  if (res.statusCode == 200){
  print(jsonDecode(res.body).toString());
}}
Brett Young
  • 168
  • 2
  • 8
2

This is my network time DateTime getNow() method. It is a global method that only gets the network time every two minutes. My app is not super time sensitive but I have run into some issues with people's clock's being off by a couple minutes. It just pings worldtimeapi.org at most every 2 minutes (you will get errors if you ping them too often) and uses their returned time to store an offset to modify the local datetime by. If there are errors with the http call, it falls back to the user's time. I'm also tracking the number of calls to this method just to help debug some timers I have to make sure they are getting disposed of properly.

The problem I had with worldclockapi is it was only accurate to the minute. I could have been doing something wrong but I solved it with using a different api. Here is the code:

int _nowOffset = 0;
int _lastHttpGet = 0;
int _nowCalls = 0;
Future<DateTime> getNow() async {
  try {
    _nowCalls++;
    DateTime nowLocal = DateTime.now();
    if ((nowLocal.millisecondsSinceEpoch - _lastHttpGet) > (oneMinuteMilliSeconds * 2)) {
      _lastHttpGet = nowLocal.millisecondsSinceEpoch;
      var res = await http.get(Uri.parse('https://worldtimeapi.org/api/timezone/Etc/UTC'));
      if (res.statusCode == 200) {
        //print(jsonDecode(res.body).toString());
        Map<String, dynamic> json = jsonDecode(res.body);
        DateTime nowHttp = DateTime.parse(json['datetime']);
        _nowOffset = nowLocal.millisecondsSinceEpoch - nowHttp.millisecondsSinceEpoch;
        if (_nowOffset > 0) {
          _nowOffset *= -1;
        }
        log('http $_nowCalls');
        return nowHttp;
      }
    }
    return DateTime.fromMillisecondsSinceEpoch(nowLocal.millisecondsSinceEpoch + _nowOffset);
  } catch (e, stack) {
    log('{http error: now calls: $_nowCalls $e\n $stack}');
    return DateTime.fromMillisecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch + _nowOffset);
  }
}
nitroplr
  • 41
  • 3
1

You can use this plugin ntp.

import 'package:ntp/ntp.dart';

final int offset = await NTP.getNtpOffset(
        localTime: DateTime.now(), lookUpAddress: "time.google.com");
DateTime internetTime = DateTime.now().add(Duration(milliseconds: offset));

Or there are plenty of API's available

here is an example GET API for Indian time

http://worldtimeapi.org/api/timezone/Asia/Kolkata

The response will be like

  {
      "abbreviation": "IST",
      "client_ip": "45.125.117.46",
      "datetime": "2022-02-26T10:50:43.406519+05:30",
      "day_of_week": 6,
      "day_of_year": 57,
      "dst": false,
      "dst_from": null,
      "dst_offset": 0,
      "dst_until": null,
      "raw_offset": 19800,
      "timezone": "Asia/Kolkata",
      "unixtime": 1645852843,
      "utc_datetime": "2022-02-26T05:20:43.406519+00:00",
      "utc_offset": "+05:30",
      "week_number": 8
    }

If you dont know your country zone just call this API to get all timezones in the world

http://worldtimeapi.org/api/timezone/

Siddharth Mehra
  • 1,691
  • 1
  • 9
  • 32
Anand
  • 4,355
  • 2
  • 35
  • 45
1

I decided to go at this a different route since I'm already using Firebase. I had never used cloud functions before but I like this option better since I'm not reliant on api calls (some of which consider a ping more than once every four minutes a denial of service attack).

  1. firebase init functions
  2. flutter pub add cloud_functions
  3. In the generated index.js file in the functions folder add this code for the cloud function:
    const functions = require("firebase-functions");
    const admin = require('firebase-admin');
    admin.initializeApp();
    
    exports.timestamp = functions.https.onCall((data, context) => {
        // verify Firebase Auth ID token
        if (!context.auth) {
            return 'Authentication Required!';
        }
        let date = new Date();
        return date.toJSON();
    });
  1. firebase deploy --only functions Then in the dart code to call the function looks like this to return a network DateTime:
    final _functions = FirebaseFunctions.instance;
    
      Future<DateTime> getDateTime() async {
        try {
          final result = await _functions.httpsCallable('timestamp').call();
          return DateTime.parse(result.data);
        }on FirebaseFunctionsException  catch (error) {
          log(error.code);
          log(error.message!);
          log(error.details);
          throw Exception('Error getting datetime from cloud function.');
        }
      }
nitroplr
  • 41
  • 3
0

in my case i have used firebase cloud functions to retrieve the time just call this function through an api call and the response will be in isostring you would just have to convert it by doing DateTime.parse(response.body).

this is the firebase javascript function code

exports.getDateTime = functions.https.onRequest((request, response) => {
    const londonTime = new Date().toLocaleString('en-GB', { timeZone: 'Europe/London' });
    const londonTimeISO = new Date(londonTime).toISOString();
    response.send(londonTimeISO);
});

and this is the flutter function.

Future<DateTime> getNetworkDateTime() async {
  final url = Uri.parse(
    'https://us-central1-service-finder-27584.cloudfunctions.net/getDateTime',
  );

  final response = await http.post(
    url,
    headers: {
      'Content-Type': 'application/json',
    },
    body: json.encode(
      {},
    ),
  );
  return DateTime.parse(response.body);
}
-3

it is possible to get Network/Server Current DateTime Without using any packages.

Use this to get the Network/Server Current DateTime:-

DateTime now =
        DateTime.now().isUtc ? DateTime.now() : DateTime.now().toUtc();