Skip to content

Instantly share code, notes, and snippets.

@apgapg
Last active November 10, 2021 16:03
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apgapg/84d855e41c0134a34ff8b2cf034ad249 to your computer and use it in GitHub Desktop.
Save apgapg/84d855e41c0134a34ff8b2cf034ad249 to your computer and use it in GitHub Desktop.
Get correct ISO DateTime String with offset in Flutter: https://dartpad.dev/84d855e41c0134a34ff8b2cf034ad249
import 'package:intl/intl.dart';
import 'package:meta/meta.dart';
void main() {
print(DateUtils.formatISOTime(DateTime.now()));
print(DateUtils.getCurrentISOTimeString());
}
class DateUtils {
///Converts DateTime to ISO format
///Output: 2020-09-16T20:41:09.331+05:30
static String formatISOTime(DateTime date) {
var duration = date.timeZoneOffset;
if (duration.isNegative)
return (date.toIso8601String() +
"-${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}");
else
return (date.toIso8601String() +
"+${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}");
}
///get ISO time String from DateTime
///Output: 2020-09-16T20:42:38.629+05:30
static String getCurrentISOTimeString({DateTime dateTime}) {
var date = dateTime ?? DateTime.now();
//Time zone may be null in dateTime hence get timezone by datetime
var duration = DateTime.now().timeZoneOffset;
if (duration.isNegative)
//TODO: convert duration to abs value instead of below params
return (date.toIso8601String() +
"-${duration.inHours.abs().toString().padLeft(2, '0')}:${(duration.inMinutes.abs() - (duration.inHours.abs() * 60)).toString().padLeft(2, '0')}");
else
return (date.toIso8601String() +
"+${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}");
}
}
@loonix
Copy link

loonix commented Sep 16, 2020

thanks for this!

@apgapg
Copy link
Author

apgapg commented Sep 16, 2020

@gsouf
Copy link

gsouf commented Nov 11, 2020

Be aware that it will fail when the given datetime was parsed from an iso string with offset:

formatISOTime(DateTime.parse('2000-04-22T01:00:00-05:00'));

That's because now dart will return the date with a trailing Z for zero offset.

So it will end up with 2 offsets specified in the final string:

// note inclusion of Z character befiore -05:00
2000-04-22T01:00:00Z-05:00

That cannot be parsed back:

// note inclusion of Z character befiore -05:00
DateTime.parse('2000-04-22T01:00:00Z-05:00'); // throw invalid date string

@gazialankus
Copy link

Fixed the double offset issue:

  static String formatISOTime(DateTime date) {
    final iso = date.toIso8601String();
    if (iso.endsWith("Z")) {
      return iso;
    }
    var duration = date.timeZoneOffset;
    if (duration.isNegative)
      return (iso +
          "-${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}");
    else
      return (iso +
          "+${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}");
  }

@gsouf
Copy link

gsouf commented Dec 17, 2020

@gazialankus thanks for sharing but I wonder if date.toIso8601String could return a time offset other that Z?

I personally adopted a slightly different approach by building the whole iso string part by part, so that we don't depend on something that could potentially break.

String getIsoDatetime(DateTime date) {
  // make date string without offset
  final month = date.month.toString().padLeft(2, '0');
  final day = date.day.toString().padLeft(2, '0');
  final hour = date.hour.toString().padLeft(2, '0');
  final minute = date.minute.toString().padLeft(2, '0');
  final second = date.second.toString().padLeft(2, '0');
  final datetimeString =
      '${date.year}-${month}-${day}T${hour}:${minute}:${second}.000';

  // make offset string
  final duration = date.timeZoneOffset;
  final offsetHours = duration.inHours.abs().toString().padLeft(2, '0');
  final offsetMinutes =
      (duration.inMinutes.abs() - (duration.inHours.abs() * 60))
          .toString()
          .padLeft(2, '0');
  final offsetString = "$offsetHours:$offsetMinutes";

  // offset positive/negative
  if (duration.isNegative)
    return datetimeString + "-" + offsetString;
  else
    return datetimeString + "+" + offsetString;
  ;
}

It worked well so far that way,

@gazialankus
Copy link

gazialankus commented Dec 18, 2020

DateTime says it's either Z or no Z, but yeah maybe it could change one day. https://api.dart.dev/stable/2.10.4/dart-core/DateTime/toIso8601String.html

@gsouf thank you for sharing your solution. It's nice to have an alternative if things fail.

@adimar-aot
Copy link

Thank you for sharing.
I did few changes on it:

class DateUtils {
 
 ///Converts DateTime to ISO format 
 ///Output: 2020-09-16T20:41:09.331+05:30
  static String formatISOTime(DateTime date) {
    var duration = date.timeZoneOffset;
    
    return "${date.toIso8601String().replaceAll('Z', '')}${duration.isNegative ? "-" : "+"}${_formatTimeZoneOffsetISO(duration)}";
  }
  
  static String _formatTimeZoneOffsetISO(Duration duration) {
    duration = duration.abs();
    return "${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}";
  }

 ///get ISO time String from DateTime
 ///Output: 2020-09-16T20:42:38.629+05:30
  static String getCurrentISOTimeString({DateTime? dateTime}) {
    return formatISOTime(DateTime.now());
  }
}

@loonix
Copy link

loonix commented Nov 10, 2021

You have a big bug there, please remove the negative logic.
If you are in a timezone that is negative you will have double minus

  /// converts [date] into into string format
  /// Example:
  /// * Positive:   (UK)   `2020-09-16T11:55:01.802248+00:00`
  /// * Negative: (Canada) `2020-09-16T11:55:01.802248-08:00`
  static String formatISOTime(DateTime date) {
    var duration = date.timeZoneOffset;
    /// If the user is in Canada the time zone is GMT-8 then the signal will need to be negative.
    /// Because we already get the minus from the hours in the string then we don't need to add it to the string.
    /// In the case the timezone is GMT-0 or higher then the sign will need to be positive.
    var timezoneSignal = !(duration.isNegative) ? '+' : '';
    var dateString = (date.toIso8601String() + "${timezoneSignal}${duration.inHours.toString().padLeft(2, '0')}:${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}");
    return dateString;
  }

@adimar-aot
Copy link

@loonix, the code will not have a double minus if it's using the absolute value of the duration (.abs()), but your suggestion would also work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment