Full Code of jogboms/time.dart for AI

master 421b33acb968 cached
13 files
54.2 KB
16.6k tokens
16 symbols
1 requests
Download .txt
Repository: jogboms/time.dart
Branch: master
Commit: 421b33acb968
Files: 13
Total size: 54.2 KB

Directory structure:
gitextract_l_0tyj6p/

├── .github/
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .pubignore
├── .vscode/
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── example/
│   └── time_example.dart
├── lib/
│   ├── src/
│   │   └── extensions.dart
│   └── time.dart
├── pubspec.yaml
└── test/
    └── time_test.dart

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/build.yml
================================================
name: Build

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  schedule:
    # runs the CI weekly
    - cron: "0 0 * * 0"

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6
      - uses: dart-lang/setup-dart@v1
        with:
          sdk: dev

      - name: Install dependencies
        run: dart pub get

      - name: Check format
        run: dart format --output=none --set-exit-if-changed -l 120 lib

      - name: Analyze
        run: dart analyze lib

      - name: Run tests
        run: dart test --coverage coverage

      - name: Coverage
        run: dart run coverage:format_coverage -l -i ./coverage/test/time_test.dart.vm.json -o ./coverage/lcov.info

      - name: Upload coverage to codecov
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: true


================================================
FILE: .gitignore
================================================
# Files and directories created by pub
.dart_tool/
.packages
test/.test_coverage.dart
coverage/
coverage_badge.svg
pubspec.lock

# Conventional directory for build outputs
build/

# Directory created by dartdoc
doc/api/

# JetBrains IDEs
.idea/*
*.iml
*.ipr
*.iws


================================================
FILE: .pubignore
================================================
.idea/*
coverage/*


================================================
FILE: .vscode/settings.json
================================================
{
  "dart.lineLength": 120,
  "[dart]": {
      "editor.tabSize": 2,
      "editor.rulers": [
          120
      ],
  },
}

================================================
FILE: CHANGELOG.md
================================================
## 2.1.6

- Include `milliseconds` and `microseconds` in `timeOfDay` extension to `DateTime`

## 2.1.5

- Introduce `endOfDay` extension to `DateTime`

## 2.1.4

- Introduce `shift` extension to `DateTime`

## 2.1.3

- Introduce `isWeekend` extension to `DateTime`
- Introduce `isWorkday` extension to `DateTime`

## 2.1.2

- Fix `min` and `max` assertion with `clamp` extension

## 2.1.1

- Introduce `firstDayOfWeek` extension to `DateTime`
- Introduce `lastDayOfWeek` extension to `DateTime`
- Introduce `firstDayOfMonth` extension to `DateTime`
- Introduce `lastDayOfMonth` extension to `DateTime`
- Introduce `firstDayOfYear` extension to `DateTime`
- Introduce `lastDayOfYear` extension to `DateTime`
- Introduce `clamp` extension to `DateTime`
- Introduce `clamp` extension to `Duration`

## 2.1.0

- Introduce `package:clock` dependency for more predictable testing

## 2.0.1

- Implement utc support for `copyWith` operation

## 2.0.0

- Migrate to null-safety

## 1.5.0-nullsafety.1

- Introduce `isAtSameYearAs` extension to `DateTime`
- Introduce `isAtSameMonthAs` extension to `DateTime`
- Introduce `isAtSameDayAs` extension to `DateTime`
- Introduce `isAtSameHourAs` extension to `DateTime`
- Introduce `isAtSameMinuteAs` extension to `DateTime`
- Introduce `isAtSameMillisecondAs` extension to `DateTime`
- Introduce `isAtSameMicrosecondAs` extension to `DateTime`
- Introduce `isLeapYear` extension to `DateTime`
- Introduce `daysInMonth` extension to `DateTime`

```dart
final DateTime specificDate = DateTime(2021, 01, 01);
final DateTime otherDate = DateTime(2021, 02, 01);

print(specificDate.isAtSameYearAs(otherDate)); // true
print(specificDate.isAtSameMonthAs(otherDate)); // false
print(specificDate.isAtSameDayAs(otherDate)); // false
```

## 1.4.1

- Introduce `isAtSameYearAs` extension to `DateTime`
- Introduce `isAtSameMonthAs` extension to `DateTime`
- Introduce `isAtSameDayAs` extension to `DateTime`
- Introduce `isAtSameHourAs` extension to `DateTime`
- Introduce `isAtSameMinuteAs` extension to `DateTime`
- Introduce `isAtSameMillisecondAs` extension to `DateTime`
- Introduce `isAtSameMicrosecondAs` extension to `DateTime`
- Introduce `isLeapYear` extension to `DateTime`
- Introduce `daysInMonth` extension to `DateTime`

## 1.5.0-nullsafety.0
- Migrated to null-safe dart

## 1.4.0

- Introduce `isToday` extension to `Duration`
- Introduce `isTomorrow` extension to `DateTime`
- Introduce `wasYesterday` extension to `DateTime`

## 1.3.0

- Introduce `delay` extension to `Duration`
- Introduce `copyWith` extension to `DateTime`

## 1.2.0

- Iterate through a `DateTime` range:

```dart
final DateTime start = DateTime(2019, 12, 2);
final DateTime end = start + 1.weeks;
final DateTime tuesday = start.to(end).firstWhere((date) => date.weekday == DateTime.tuesday);
```

## 1.1.1

- Fix breaking change from v1.1.0 by introducing @deprecated flag

## 1.1.0

- Breaking Change: renamed `later` to `fromNow` to align with other ecosystems
- Introduced support for other variants of `num` i.e `double`

## 1.0.0

- Named extensions to allow discoverability
- Introduced quality control test cases

## 0.0.1+1

- Unify extensions for int, Duration & DateTime

## 0.0.1

- Initial version


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Jeremiah Ogbomo

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================
FILE: README.md
================================================
# ⏰ Time


[![Build](https://github.com/jogboms/time.dart/actions/workflows/build.yml/badge.svg)](https://github.com/jogboms/time.dart/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/jogboms/time.dart/branch/master/graph/badge.svg)](https://codecov.io/gh/jogboms/time.dart) [![pub package](https://img.shields.io/pub/v/time.svg)](https://pub.dartlang.org/packages/time)

With shiny extensions, if you have ever written something like this, then look no further:

```dart
final DateTime fourHoursFromNow = DateTime.now() + Duration(hours: 4);
```

## 🎖 Installation
```yaml
dependencies:
  time: "^2.1.6"
```

### ⚡ Import

```dart
import 'package:time/time.dart';
```

## 🎮 Usage

```dart
final Duration tenMinutes = 10.minutes;
final Duration oneHourThirtyMinutes = 1.5.hours;
final DateTime afterTenMinutes = DateTime.now() + 10.minutes;
final Duration tenMinutesAndSome = 10.minutes + 15.seconds;
final int tenMinutesInSeconds = 10.minutes.inSeconds;
final DateTime tenMinutesFromNow = 10.minutes.fromNow;
```

You can perform all basic arithmetic operations on `Duration` as you always have been:

```dart
final Duration interval = 10.minutes + 15.seconds - 3.minutes + 2.hours;
final Duration doubled = interval * 2;
```

You can also use these operations on `DateTime`:

```dart
final DateTime oneHourAfter = DateTime() + 1.hours;
```

`Duration` is easily convertible as it always has been:

```dart
final int twoMinutesInSeconds = 2.minutes.inSeconds;
```

You can also convert `Duration` to `DateTime`, if needed:

```dart
final DateTime timeInFuture = 5.minutes.fromNow;
final DateTime timeInPast = 5.minutes.ago;
```

Iterate through a `DateTime` range:

```dart
final DateTime start = DateTime(2019, 12, 2);
final DateTime end = start + 1.weeks;
final DateTime tuesday = start.to(end).firstWhere((date) => date.weekday == DateTime.tuesday);
```

Granular comparison between `DateTime` fields:

```dart
final DateTime specificDate = DateTime(2021, 01, 01);
final DateTime otherDate = DateTime(2021, 02, 01);

print(specificDate.isAtSameYearAs(otherDate)); // true
print(specificDate.isAtSameMonthAs(otherDate)); // false
print(specificDate.isAtSameDayAs(otherDate)); // false
```

You can also delay code execution:

```dart
void doSomething() async {
  await 5.seconds.delay;
  // Do the other things
}
```

You can also use the popular `copyWith`:

```dart
final initial = DateTime(2019, 2, 4, 24, 50, 45, 1, 1);
final expected = initial.copyWith(
  year: 2021,
  month: 10,
  day: 28,
  hour: 12,
  minute: 45,
  second: 10,
  millisecond: 0,
  microsecond: 12,
);
```

Visit the [API Reference](https://pub.dev/documentation/time/latest/time/time-library.html) to find out all that is available.

## 🐛 Bugs/Requests

If you encounter any problems feel free to open an issue. If you feel the library is missing a feature, please raise a ticket on Github and I'll look into it. Pull request are also welcome.

## 👏 Inspiration

- Swift library of the same name - [Time](https://github.com/dreymonde/Time).
- Kotlin library of the same name - [Time](https://github.com/kizitonwose/Time).

## ⭐ License

MIT License


================================================
FILE: analysis_options.yaml
================================================
include: package:lints/recommended.yaml

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false
  errors:
    missing_required_param: error
    missing_return: error
    unused_import: error
    unused_local_variable: error
    dead_code: error


================================================
FILE: example/time_example.dart
================================================
import 'package:time/time.dart';

void main() async {
  // Num Extensions
  print(1.weeks);
  print(1.5.weeks);
  print(7.days);
  print(7.5.days);
  print(22.hours);
  print(22.5.hours);
  print(45.minutes);
  print(45.5.minutes);
  print(30.seconds);
  print(30.5.seconds);
  print(15.milliseconds);
  print(15.5.milliseconds);
  print(10.microseconds);
  print(10.5.microseconds);
  print(5.nanoseconds);
  print(5.5.nanoseconds);

  // Delay for 5 seconds
  await 5.seconds.delay;

  // DateTime Extensions
  print(DateTime.now() + 7.days);
  print(DateTime.now() - 7.days);
  print(
    DateTime(2019, 2, 4, 24, 50, 45, 1, 1).copyWith(
      year: 2021,
      month: 10,
      day: 28,
      hour: 12,
      minute: 45,
      second: 10,
      millisecond: 0,
      microsecond: 12,
    ),
  );

  // Duration Extensions
  print(7.days.inWeeks);
  print(7.days.fromNow);
  print(7.days.ago);

  DateTime.now().to(1.weeks.fromNow, by: 1.days).forEach(print);
}


================================================
FILE: lib/src/extensions.dart
================================================
import 'package:clock/clock.dart';

extension NumTimeExtension<T extends num> on T {
  /// Returns a Duration represented in weeks
  Duration get weeks => days * DurationTimeExtension.daysPerWeek;

  /// Returns a Duration represented in days
  Duration get days => milliseconds * Duration.millisecondsPerDay;

  /// Returns a Duration represented in hours
  Duration get hours => milliseconds * Duration.millisecondsPerHour;

  /// Returns a Duration represented in minutes
  Duration get minutes => milliseconds * Duration.millisecondsPerMinute;

  /// Returns a Duration represented in seconds
  Duration get seconds => milliseconds * Duration.millisecondsPerSecond;

  /// Returns a Duration represented in milliseconds
  Duration get milliseconds => Duration(microseconds: (this * Duration.microsecondsPerMillisecond).toInt());

  /// Returns a Duration represented in microseconds
  Duration get microseconds => milliseconds ~/ Duration.microsecondsPerMillisecond;

  /// Returns a Duration represented in nanoseconds
  Duration get nanoseconds => microseconds ~/ DurationTimeExtension.nanosecondsPerMicrosecond;
}

extension DateTimeTimeExtension on DateTime {
  /// Adds this DateTime and Duration and returns the sum as a new DateTime object.
  DateTime operator +(Duration duration) => add(duration);

  /// Subtracts the Duration from this DateTime returns the difference as a new DateTime object.
  DateTime operator -(Duration duration) => subtract(duration);

  /// Returns only year, month and day
  DateTime get date => isUtc ? DateTime.utc(year, month, day) : DateTime(year, month, day);

  /// Returns only the time
  Duration get timeOfDay =>
      hour.hours + minute.minutes + second.seconds + millisecond.milliseconds + microsecond.microseconds;

  /// Returns if today, true
  bool get isToday {
    return _calculateDifference(this) == 0;
  }

  /// Returns if tomorrow, true
  bool get isTomorrow {
    return _calculateDifference(this) == 1;
  }

  /// Returns if yesterday, true
  bool get wasYesterday {
    return _calculateDifference(this) == -1;
  }

  /// Returns true if this year is a leap year.
  bool get isLeapYear =>
      // Leap years are used since 1582.
      year >= 1582 && year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);

  /// Returns the amount of days that are in this month.
  ///
  /// Accounts for leap years.
  int get daysInMonth {
    final days = [
      31, // January
      if (isLeapYear) 29 else 28, // February
      31, // March
      30, // April
      31, // May
      30, // June
      31, // July
      31, // August
      30, // September
      31, // October
      30, // November
      31, // December
    ];

    return days[month - 1];
  }

  /// Returns true if [other] is in the same year as [this].
  ///
  /// Does not account for timezones.
  bool isAtSameYearAs(DateTime other) => year == other.year;

  /// Returns true if [other] is in the same month as [this].
  ///
  /// This means the exact month, including year.
  ///
  /// Does not account for timezones.
  bool isAtSameMonthAs(DateTime other) => isAtSameYearAs(other) && month == other.month;

  /// Returns true if [other] is on the same day as [this].
  ///
  /// This means the exact day, including year and month.
  ///
  /// Does not account for timezones.
  bool isAtSameDayAs(DateTime other) => isAtSameMonthAs(other) && day == other.day;

  /// Returns true if [other] is at the same hour as [this].
  ///
  /// This means the exact hour, including year, month and day.
  ///
  /// Does not account for timezones.
  bool isAtSameHourAs(DateTime other) => isAtSameDayAs(other) && hour == other.hour;

  /// Returns true if [other] is at the same minute as [this].
  ///
  /// This means the exact minute, including year, month, day and hour.
  ///
  /// Does not account for timezones.
  bool isAtSameMinuteAs(DateTime other) => isAtSameHourAs(other) && minute == other.minute;

  /// Returns true if [other] is at the same second as [this].
  ///
  /// This means the exact second, including year, month, day, hour and minute.
  ///
  /// Does not account for timezones.
  bool isAtSameSecondAs(DateTime other) => isAtSameMinuteAs(other) && second == other.second;

  /// Returns true if [other] is at the same millisecond as [this].
  ///
  /// This means the exact millisecond,
  /// including year, month, day, hour, minute and second.
  ///
  /// Does not account for timezones.
  bool isAtSameMillisecondAs(DateTime other) => isAtSameSecondAs(other) && millisecond == other.millisecond;

  /// Returns true if [other] is at the same microsecond as [this].
  ///
  /// This means the exact microsecond,
  /// including year, month, day, hour, minute, second and millisecond.
  ///
  /// Does not account for timezones.
  bool isAtSameMicrosecondAs(DateTime other) => isAtSameMillisecondAs(other) && microsecond == other.microsecond;

  static int _calculateDifference(DateTime date) {
    final now = clock.now();
    return DateTime(date.year, date.month, date.day).difference(DateTime(now.year, now.month, now.day)).inDays;
  }

  /// Returns a range of dates to [to], exclusive start, inclusive end
  /// ```dart
  /// final start = DateTime(2019);
  /// final end = DateTime(2020);
  /// start.to(end, by: const Duration(days: 365)).forEach(print); // 2020-01-01 00:00:00.000
  /// ```
  Iterable<DateTime> to(DateTime to, {Duration by = const Duration(days: 1)}) sync* {
    if (isAtSameMomentAs(to)) return;

    if (isBefore(to)) {
      var value = this + by;
      yield value;

      var count = 1;
      while (value.isBefore(to)) {
        value = this + (by * ++count);
        yield value;
      }
    } else {
      var value = this - by;
      yield value;

      var count = 1;
      while (value.isAfter(to)) {
        value = this - (by * ++count);
        yield value;
      }
    }
  }

  DateTime copyWith({
    int? year,
    int? month,
    int? day,
    int? hour,
    int? minute,
    int? second,
    int? millisecond,
    int? microsecond,
  }) {
    return isUtc
        ? DateTime.utc(
            year ?? this.year,
            month ?? this.month,
            day ?? this.day,
            hour ?? this.hour,
            minute ?? this.minute,
            second ?? this.second,
            millisecond ?? this.millisecond,
            microsecond ?? this.microsecond,
          )
        : DateTime(
            year ?? this.year,
            month ?? this.month,
            day ?? this.day,
            hour ?? this.hour,
            minute ?? this.minute,
            second ?? this.second,
            millisecond ?? this.millisecond,
            microsecond ?? this.microsecond,
          );
  }

  /// Returns the Monday of this week
  DateTime get firstDayOfWeek =>
      isUtc ? DateTime.utc(year, month, day + 1 - weekday) : DateTime(year, month, day + 1 - weekday);

  /// Returns the Sunday of this week
  DateTime get lastDayOfWeek =>
      isUtc ? DateTime.utc(year, month, day + 7 - weekday) : DateTime(year, month, day + 7 - weekday);

  /// Returns the first day of this month
  DateTime get firstDayOfMonth => isUtc ? DateTime.utc(year, month, 1) : DateTime(year, month, 1);

  /// Returns the last day of this month (considers leap years)
  DateTime get lastDayOfMonth => isUtc ? DateTime.utc(year, month + 1, 0) : DateTime(year, month + 1, 0);

  /// Returns the first day of this year
  DateTime get firstDayOfYear => isUtc ? DateTime.utc(year, 1, 1) : DateTime(year, 1, 1);

  /// Returns the last day of this year
  DateTime get lastDayOfYear => isUtc ? DateTime.utc(year, 12, 31) : DateTime(year, 12, 31);

  /// Returns this [DateTime] clamped to be in the range [min]-[max].
  ///
  /// The comparison is done using [compareTo].
  ///
  /// The arguments [min] and [max] must form a valid range where
  /// `min.compareTo(max) <= 0`.
  ///
  /// Example:
  /// ```dart
  /// var result = DateTime(2022, DateTime.october, 15).clamp(
  ///   min: DateTime(2022, DateTime.september, 1),
  ///   max: DateTime(2022, DateTime.september, 30),
  /// ); // DateTime(2022, DateTime.september, 30);
  /// result = DateTime(2022, DateTime.august, 21).clamp(
  ///   min: DateTime(2022, DateTime.september, 15),
  ///   max: DateTime(2022, DateTime.september, 30),
  /// ); // DateTime(2022, DateTime.september, 15);
  /// result = DateTime(2022, DateTime.september, 1).clamp(
  ///   min: DateTime(2022, DateTime.august, 1),
  ///   max: DateTime(2022, DateTime.september, 30),
  /// ); // DateTime(2022, DateTime.september, 1);
  /// ```
  DateTime clamp({DateTime? min, DateTime? max}) {
    assert(
      ((min != null) && (max != null)) ? (min.isBefore(max) || (min == max)) : true,
      'DateTime min has to be before or equal to max\n(min: $min - max: $max)',
    );
    if ((min != null) && compareTo(min).isNegative) {
      return min;
    } else if ((max != null) && max.compareTo(this).isNegative) {
      return max;
    }
    return this;
  }

  /// Adds time units to the calendar date and/or clock time.
  ///
  /// Creates a new [DateTime] object with a calendar date offset from
  /// that of the the current one by the provided number of years, months, and/or days,
  /// and a wall clock time offset from that of the current one by the provided
  /// hours, minutes, seconds, milliseconds and/or microseconds.
  ///
  /// The provided time units can be positive or negative, or any combination.
  /// Overflowing, say by adding more than 30 days to a any date, works like
  /// in the [DateTime] constructor.
  /// The resulting day and time must be within the supported range for
  /// the `DateTime` class.
  DateTime shift({
    int years = 0,
    int months = 0,
    int days = 0,
    int hours = 0,
    int minutes = 0,
    int seconds = 0,
    int milliseconds = 0,
    int microseconds = 0,
  }) =>
      copyWith(
        year: year + years,
        month: month + months,
        day: day + days,
        hour: hour + hours,
        minute: minute + minutes,
        second: second + seconds,
        millisecond: millisecond + milliseconds,
        microsecond: microsecond + microseconds,
      );

  bool get isWeekend => (weekday == DateTime.saturday) || (weekday == DateTime.sunday);

  bool get isWorkday => !isWeekend;

  /// Returns the last microsecond of the day (23:59:59.999999)
  /// ```dart
  /// final date = DateTime(2020, 1, 1);
  /// date.endOfDay; // 2020-01-01 23:59:59.999999
  /// final date = DateTime(2020, 1, 1, 12, 30, 15, 123, 456);
  /// date.endOfDay; // 2020-01-01 23:59:59.999999
  /// ```
  DateTime get endOfDay {
    const microsecond = Duration(microseconds: 1);
    if (isUtc) return DateTime.utc(year, month, day + 1) - microsecond;
    return DateTime(year, month, day + 1) - microsecond;
  }
}

extension DurationTimeExtension on Duration {
  static const int daysPerWeek = 7;
  static const int nanosecondsPerMicrosecond = 1000;

  /// Returns the representation in weeks
  int get inWeeks => (inDays / daysPerWeek).ceil();

  /// Adds the Duration to the current DateTime and returns a DateTime in the future
  DateTime get fromNow => clock.now() + this;

  /// Subtracts the Duration from the current DateTime and returns a DateTime in the past
  DateTime get ago => clock.now() - this;

  /// Returns a Future.delayed from this
  Future<void> get delay => Future.delayed(this);

  /// Returns this [Duration] clamped to be in the range [min]-[max].
  ///
  /// The comparison is done using [compareTo].
  ///
  /// The arguments [min] and [max] must form a valid range where
  /// `min.compareTo(max) <= 0`.
  ///
  /// Example:
  /// ```dart
  /// var result = Duration(days: 10, hours: 12).clamp(
  ///   min: Duration(days: 5),
  ///   max: Duration(days: 10),
  /// ); // Duration(days: 10)
  /// result = Duration(hours: 18).clamp(
  ///   min: Duration(days: 5),
  ///   max: Duration(days: 10),
  /// ); // Duration(days: 5)
  /// result = Duration(days: 0).clamp(
  ///   min: Duration(days: -5),
  ///   max: Duration(days: 5),
  /// ); // Duration(days: 0)
  /// ```
  Duration clamp({Duration? min, Duration? max}) {
    assert(
      ((min != null) && (max != null)) ? min.compareTo(max) <= 0 : true,
      'Duration min has to be shorter than max\n(min: $min - max: $max)',
    );
    if ((min != null) && compareTo(min).isNegative) {
      return min;
    } else if ((max != null) && max.compareTo(this).isNegative) {
      return max;
    }
    return this;
  }
}


================================================
FILE: lib/time.dart
================================================
library time;

export './src/extensions.dart';


================================================
FILE: pubspec.yaml
================================================
name: time
description: Type-safe DateTime and Duration calculations, powered by extensions.
version: 2.1.6
homepage: https://github.com/jogboms/time.dart

environment:
  sdk: ">=2.12.0 <4.0.0"

dependencies:
  clock: ">=1.1.1 <2.0.0"

funding:
  - https://www.paypal.me/jogboms
  - https://www.buymeacoffee.com/jogboms

dev_dependencies:
  lints: ^6.0.0
  test: ^1.28.0
  coverage: ^1.15.0


================================================
FILE: test/time_test.dart
================================================
import 'package:clock/clock.dart';
import 'package:test/test.dart';
import 'package:time/time.dart';

void main() {
  final date = DateTime(2000, 1, 1);

  group('TimeExtension', () {
    group('Integers', () {
      test('can be converted into weeks', () {
        expect(1.weeks, Duration(days: 7));
      });

      test('can be converted into days', () {
        expect(5.days, Duration(days: 5));
      });

      test('can be converted into hours', () {
        expect(22.hours, Duration(hours: 22));
      });

      test('can be converted into minutes', () {
        expect(45.minutes, Duration(minutes: 45));
      });

      test('can be converted into seconds', () {
        expect(30.seconds, Duration(seconds: 30));
      });

      test('can be converted into milliseconds', () {
        expect(15.milliseconds, Duration(milliseconds: 15));
      });

      test('can be converted into microseconds', () {
        expect(10.microseconds, Duration(microseconds: 10));
      });

      test('can be converted into nanoseconds', () {
        expect(5.nanoseconds, Duration(microseconds: 5 ~/ 1000));
      });
    });

    group('Double', () {
      test('can be converted into weeks', () {
        expect(1.5.weeks, Duration(days: 10, hours: 12));
      });

      test('can be converted into days', () {
        expect(5.5.days, Duration(days: 5, hours: 12));
      });

      test('can be converted into hours', () {
        expect(22.5.hours, Duration(hours: 22, minutes: 30));
      });

      test('can be converted into minutes', () {
        expect(45.5.minutes, Duration(minutes: 45, seconds: 30));
      });

      test('can be converted into seconds', () {
        expect(30.5.seconds, Duration(seconds: 30, milliseconds: 500));
      });

      test('can be converted into milliseconds', () {
        expect(15.5.milliseconds, Duration(milliseconds: 15, microseconds: 500));
      });

      test('can be converted into microseconds', () {
        expect(10.5.microseconds, Duration(microseconds: 10));
      });

      test('can be converted into nanoseconds', () {
        expect(5.5.nanoseconds, Duration(microseconds: 5 ~/ 1000));
      });
    });

    group('DateTime', () {
      test('can subtract Durations', () {
        expect(
          DateTime(2019, 1, 1, 0, 0, 30) - 30.seconds,
          DateTime(2019, 1, 1, 0, 0, 0),
        );
      });

      test('can add Durations', () {
        expect(
          DateTime(2019, 1, 1, 0, 0, 30) - 30.seconds,
          DateTime(2019, 1, 1, 0, 0, 0),
        );
      });

      group('can get only year, month and day', () {
        test('when is not utc', () {
          expect(
            DateTime(2020, 4, 10, 15, 27, 30).date,
            DateTime(2020, 4, 10, 0, 0, 0),
          );
        });

        test('when is utc', () {
          expect(
            DateTime.utc(2020, 4, 10, 15, 27, 30).date,
            DateTime.utc(2020, 4, 10, 0, 0, 0),
          );
        });
      });

      test('can get only the time', () {
        expect(
          /// Returns the time
          DateTime(2020, 4, 10, 15, 27, 30).timeOfDay,
          Duration(hours: 15, minutes: 27, seconds: 30),
        );
      });

      test('can get complete time with milliseconds and microseconds', () {
        expect(
          DateTime(2020, 4, 10, 15, 27, 30, 500, 250).timeOfDay,
          Duration(hours: 15, minutes: 27, seconds: 30, milliseconds: 500, microseconds: 250),
        );
      });

      test('timeOfDay preserves precision when reconstructing DateTime', () {
        final original = DateTime(2020, 1, 1, 12, 30, 15, 500, 250);
        final reconstructed = original.date + original.timeOfDay;
        expect(reconstructed, equals(original));
      });

      test('can handle isToday', () {
        final today = date;
        withClock(Clock.fixed(today), () {
          final yesterday = today.subtract(Duration(days: 1));
          final tomorrow = today.add(Duration(days: 1));
          expect(today.isToday, true);
          expect(yesterday.isToday, false);
          expect(tomorrow.isToday, false);
        });
      });

      test('can handle isTomorrow', () {
        final today = date;
        withClock(Clock.fixed(today), () {
          final yesterday = today.subtract(Duration(days: 1));
          final tomorrow = today.add(Duration(days: 1));
          expect(today.isTomorrow, false);
          expect(yesterday.isTomorrow, false);
          expect(tomorrow.isTomorrow, true);
        });
      });

      test('can handle wasYesterday', () {
        final today = date;
        withClock(Clock.fixed(today), () {
          final yesterday = today.subtract(Duration(days: 1));
          final tomorrow = today.add(Duration(days: 1));
          expect(today.wasYesterday, false);
          expect(yesterday.wasYesterday, true);
          expect(tomorrow.wasYesterday, false);
        });
      });

      test('can handle isLeapYear', () {
        expect(DateTime(2020, 10, 10).isLeapYear, true);
        expect(DateTime(2019, 10, 10).isLeapYear, false);
        expect(DateTime(2100, 01, 01).isLeapYear, false);
        expect(DateTime(2000, 01, 01).isLeapYear, true);
        expect(DateTime(1000, 01, 01).isLeapYear, false);
        expect(DateTime(1584, 01, 01).isLeapYear, true);
      });

      test('can handle daysInMonth', () {
        // Leap year.
        expect(DateTime(2020, 01, 01).daysInMonth, 31);
        expect(DateTime(2020, 02, 01).daysInMonth, 29);
        expect(DateTime(2020, 03, 01).daysInMonth, 31);
        expect(DateTime(2020, 04, 01).daysInMonth, 30);
        expect(DateTime(2020, 05, 01).daysInMonth, 31);
        expect(DateTime(2020, 06, 01).daysInMonth, 30);
        expect(DateTime(2020, 07, 01).daysInMonth, 31);
        expect(DateTime(2020, 08, 01).daysInMonth, 31);
        expect(DateTime(2020, 09, 01).daysInMonth, 30);
        expect(DateTime(2020, 10, 01).daysInMonth, 31);
        expect(DateTime(2020, 11, 01).daysInMonth, 30);
        expect(DateTime(2020, 12, 01).daysInMonth, 31);

        // Non-leap year.
        expect(DateTime(2019, 01, 01).daysInMonth, 31);
        expect(DateTime(2019, 02, 01).daysInMonth, 28);
        expect(DateTime(2019, 03, 01).daysInMonth, 31);
        expect(DateTime(2019, 04, 01).daysInMonth, 30);
        expect(DateTime(2019, 05, 01).daysInMonth, 31);
        expect(DateTime(2019, 06, 01).daysInMonth, 30);
        expect(DateTime(2019, 07, 01).daysInMonth, 31);
        expect(DateTime(2019, 08, 01).daysInMonth, 31);
        expect(DateTime(2019, 09, 01).daysInMonth, 30);
        expect(DateTime(2019, 10, 01).daysInMonth, 31);
        expect(DateTime(2019, 11, 01).daysInMonth, 30);
        expect(DateTime(2019, 12, 01).daysInMonth, 31);
      });

      test('can handle isAtSameYearAs', () {
        expect(
          DateTime(2020, 10, 10).isAtSameYearAs(DateTime(2020, 09, 12)),
          true,
        );
        expect(
          DateTime(2019, 10, 10).isAtSameYearAs(DateTime(2020, 10, 10)),
          false,
        );
      });

      test('can handle isAtSameMonthAs', () {
        expect(
          DateTime(2020, 10, 18).isAtSameMonthAs(DateTime(2020, 10, 12)),
          true,
        );
        expect(
          DateTime(2020, 09, 10).isAtSameMonthAs(DateTime(2020, 10, 10)),
          false,
        );
        expect(
          DateTime(2019, 10, 10).isAtSameMonthAs(DateTime(2020, 10, 10)),
          false,
        );
      });

      test('can handle isAtSameDayAs', () {
        expect(
          DateTime(2020, 10, 06, 12).isAtSameDayAs(DateTime(2020, 10, 06, 15)),
          true,
        );
        expect(
          DateTime(2020, 10, 08, 15).isAtSameDayAs(DateTime(2020, 10, 06, 15)),
          false,
        );
        expect(
          DateTime(2020, 12, 06, 15).isAtSameDayAs(DateTime(2020, 10, 06, 15)),
          false,
        );
        expect(
          DateTime(2021, 12, 06, 15).isAtSameDayAs(DateTime(2020, 10, 06, 15)),
          false,
        );
      });

      test('can handle isAtSameHourAs', () {
        expect(
          DateTime(2020, 10, 06, 12, 15).isAtSameHourAs(
            DateTime(2020, 10, 06, 12, 25),
          ),
          true,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15).isAtSameHourAs(
            DateTime(2020, 10, 06, 15, 15),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15).isAtSameHourAs(
            DateTime(2020, 10, 07, 12, 15),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15).isAtSameHourAs(
            DateTime(2020, 11, 06, 12, 15),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15).isAtSameHourAs(
            DateTime(2021, 10, 06, 12, 15),
          ),
          false,
        );
      });

      test('can handle isAtSameMinuteAs', () {
        expect(
          DateTime(2020, 10, 06, 12, 15, 30).isAtSameMinuteAs(
            DateTime(2020, 10, 06, 12, 15, 40),
          ),
          true,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30).isAtSameMinuteAs(
            DateTime(2020, 10, 06, 12, 17, 30),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30).isAtSameMinuteAs(
            DateTime(2020, 10, 06, 15, 15, 30),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30).isAtSameMinuteAs(
            DateTime(2020, 10, 09, 12, 15, 30),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30).isAtSameMinuteAs(
            DateTime(2020, 12, 06, 12, 15, 30),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30).isAtSameMinuteAs(
            DateTime(2022, 10, 06, 12, 15, 30),
          ),
          false,
        );
      });

      test('can handle isAtSameSecondAs', () {
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2020, 10, 06, 12, 15, 30, 150),
          ),
          true,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2020, 10, 06, 12, 15, 45, 100),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2020, 10, 06, 12, 25, 30, 100),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2020, 10, 06, 17, 15, 30, 100),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2020, 10, 02, 12, 15, 30, 100),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2020, 06, 06, 12, 15, 30, 100),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 100).isAtSameSecondAs(
            DateTime(2016, 10, 06, 12, 15, 30, 100),
          ),
          false,
        );
      });

      test('can handle isAtSameMillisecondAs', () {
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 10, 06, 12, 15, 30, 150, 750),
          ),
          true,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 10, 06, 12, 15, 30, 175, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 10, 06, 12, 15, 32, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 10, 06, 12, 18, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 10, 06, 18, 15, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 10, 09, 12, 15, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(2020, 04, 06, 12, 15, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMillisecondAs(
            DateTime(1948, 10, 06, 12, 15, 30, 150, 600),
          ),
          false,
        );
      });

      test('can handle isAtSameMicrosecondAs', () {
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 06, 12, 15, 30, 150, 600),
          ),
          true,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 06, 12, 15, 30, 150, 900),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 06, 12, 15, 30, 160, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 06, 12, 15, 34, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 06, 12, 12, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 06, 07, 15, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 10, 09, 12, 15, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2020, 06, 06, 12, 15, 30, 150, 600),
          ),
          false,
        );
        expect(
          DateTime(2020, 10, 06, 12, 15, 30, 150, 600).isAtSameMicrosecondAs(
            DateTime(2030, 10, 06, 12, 15, 30, 150, 600),
          ),
          false,
        );
      });

      test('can iterate over DateTimes', () {
        final start = DateTime(2019);
        final end = start + 2.days;
        final days = start.to(end);
        expect(days.length, 2);
        expect(days.first, start + 1.days);
        expect(days.last, end);
      });

      test('iterating with same dates results in empty iterator', () {
        expect(DateTime(2019).to(DateTime(2019)), isEmpty);
      });

      test('can iterate over DateTimes backwards', () {
        final start = DateTime(2019);
        final end = start - 2.days;
        final days = start.to(end);
        expect(days.length, 2);
        expect(days.first, start - 1.days);
        expect(days.last, end);
      });

      test('can iterate by Duration', () {
        final start = DateTime(2019);
        final end = DateTime(2019) + 1.hours;
        final hours = start.to(end, by: 1.hours);
        expect(hours.single, end);
      });

      group('can handle copyWith', () {
        test('without null values', () {
          final initial = DateTime(2019, 2, 4, 24, 50, 45, 1, 1);
          final expected = initial.copyWith(
            year: 2021,
            month: 10,
            day: 28,
            hour: 12,
            minute: 45,
            second: 10,
            millisecond: 0,
            microsecond: 12,
          );
          expect(expected.year, 2021);
          expect(expected.month, 10);
          expect(expected.day, 28);
          expect(expected.hour, 12);
          expect(expected.minute, 45);
          expect(expected.second, 10);
          expect(expected.millisecond, 0);
          expect(expected.microsecond, 12);
        });

        test('with null values', () {
          final initial = DateTime(2019, 2, 4, 24, 50, 45, 1, 1);
          final year = initial.copyWith(year: 2021);
          expect(year.year, 2021);
          final month = initial.copyWith(month: 10);
          expect(month.month, 10);
          final day = initial.copyWith(day: 28);
          expect(day.day, 28);
          final hour = initial.copyWith(hour: 12);
          expect(hour.hour, 12);
          final minute = initial.copyWith(minute: 45);
          expect(minute.minute, 45);
          final second = initial.copyWith(second: 10);
          expect(second.second, 10);
          final millisecond = initial.copyWith(millisecond: 0);
          expect(millisecond.millisecond, 0);
          final microsecond = initial.copyWith(microsecond: 12);
          expect(microsecond.microsecond, 12);
        });

        test('copyWith should save isUtc', () async {
          final now = DateTime.now().toUtc();
          expect(now.isUtc, isTrue);

          final later = now.copyWith(hour: now.hour + 3);
          expect(later.isUtc, isTrue);
        });
      });

      group('can get first/last days', () {
        test('first day of week', () {
          final initial = DateTime(2022, 5, 20);
          final expected = DateTime(2022, 5, 16);
          expect(initial.firstDayOfWeek, expected);
        });

        test('last day of week', () {
          final initial = DateTime(2022, 5, 20);
          final expected = DateTime(2022, 5, 22);
          expect(initial.lastDayOfWeek, expected);
        });

        test('first day of month', () {
          final initial = DateTime(2022, 5, 20);
          final expected = DateTime(2022, 5, 1);
          expect(initial.firstDayOfMonth, expected);
        });

        group('last day of month', () {
          test('last day of month', () {
            final initial = DateTime(2022, 5, 20);
            final expected = DateTime(2022, 5, 31);
            expect(initial.lastDayOfMonth, expected);
          });
          test('february not leap year', () {
            final initial = DateTime(2022, 2, 20);
            final expected = DateTime(2022, 2, 28);
            expect(initial.lastDayOfMonth, expected);
          });

          test('february leap year', () {
            final initial = DateTime(2020, 2, 20);
            final expected = DateTime(2020, 2, 29);
            expect(initial.lastDayOfMonth, expected);
          });
        });

        test('first day of year', () {
          final initial = DateTime(2022, 5, 20);
          final expected = DateTime(2022, 1, 1);
          expect(initial.firstDayOfYear, expected);
        });

        test('last day of year', () {
          final initial = DateTime(2022, 5, 20);
          final expected = DateTime(2022, 12, 31);
          expect(initial.lastDayOfYear, expected);
        });
      });
    });

    group('clamp', () {
      group('returns max when before it', () {
        final it = DateTime(2022, DateTime.october, 15);
        final min = DateTime(2022, DateTime.september, 1);
        final max = DateTime(2022, DateTime.september, 30);

        test('when it has a value for min', () {
          expect(it.clamp(min: min, max: max), equals(max));
        });

        test('when it does not have a value for min', () {
          expect(it.clamp(max: max), equals(max));
        });
      });

      group('returns min when after it', () {
        final it = DateTime(2022, DateTime.august, 21);
        final min = DateTime(2022, DateTime.september, 15);
        final max = DateTime(2022, DateTime.september, 30);

        test('when it has a value for max', () {
          expect(it.clamp(min: min, max: max), equals(min));
        });

        test('when it does not have a value for max', () {
          expect(it.clamp(min: min), equals(min));
        });
      });

      group('returns it', () {
        final it = DateTime(2022, DateTime.september, 1);
        final min = DateTime(2022, DateTime.august, 1);
        final max = DateTime(2022, DateTime.september, 30);

        test('when both min and max are null', () {
          expect(it.clamp(), equals(it));
        });

        test('when is longer than min and max is null', () {
          expect(it.clamp(min: min), equals(it));
        });

        test('when is shorter than max and min is null', () {
          expect(it.clamp(max: max), equals(it));
        });

        test('when is longer than min and shorter than max', () {
          expect(it.clamp(min: min, max: max), equals(it));
        });
      });

      test('asserts that min should be before max', () {
        final it = DateTime(2022, DateTime.september, 1);
        final min = DateTime(2022, DateTime.september, 30);
        final max = DateTime(2022, DateTime.august, 1);
        expect(
          () => it.clamp(min: min, max: max),
          throwsA(isA<AssertionError>()),
        );
      });

      test('returns min/max if are equal', () {
        final it = DateTime(2022, DateTime.september, 1);
        final min = DateTime(2022, DateTime.september, 30);
        final max = min;
        expect(it.clamp(min: min, max: max), min);
      });
    });
    group('Weekend', () {
      test('monday is not weekend', () {
        final it = DateTime(2022, DateTime.august, 1);
        expect(it.isWeekend, isFalse);
      });
      test('tuesday is not weekend', () {
        final it = DateTime(2022, DateTime.august, 2);
        expect(it.isWeekend, isFalse);
      });
      test('wednesday is not weekend', () {
        final it = DateTime(2022, DateTime.august, 3);
        expect(it.isWeekend, isFalse);
      });
      test('thursday is not weekend', () {
        final it = DateTime(2022, DateTime.august, 4);
        expect(it.isWeekend, isFalse);
      });
      test('friday is not weekend', () {
        final it = DateTime(2022, DateTime.august, 5);
        expect(it.isWeekend, isFalse);
      });
      test('saturday is weekend', () {
        final it = DateTime(2022, DateTime.august, 6);
        expect(it.isWeekend, isTrue);
      });
      test('sunday is weekend', () {
        final it = DateTime(2022, DateTime.august, 7);
        expect(it.isWeekend, isTrue);
      });
    });
    group('Workday', () {
      test('monday is workday', () {
        final it = DateTime(2022, DateTime.august, 1);
        expect(it.isWorkday, isTrue);
      });
      test('tuesday is workday', () {
        final it = DateTime(2022, DateTime.august, 2);
        expect(it.isWorkday, isTrue);
      });
      test('wednesday is workday', () {
        final it = DateTime(2022, DateTime.august, 3);
        expect(it.isWorkday, isTrue);
      });
      test('thursday is workday', () {
        final it = DateTime(2022, DateTime.august, 4);
        expect(it.isWorkday, isTrue);
      });
      test('friday is workday', () {
        final it = DateTime(2022, DateTime.august, 5);
        expect(it.isWorkday, isTrue);
      });
      test('saturday is not workday', () {
        final it = DateTime(2022, DateTime.august, 6);
        expect(it.isWorkday, isFalse);
      });
      test('sunday is not workday', () {
        final it = DateTime(2022, DateTime.august, 7);
        expect(it.isWorkday, isFalse);
      });
    });
    group('EndOfDay', () {
      test('returns the last microsecond of the day', () {
        final it = DateTime(2022, DateTime.august, 1, 12, 30, 15, 10, 5);
        final expected = DateTime(2022, DateTime.august, 1, 23, 59, 59, 999, 999);
        expect(it.endOfDay, expected);
      });
      test('returns the last microsecond of the day for utc', () {
        final it = DateTime.utc(2022, DateTime.august, 1, 12, 30, 15, 10, 5);
        final expected = DateTime.utc(2022, DateTime.august, 1, 23, 59, 59, 999, 999);
        expect(it.endOfDay, expected);
      });
    });
    group('Shift', () {
      group('empty parameters', () {
        test('local', () {
          final it = DateTime(2024, DateTime.january, 3, 2, 24, 12, 123, 456);
          expect(it.shift(), it);
        });
        test('utc', () {
          final it = DateTime.utc(2024, DateTime.january, 3, 2, 24, 12, 123, 456);
          expect(it.shift(), it);
        });
      });
      group('adding parameters', () {
        final it = DateTime(2000, 1, 1, 1, 1, 1, 1, 1);
        test('can shift years', () {
          expect(it.shift(years: 1), DateTime(2001, 1, 1, 1, 1, 1, 1, 1));
        });
        test('can shift months', () {
          expect(it.shift(months: 1), DateTime(2000, 2, 1, 1, 1, 1, 1, 1));
        });
        test('can shift days', () {
          expect(it.shift(days: 1), DateTime(2000, 1, 2, 1, 1, 1, 1, 1));
        });
        test('can shift hours', () {
          expect(it.shift(hours: 1), DateTime(2000, 1, 1, 2, 1, 1, 1, 1));
        });
        test('can shift minutes', () {
          expect(it.shift(minutes: 1), DateTime(2000, 1, 1, 1, 2, 1, 1, 1));
        });
        test('can shift seconds', () {
          expect(it.shift(seconds: 1), DateTime(2000, 1, 1, 1, 1, 2, 1, 1));
        });
        test('can shift milliseconds', () {
          expect(it.shift(milliseconds: 1), DateTime(2000, 1, 1, 1, 1, 1, 2, 1));
        });
        test('can shift microseconds', () {
          expect(it.shift(microseconds: 1), DateTime(2000, 1, 1, 1, 1, 1, 1, 2));
        });
      });
      group('subtracting parameters', () {
        final it = DateTime(2000, 1, 1, 1, 1, 1, 1, 1);
        test('can shift years', () {
          expect(it.shift(years: -1), DateTime(1999, 1, 1, 1, 1, 1, 1, 1));
        });
        test('can shift months', () {
          expect(it.shift(months: -1), DateTime(1999, 12, 1, 1, 1, 1, 1, 1));
        });
        test('can shift days', () {
          expect(it.shift(days: -1), DateTime(1999, 12, 31, 1, 1, 1, 1, 1));
        });
        test('can shift hours', () {
          expect(it.shift(hours: -1), DateTime(2000, 1, 1, 0, 1, 1, 1, 1));
        });
        test('can shift minutes', () {
          expect(it.shift(minutes: -1), DateTime(2000, 1, 1, 1, 0, 1, 1, 1));
        });
        test('can shift seconds', () {
          expect(it.shift(seconds: -1), DateTime(2000, 1, 1, 1, 1, 0, 1, 1));
        });
        test('can shift milliseconds', () {
          expect(it.shift(milliseconds: -1), DateTime(2000, 1, 1, 1, 1, 1, 0, 1));
        });
        test('can shift microseconds', () {
          expect(it.shift(microseconds: -1), DateTime(2000, 1, 1, 1, 1, 1, 1, 0));
        });
      });
      group('overflow', () {
        group('months', () {
          test('adding', () {
            final it = DateTime(2023, 11);
            expect(it.shift(months: 2), DateTime(2024));
          });
          test('subtracting', () {
            final it = DateTime(2023);
            expect(it.shift(months: -2), DateTime(2022, 11));
          });
        });
        group('days', () {
          test('adding', () {
            final it = DateTime(2023, 2, 27);
            expect(it.shift(days: 2), DateTime(2023, 3, 1));
          });
          test('subtracting', () {
            final it = DateTime(2023, 2, 1);
            expect(it.shift(days: -2), DateTime(2023, 1, 30));
          });
        });
        group('hours', () {
          test('adding', () {
            final it = DateTime(2023, 2, 1, 23);
            expect(it.shift(hours: 2), DateTime(2023, 2, 2, 1));
          });
          test('subtracting', () {
            final it = DateTime(2023, 2, 2, 1);
            expect(it.shift(hours: -2), DateTime(2023, 2, 1, 23));
          });
        });
        group('minutes', () {
          test('adding', () {
            final it = DateTime(2023, 2, 1, 1, 59);
            expect(it.shift(minutes: 2), DateTime(2023, 2, 1, 2, 1));
          });
          test('subtracting', () {
            final it = DateTime(2023, 2, 1, 1);
            expect(it.shift(minutes: -2), DateTime(2023, 2, 1, 0, 58));
          });
        });
        group('seconds', () {
          test('adding', () {
            final it = DateTime(2023, 2, 1, 1, 1, 59);
            expect(it.shift(seconds: 2), DateTime(2023, 2, 1, 1, 2, 1));
          });
          test('subtracting', () {
            final it = DateTime(2023, 2, 1, 1, 1);
            expect(it.shift(seconds: -2), DateTime(2023, 2, 1, 1, 0, 58));
          });
        });
        group('milliseconds', () {
          test('adding', () {
            final it = DateTime(2023, 2, 1, 1, 1, 1, 999);
            expect(it.shift(milliseconds: 2), DateTime(2023, 2, 1, 1, 1, 2, 1));
          });
          test('subtracting', () {
            final it = DateTime(2023, 2, 1, 1, 1, 1, 1);
            expect(it.shift(milliseconds: -2), DateTime(2023, 2, 1, 1, 1, 0, 999));
          });
        });
        group('microseconds', () {
          test('adding', () {
            final it = DateTime(2023, 2, 1, 1, 1, 1, 1, 999);
            expect(it.shift(microseconds: 2), DateTime(2023, 2, 1, 1, 1, 1, 2, 1));
          });
          test('subtracting', () {
            final it = DateTime(2023, 2, 1, 1, 1, 1, 1, 1);
            expect(it.shift(microseconds: -2), DateTime(2023, 2, 1, 1, 1, 1, 0, 999));
          });
        });
      });
    });
  });

  group('Duration', () {
    test('has correct days-to-week static value', () {
      expect(DurationTimeExtension.daysPerWeek, 7);
    });

    test('has correct nanosecond-to-microsecond static value', () {
      expect(DurationTimeExtension.nanosecondsPerMicrosecond, 1000);
    });

    test('can be converted to weeks', () {
      expect(7.days.inWeeks, 1);
    });

    test('can be converted into a future DateTime', () {
      withClock(Clock.fixed(date), () {
        expect(7.days.fromNow, date + 7.days);
      });
    });

    test('can be converted into a previous DateTime', () {
      withClock(Clock.fixed(date), () {
        expect(7.days.ago, date - 7.days);
      });
    });

    test('Can be used to pause the program flow', () async {
      final timeToWait = Duration(seconds: 2);
      final before = DateTime.now();
      await timeToWait.delay;
      final after = DateTime.now();
      final extraTime = after.millisecondsSinceEpoch - before.add(timeToWait).millisecondsSinceEpoch;
      expect(extraTime >= 0, true);
    });

    group('clamp', () {
      group('returns max when shorter than it', () {
        final it = Duration(days: 10, hours: 12);
        final min = Duration(days: 5);
        final max = Duration(days: 10);

        test('when it has a value for min', () {
          expect(it.clamp(min: min, max: max), equals(max));
        });

        test('when it does not have a value for min', () {
          expect(it.clamp(max: max), equals(max));
        });
      });

      group('returns min when longer than it', () {
        final it = Duration(hours: 18);
        final min = Duration(days: 5);
        final max = Duration(days: 10);

        test('when it has a value for max', () {
          expect(it.clamp(min: min, max: max), equals(min));
        });

        test('when it does not have a value for max', () {
          expect(it.clamp(min: min), equals(min));
        });
      });

      group('returns it', () {
        final it = Duration(days: 0);
        final min = Duration(days: -5);
        final max = Duration(days: 5);

        test('when both min and max are null', () {
          expect(it.clamp(), equals(it));
        });

        test('when is longer than min and max is null', () {
          expect(it.clamp(min: min), equals(it));
        });

        test('when is shorter than max and min is null', () {
          expect(it.clamp(max: max), equals(it));
        });

        test('when is longer than min and shorter than max', () {
          expect(it.clamp(min: min, max: max), equals(it));
        });
      });

      test('asserts that min should be shorter than max', () {
        final it = Duration(days: -0);
        final min = Duration(days: 5);
        final max = Duration(days: -5);
        expect(
          () => it.clamp(min: min, max: max),
          throwsA(isA<AssertionError>()),
        );
      });

      test('returns min/max if are equal', () {
        final it = Duration(days: -0);
        final min = Duration(days: 5);
        final max = min;
        expect(it.clamp(min: min, max: max), min);
      });
    });
  });
}
Download .txt
gitextract_l_0tyj6p/

├── .github/
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .pubignore
├── .vscode/
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── example/
│   └── time_example.dart
├── lib/
│   ├── src/
│   │   └── extensions.dart
│   └── time.dart
├── pubspec.yaml
└── test/
    └── time_test.dart
Download .txt
SYMBOL INDEX (16 symbols across 3 files)

FILE: example/time_example.dart
  function main (line 3) | void main()

FILE: lib/src/extensions.dart
  function isAtSameYearAs (line 88) | bool isAtSameYearAs(DateTime other)
  function isAtSameMonthAs (line 95) | bool isAtSameMonthAs(DateTime other)
  function isAtSameDayAs (line 102) | bool isAtSameDayAs(DateTime other)
  function isAtSameHourAs (line 109) | bool isAtSameHourAs(DateTime other)
  function isAtSameMinuteAs (line 116) | bool isAtSameMinuteAs(DateTime other)
  function isAtSameSecondAs (line 123) | bool isAtSameSecondAs(DateTime other)
  function isAtSameMillisecondAs (line 131) | bool isAtSameMillisecondAs(DateTime other)
  function isAtSameMicrosecondAs (line 139) | bool isAtSameMicrosecondAs(DateTime other)
  function _calculateDifference (line 141) | int _calculateDifference(DateTime date)
  function to (line 152) | Iterable<DateTime> to(DateTime to, {Duration by = const Duration(days: 1)})
  function copyWith (line 176) | DateTime copyWith({
  function clamp (line 251) | DateTime clamp({DateTime? min, DateTime? max})
  function shift (line 276) | DateTime shift({
  function clamp (line 353) | Duration clamp({Duration? min, Duration? max})

FILE: test/time_test.dart
  function main (line 5) | void main()
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (58K chars).
[
  {
    "path": ".github/workflows/build.yml",
    "chars": 910,
    "preview": "name: Build\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n  schedule:\n    # runs the CI"
  },
  {
    "path": ".gitignore",
    "chars": 264,
    "preview": "# Files and directories created by pub\n.dart_tool/\n.packages\ntest/.test_coverage.dart\ncoverage/\ncoverage_badge.svg\npubsp"
  },
  {
    "path": ".pubignore",
    "chars": 19,
    "preview": ".idea/*\ncoverage/*\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 123,
    "preview": "{\n  \"dart.lineLength\": 120,\n  \"[dart]\": {\n      \"editor.tabSize\": 2,\n      \"editor.rulers\": [\n          120\n      ],\n  }"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3229,
    "preview": "## 2.1.6\n\n- Include `milliseconds` and `microseconds` in `timeOfDay` extension to `DateTime`\n\n## 2.1.5\n\n- Introduce `end"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2019 Jeremiah Ogbomo\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 3142,
    "preview": "# ⏰ Time\n\n\n[![Build](https://github.com/jogboms/time.dart/actions/workflows/build.yml/badge.svg)](https://github.com/jog"
  },
  {
    "path": "analysis_options.yaml",
    "chars": 269,
    "preview": "include: package:lints/recommended.yaml\n\nanalyzer:\n  strong-mode:\n    implicit-casts: false\n    implicit-dynamic: false\n"
  },
  {
    "path": "example/time_example.dart",
    "chars": 965,
    "preview": "import 'package:time/time.dart';\n\nvoid main() async {\n  // Num Extensions\n  print(1.weeks);\n  print(1.5.weeks);\n  print("
  },
  {
    "path": "lib/src/extensions.dart",
    "chars": 12452,
    "preview": "import 'package:clock/clock.dart';\n\nextension NumTimeExtension<T extends num> on T {\n  /// Returns a Duration represente"
  },
  {
    "path": "lib/time.dart",
    "chars": 47,
    "preview": "library time;\n\nexport './src/extensions.dart';\n"
  },
  {
    "path": "pubspec.yaml",
    "chars": 391,
    "preview": "name: time\ndescription: Type-safe DateTime and Duration calculations, powered by extensions.\nversion: 2.1.6\nhomepage: ht"
  },
  {
    "path": "test/time_test.dart",
    "chars": 32641,
    "preview": "import 'package:clock/clock.dart';\nimport 'package:test/test.dart';\nimport 'package:time/time.dart';\n\nvoid main() {\n  fi"
  }
]

About this extraction

This page contains the full source code of the jogboms/time.dart GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (54.2 KB), approximately 16.6k tokens, and a symbol index with 16 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!