Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Changes between Version 4 and Version 5 of TimePackageDesign

Show
Ignore:
Author:
schveiguy (IP: 12.163.137.244)
Timestamp:
11/29/07 18:41:54 (16 years ago)
Comment:

Moved my post into main page

Legend:

Unmodified
Added
Removed
Modified
  • TimePackageDesign

    v4 v5  
    1818 
    1919 * One alternative would be to revert to the prior Interval notion (a typedef) and implement all functionality as free functions. This would resolve the interface issue noted above, resolve concerns over toString and co, and may well resolve a slice of the others too? 
     20=== Schveiguy's design === 
     21 
     22  * !DateTime and !TimeSpan are represented as separate distinct structs 
     23Yes, the functionality is very similar, but the alternative of having 96-bit or even 128-bit representation is much less attractive to me.  The current implementation loses no performance over the original implementation of time as a long enumeration.  With separate types, you don't have to check unreasonable calls at run time.  For example, let's say TimeX is the type, and it has a member referenceDate, you would have to do runtime checks for simple operations: 
     24{{{ 
     25#!d 
     26  TimeX opAdd(TimeX t) 
     27  { 
     28     if(referenceDate != unspecified && t.referenceDate != unspecified) 
     29        throw Exception("cannot add two points in time together"); 
     30 
     31     ... 
     32  } 
     33}}} 
     34The current code performs this check at compile time because a !DateTime cannot be added to another !DateTime. 
     35 
     36Using a typedef to a builtin type does not allow overloading of the operators, and so it is impossible to enforce the relationship. 
     37 
     38Finally, the duplicate functionality is written only once, and has no effect on the user of the code.  In fact, one type makes the code look more confusing if the coder does not name his variables appropriately, or re-uses variables to mean both types. 
     39 
     40  * !DateTime should not be coupled to a particular calendar. 
     41 
     42This means all calendar related functions for a !DateTime will be deprecated.  The following !DateTime functions already exist in exact or portable forms in Calendar: 
     43{{{ 
     44#!d  
     45    opCall(int year, int month, int day); 
     46    // Calendar.getDateTime (int year, int month, int day, int hour, int minute, int second, int millisecond) 
     47 
     48    int day(); 
     49    int month(); 
     50    int year(); 
     51    int dayOfYear(); 
     52    DayOfWeek dayOfWeek(); 
     53    static int daysInMonth(int year, int month) 
     54    static bool isLeapYear(int year); 
     55    // Calendar.getDayOfMonth(DateTime) 
     56    // Calendar.getMonth(DateTime) 
     57    // Calendar.getYear(DateTime) 
     58    // Calendar.getDayOfYear(DateTime) 
     59    // Calendar.getDayOfWeek(DateTime) 
     60    // Calendar.getDaysInMonth(int year, int month) 
     61    // Calendar.isLeapYear(int year) 
     62}}} 
     63 
     64There are 2 functions which are in !DateTime which are not in Calendar, but should go into Calendar: 
     65 
     66{{{ 
     67#!d 
     68    DateTime addMonths(int value); 
     69    DateTime addYears(int value); 
     70}}} 
     71 
     72I believe these kinds of functions belong in Calendar because they can be different depending on the calendar you want to use. 
     73 
     74  * Calendar should be more useable 
     75 
     76If we just deprecate !DateTime functions, and put them in calendar, then the following annoyance occurs: 
     77 
     78{{{ 
     79#!d 
     80DateTime dt = Clock.now; 
     81//int year = dt.year; 
     82int year = GregorianCalendar.getDefaultInstance.getYear(dt); 
     83}}} 
     84 
     85Most of the time, you want to use the same calendar throughout all code.  Therefore, I propose you set a default calendar in a static member of Calendar itself.  This will default to !GregorianCalendar.  In !GregorianCalendar, this can be done by: 
     86 
     87{{{ 
     88#!d 
     89static this() 
     90{ 
     91  if(Calendar.current !is null) 
     92     Calendar.current = getDefaultInstance(); 
     93} 
     94}}} 
     95 
     96This way, if a user sets the default calendar in his own static this(), Gregorian doesn't override it.  Now the call isn't as ugly: 
     97 
     98{{{ 
     99#!d 
     100int year = Calendar.current.getYear(dt); 
     101}}} 
     102 
     103And if static methods are added to calendar which simply call the default instance's method: 
     104 
     105{{{ 
     106#!d 
     107int year = Calendar.year(dt); 
     108}}} 
     109 
     110  * !DateTime only imports !TimeSpan, !TimeSpan imports nothing. 
     111 
     112This addresses the issue of making !DateTime and !TimeSpan the minimal implementation so they can be used in interfaces.  When D 2.0 is released, it is supposed to allow extending structs via the same syntax that arrays now have.  i.e.: 
     113 
     114{{{ 
     115#!d 
     116char[] toString(DateTime dt) 
     117}}} 
     118 
     119can be called like: 
     120 
     121{{{ 
     122#!d 
     123dt.toString(); 
     124}}} 
     125 
     126This will allow us to extend functionality without having interfaces import this functionality.  If one wants the toString function, one will just import the module that does it. 
     127 
     128  * !TimeSpan represents a time of day decoupled from a date 
     129 
     130This really isn't a huge problem as I see it.  Perhaps we could add accessors to !TimeSpan to get the time of day fields.  Currently the .seconds accessor gets the total seconds represented by the !TimeSpan.  Perhaps a .compSeconds accessor would get the number of seconds % 60. 
     131 
     132These can also be done in a separate method, and then used as accessors when D 2.0 supports it. 
     133 
     134  * tango/text/convert/!TimeStamp allows conversion of !TimeSpan to a "[-]day.hh:mm:ss.subseconds" string 
     135 
     136This allows people to print !TimeStamp in a meaningful way (instead of a huge number of 100ns increments) 
     137 
     138  * tango/text/convert/Layout should print !DateTime and !TimeSpan natively 
     139 
     140This further completes the notion that the types are builtin 
     141 
     142I spoke with Kris on IRC, and he doesn't like this idea :) But it's just a nicety that I think would be good.  The issue is that if you import !TimeStamp in Layout, there is a lot of extra code there that may never be used. 
     143 
     144  * Clock and !WallClock.now should be overloaded to return standard c time 
     145 
     146Yes, the old seconds since 1970 :) Except we should return it as a long to avoid the 2038 bug.  I find this is a very often requested feature, and doing the conversion with the current design is cumbersome: 
     147 
     148{{{ 
     149#!d 
     150int unixTime = (Clock.now - DateTime.epoch1970).seconds; 
     151}}} 
     152 
     153=== Schveiguy's enhancements === 
     154These are some ideas which might solve some of the mentioned concerns, but may be beyond the scope 
     155 
     156==== !TimeZone class ==== 
     157 
     158Currently, there is no !TimeZone representation.  The only notion of a time zone is the current time zone, and that does not describe when Daylight Savings time is in effect.  Essentially, a !TimeZone class should contain: 
     159 
     160  * Name 
     161  * Years of effectiveness.  Time zones can change, this is not determined by science, but by governments, and so there is no logic behind when this happens.  In order to account for when these occur, a !TimeZone should know when it was in effect.  Two timezones can have the same name, but they must have non-overlapping years of effectiveness. 
     162  * DST begin and end information.  This allows one to determine when DST was in effect.  I think this varies from year to year, so it should be a function of the year. 
     163  * Normal offset of UTC.  This will be a !TimeSpan. 
     164  * DST offset of UTC. 
     165 
     166Time zones are loaded from the OS.  On windows, it's in the registry, on Posix systems, I'm not sure. 
     167 
     168The current time zone name should be accessible from !WallClock. 
     169 
     170Using a !TimeZone class, we can now allow Date to be valid for all values of !DateTime.  Currently, the functions that translate a !DateTime to a Date depend on OS support. 
     171 
     172==== extended !DateTime class ==== 
     173 
     174Some of the concerns have to do with ease of use with the time structures, and ability to extract components of the date and time from a point in time.  All these depend on a lot of the features of Calendar, Clock, !TimeZone, toString, and others.  Putting all this functionality into the current !DateTime would make it painful to include !DateTime in interfaces.  However, if all the functionality exists outside !DateTime, then the usability of these features is less than ideal.   
     175 
     176What we could do is have a class that encapsulates all these functions into one type.  The class would replace Date, and contain components of the point in time like Date does.  It would have a reference to a Calendar and a !TimeZone.  The constructor would take a !DateTime instance, and optionally a Calendar and !TimeZone instance.  If not provided a Calendar or !TimeZone, it would use the current default. 
     177 
     178If this is a desired thing, then I think that we should change the names of everything.  !DateTime seems like a very good name for this.  We could rename the struct representing a point in time to just Time. 
     179 
     180The new !DateTime would contain fields for all date/time components (i.e. year, month, day, etc down to nanosecond), and would provide methods to convert to a Time using the Calendar and !TimeZone references.  We could provide operators on 2 !DateTime values (convert both to UTC Time, then perform operation). 
     181 
     182The usage would be: 
     183 
     184  * Interfaces are only allowed to use !TimeSpan or Time. 
     185  * If you want to just do simple timing functions, such as timeouts or date/time arithmetic, use Time and !TimeSpan. 
     186  * If you want to use a single extended feature, such as Calendar functions, but do not want to increase the bloat of your code, use that feature directly with Time/TimeSpan as an argument. 
     187  * If you want an encapsulation of all things, including string conversion, Calendar, and !TimeZone, use !DateTime (the new version). 
     188 
     189==== Dependencies of all the types ==== 
     190 
     191  * !TimeSpan: none 
     192  * Time: !TimeSpan 
     193  * Calendar: Time, !TimeSpan 
     194  * !TimeZone: !TimeSpan 
     195  * Clock: Time, !TimeSpan 
     196  * !WallClock: Clock, Time, !TimeSpan, !TimeZone 
     197  * text/convert/!TimeStamp: Calendar, Time, !TimeSpan, !TimeZone 
     198  * !DateTime: all of the above 
     199 
    20200 
    21201== References ==