Date manipulation: Difference between revisions

From Rosetta Code
Content added Content deleted
Line 7: Line 7:
<lang AppleScript>set x to "March 7 2009 7:30pm EST"
<lang AppleScript>set x to "March 7 2009 7:30pm EST"
return (date x) + 12 * hours</lang>
return (date x) + 12 * hours</lang>

Result is:
<lang AppleScript>date "Sunday, March 8, 2009 7:30:00 AM"</lang>


=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==

Revision as of 04:27, 19 February 2010

Task
Date manipulation
You are encouraged to solve this task according to the task description, using any language you may know.

Given the date string "March 7 2009 7:30pm EST", output the time 12 hours later in any human-readable format.

As extra credit, display the resulting time in a time zone different from your own.

AppleScript

AppleScript has a built-in date class and can coerce a string to a date automatically. It also has reserved constants such as hours which are defined in the unit of seconds. There is no built-in support for time zones. <lang AppleScript>set x to "March 7 2009 7:30pm EST" return (date x) + 12 * hours</lang>

Result is: <lang AppleScript>date "Sunday, March 8, 2009 7:30:00 AM"</lang>

AutoHotkey

This example is incorrect. Please fix the code and remove this message.

Details: It does not use the specified datetime string.

To fix, one might use Titan's DateParse function located here. <lang AutoHotkey>time = 03 07 2009 7:30pm EST StringSplit, time, time, %A_Space% month := time1 date := time2 year := time3 hours := time4 zone := time5 StringSplit, hours, hours, : hour := hours1 StringLeft, minutes, hours2, 2 StringRight, ampm, hours2, 2 If (ampm = "pm")

 hour += 12

time := year . month . date . hour . minutes time += 12, hours FormatTime, TimeString, % time MsgBox Eastern Time %TimeString%</lang>

C

Works with: POSIX

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <time.h>

int main() {

 struct tm ts;
 time_t t;
 const char *d = "March 7 2009 7:30pm EST";
 
 strptime(d, "%B %d %Y %I:%M%p %Z", &ts);
 /* ts.tm_hour += 12; instead of t += 12*60*60
    works too. */
 t = mktime(&ts);
 t += 12*60*60;
 printf("%s", ctime(&t));
 return EXIT_SUCCESS;

}</lang>

Note: ctime treats the date as local, so that it is like the timezone information were discarded (to see the passage to daylight saving time I must change the date into March 28... no matter the timezone specified)

C#

<lang csharp> class Program

   {
       static void Main(string[] args)
       {
           CultureInfo ci=CultureInfo.CreateSpecificCulture("en-US");
           string dateString = "March 7 2009 7:30pm EST";
           string format = "MMMM d yyyy h:mmtt z";
           DateTime myDateTime = DateTime.ParseExact(dateString.Replace("EST","+6"),format,ci) ;
           DateTime newDateTime = myDateTime.AddHours(12).AddDays(1) ;
           Console.WriteLine(newDateTime.ToString(format).Replace("-5","EST")); //probably not the best way to do this
          
           Console.ReadLine();
       }
   }</lang>

C++

Library: Boost

compiled with g++ -lboost_date_time <lang cpp>#include <string>

  1. include <iostream>
  2. include <boost/date_time/local_time/local_time.hpp>
  3. include <sstream>
  4. include <boost/date_time/gregorian/gregorian.hpp>
  5. include <vector>
  6. include <boost/algorithm/string.hpp>
  7. include <cstdlib>
  8. include <locale>


int main( ) {

  std::string datestring ("March 7 2009 7:30pm EST" ) ;
  //we must first parse the date string into a date , a time and a time
  //zone part , to take account of present restrictions in the input facets
  //of the Boost::DateTime library used for this example
  std::vector<std::string> elements ;
  //parsing the date string
  boost::split( elements , datestring , boost::is_any_of( " " ) ) ;
  std::string datepart = elements[ 0 ] + " " + "0" + elements[ 1 ] + " " +
     elements[ 2 ] ; //we must add 0 to avoid trouble with the boost::date_input format strings
  std::string timepart = elements[ 3 ] ;
  std::string timezone = elements[ 4 ] ;
  const char meridians[ ] = { 'a' , 'p' } ;
  //we have to find out if the time is am or pm, to change the hours appropriately
  std::string::size_type found = timepart.find_first_of( meridians, 0 ) ;
  std::string twelve_hour ( timepart.substr( found , 1 ) ) ;
  timepart = timepart.substr( 0 , found ) ; //we chop off am or pm
  elements.clear( ) ;
  boost::split( elements , timepart , boost::is_any_of ( ":" ) ) ;
  long hour = std::atol( (elements.begin( ))->c_str( ) ) ;// hours in the string
  if ( twelve_hour == "p" ) //it's post meridian, we're converting to 24-hour-clock
     hour += 12 ;
  long minute = std::atol( ( elements.begin( ) + 1)->c_str( ) ) ; 
  boost::local_time::tz_database tz_db ;
  tz_db.load_from_file( "/home/ulrich/internetpages/date_time_zonespec.csv" ) ;
  //according to the time zone database, this corresponds to one possible EST time zone
  boost::local_time::time_zone_ptr dyc = tz_db.time_zone_from_region( "America/New_York" ) ;
  //this is the string input format to initialize the date field 
  boost::gregorian::date_input_facet *f =
     new boost::gregorian::date_input_facet( "%B %d %Y"  ) ;
  std::stringstream ss ;
  ss << datepart ;
  ss.imbue( std::locale( std::locale::classic( ) , f ) ) ;
  boost::gregorian::date d ;
  ss >> d ;
  boost::posix_time::time_duration td (  hour , minute , 0  ) ;
  //that's how we initialize the New York local time , by using date and adding
  //time duration with values coming from parsed date input string
  boost::local_time::local_date_time lt ( d , td ,  dyc ,

boost::local_time::local_date_time::NOT_DATE_TIME_ON_ERROR ) ;

  std::cout << "local time: " << lt << '\n' ;
  ss.str( "" ) ;
  ss << lt ;
  //we have to add 12 hours, so a new time duration object is created
  boost::posix_time::time_duration td2 (12 , 0 , 0 , 0 ) ;
  boost::local_time::local_date_time ltlater = lt + td2 ; //local time 12 hours later
  boost::gregorian::date_facet *f2 =
     new boost::gregorian::date_facet( "%B %d %Y , %R %Z" ) ;
  std::cout.imbue( std::locale( std::locale::classic( ) , f2 ) ) ;
  std::cout << "12 hours after " << ss.str( )  << " it is " << ltlater << " !\n" ;
  //what's New York time in the Berlin time zone ?
  boost::local_time::time_zone_ptr bt = tz_db.time_zone_from_region( "Europe/Berlin" ) ;
  std::cout.imbue( std::locale( "de_DE.UTF-8" ) ) ; //choose the output forman appropriate for the time zone
  std::cout << "This corresponds to " << ltlater.local_time_in( bt ) << " in Berlin!\n" ;
  return 0 ;

} </lang> this produces the following output:

local time: 2009-Mar-07 19:30:00 EST
12 hours after 2009-Mar-07 19:30:00 EST it is 2009-Mar-08 08:30:00 EDT !
This corresponds to 2009-Mär-08 13:30:00 CET in Berlin!

Haskell

<lang haskell>import Data.Time.Clock.POSIX import Data.Time.Format import System.Locale

main = print t2

 where t1 = readTime defaultTimeLocale
           "%B %e %Y %l:%M%P %Z"
           "March 7 2009 7:30pm EST"
       t2 = posixSecondsToUTCTime $ 12*60*60 + utcTimeToPOSIXSeconds t1</lang>

Java

<lang Java>import java.util.Date; import java.text.SimpleDateFormat; public class DateManip{

   public static void main(String[] args) throws Exception{

String dateStr = "March 7 2009 7:30pm EST";

SimpleDateFormat sdf = new SimpleDateFormat("MMMM d yyyy h:mma zzz");

Date date = sdf.parse(dateStr);

date.setTime(date.getTime() + 43200000l);

System.out.println(sdf.format(date));

   }

}</lang> Output:

March 8 2009 8:30AM EDT

or using System.out.println(date); as the last line:

Sun Mar 08 08:30:00 EDT 2009

Perl

We use Mountain Standard Time for output.

<lang perl>use DateTime; use DateTime::Format::Strptime 'strptime';

print

   strptime('%b %d %Y %I:%M%p %z', 'March 7 2009 7:30pm -0500')->
       add(hours => 12)->
       set_time_zone('MST')->
       strftime('%b %d %Y %I:%M %p MST'),
   "\n";</lang>

We need to use "-0500" in the input rather than "EST" because DateTime::Format::Strptime rejects the latter as ambiguous— "EST" could refer to the Eastern Time Zone or to Australian Eastern Standard Time.

PHP

<lang php><?php $time = new DateTime('March 7 2009 7:30pm EST'); $time->modify('+12 hours'); echo $time->format('c'); ?></lang>

Python

I don't do anything with timezone here, but it is possible.

<lang python>import time import datetime

def mt(): datime1="March 7 2009 7:30pm EST" formatting = "%B %d %Y %I:%M%p " datime2 = datime1[:-3]

s1 = time.strptime(datime2, formatting) s2 = time.mktime(s1) tdelta = datetime.timedelta(hours=12) # ten hours.. s3 = datetime.datetime.fromtimestamp(s2) datime2 = s3+tdelta date2 = datime2.date() time2 = datime2.time() print date2.strftime("%B %d %Y"), time2.strftime('%I:%M%p %Z'), datime1[-3:]

mt()</lang>

R

<lang R>time <- strptime("March 7 2009 7:30pm EST", "%B %d %Y %I:%M%p %Z") # "2009-03-07 19:30:00" isotime <- ISOdatetime(1900 + time$year, time$mon, time$mday,

  time$hour, time$min, time$sec, "EST")                           # "2009-02-07 19:30:00 EST"

twelvehourslater <- isotime + 12 * 60 * 60 # "2009-02-08 07:30:00 EST" timeincentraleurope <- format(isotime, tz="CET", usetz=TRUE) #"2009-02-08 01:30:00 CET"</lang>

REBOL

<lang REBOL>REBOL [ Title: "Date Manipulation" Author: oofoe Date: 2009-12-06 URL: http://rosettacode.org/wiki/Date_Manipulation ]

Only North American zones here -- feel free to extend for your area.

zones: [ NST -3:30 NDT -2:30 AST -4:00 ADT -3:00 EST -5:00 EDT -4:00 CST -6:00 CDT -5:00 MST -7:00 MDT -6:00 PST -8:00 PDT -7:00 AKST -9:00 AKDT -8:00 HAST -10:00 HADT -9:00]

read-time: func [ text /local m d y t z ][ parse load text [ set m word! (m: index? find system/locale/months to-string m) set d integer! set y integer! set t time! set tz word!] to-date reduce [y m d t zones/:tz] ]

print 12:00 + read-time "March 7 2009 7:30pm EST" </lang>

Output:

8-Mar-2009/7:30-5:00

Ruby

The Time package in the standard library adds a parse method to the core Time class.

Library: RubyGems
Library: ActiveSupport

<lang ruby>require 'time' d = "March 7 2009 7:30pm EST" t = Time.parse(d) puts t.rfc2822 puts t.zone

new = t + 12*3600 puts new.rfc2822 puts new.zone

  1. another timezone

require 'rubygems' require 'active_support' zone = ActiveSupport::TimeZone['Beijing'] remote = zone.at(new.to_f) puts remote.rfc2822 puts remote.zone</lang> outputs

Sat, 07 Mar 2009 19:30:00 -0500
EST
Sun, 08 Mar 2009 08:30:00 -0400
EDT
Sun, 08 Mar 2009 20:30:00 +0800
CST

Using ActiveSupport, we can add 12 hours with any of: <lang ruby>new = t + 12.hours new = t.in(12.hours) new = t.advance(:hours => 12)</lang>

Smalltalk

Works with: GNU Smalltalk

The aim of the class DateTimeTZ is to provide the ability to understand time with "meridian" (PM/AM, even though no checks are done to assure coherency of the format) and to handle timezones despite the locale (which anyway is gently "ignored", or rather unknown in the format of letters, to Date), providing a proper set of informations to the method readFromWithMeridian:andTimeZone:.

The aDict argument must be a dictionary where keys are the abbreviated timezone code (e.g. EST), and values are three-elements array: difference between the timezone and GMT (as Duration), the DateTime when there's passage between using or not using the daylight saving time (year is ignored), and the "direction" (as Duration) of the change. All data must be filled by hand... As example I've put EST (and there's no way to represent the "new" date and time correctly with the new EDT timezone).

The code also fails when adding a duration that "jumps" beyond two DST changes (e.g from EST to EDT and EST again); (it could be partially fixed by considering intervals instead of single date, and adding a fourth element to link to the "new" timezone abbreviation)

<lang smalltalk>DateTime extend [

 setYear: aNum [ year := aNum ]

].

Object subclass: DateTimeTZ [

 |dateAndTime timeZoneDST timeZoneName timeZoneVar|
 DateTimeTZ class >> new [ ^(super basicNew) ]
 DateTimeTZ class >> readFromWithMeridian: aStream andTimeZone: aDict [
   |me|
   me := self new.
   ^ me initWithMeridian: aStream andTimeZone: aDict
 ]
 initWithMeridian: aStream andTimeZone: aDict [ |s|
   dateAndTime := DateTime readFrom: aStream copy.
   s := aStream collection asString.
   s =~ '[pP][mM]'
     ifMatched: [ :m |
       dateAndTime := dateAndTime + (Duration days: 0 hours: 12 minutes: 0 seconds: 0)
     ].
   aDict keysAndValuesDo: [ :k :v |
     s =~ k
       ifMatched: [ :x |
         dateAndTime := dateAndTime setOffset: (v at: 1).

timeZoneDST := (v at: 2) setOffset: (v at: 1). timeZoneVar := (v at: 3). timeZoneDST setYear: (self year). "ignore the year"

         timeZoneName := k
       ]
   ].
   ^ self
 ]
 setYear: aNum [ dateAndTime setYear: aNum ]
 year [ ^ dateAndTime year ]
 timeZoneName [ ^timeZoneName ]
 + aDuration [ |n|
   n := dateAndTime + aDuration.
   (n > timeZoneDST) ifTrue: [ n := n + timeZoneVar ].
   ^ (self copy dateTime: n)
 ]
 dateTime [ ^dateAndTime ]
 dateTime: aDT [ dateAndTime := aDT ]

].</lang>

Usage example (note: the code is rather rigid, so not all operations possible on DateTime are possible on DateTimeTZ).

<lang smalltalk>|s abbrDict dt|

s := 'March 7 2009 7:30pm EST'.

"Build a abbreviation -> offset for timezones (example)" abbrDict := Dictionary new.

abbrDict at: 'EST'

        put: { (Duration days: 0 hours: -5 minutes: 0 seconds: 0).
               (DateTime year: 2009 month: 3 day: 8 hour: 2 minute: 0 second: 0).

(Duration days: 0 hours: 1 minutes: 0 seconds: 0) }.

dt := DateTimeTZ readFromWithMeridian: (s readStream) andTimeZone: abbrDict.

dt := dt + (Duration days: 0 hours: 12 minutes: 0 seconds: 0).

"let's print it" ('%1 %2 %3 %4:%5%6 %7' % {

 (dt dateTime) monthName asString.
 (dt dateTime) day.
 (dt dateTime) year.
 (dt dateTime) hour12.
 (dt dateTime) minute.
 (dt dateTime) meridianAbbreviation asString.
 dt timeZoneName.

}) displayNl.

(dt dateTime) asUTC displayNl.</lang>

Output example (note that EST should be EDT):

March 8 2009 8:30AM EST
 2009-03-08T13:30:00+00:00

Tcl

Works with: Tcl version 8.5

<lang tcl>set date "March 7 2009 7:30pm EST" set epoch [clock scan $date -format "%B %d %Y %I:%M%p %z"] set later [clock add $epoch 12 hours] puts [clock format $later] ;# Sun Mar 08 08:30:00 EDT 2009 puts [clock format $later -timezone :Asia/Shanghai] ;# Sun Mar 08 20:30:00 CST 2009</lang>

Note the transition into daylight savings time in the interval (in the Eastern timezone).